Skip to content

Commit 9f96a27

Browse files
authored
Merge the diagnostics library from the toolchain repo. (#212)
Main original commit message: > Add stubs of a diagnostic emission library. (#13) > > This doesn't have much wired up yet, but tries to lay out the most > primitive API pattern.
1 parent 650f821 commit 9f96a27

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

diagnostics/BUILD

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
# Exceptions. See /LICENSE for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
6+
7+
package(default_visibility = ["//visibility:public"])
8+
9+
cc_library(
10+
name = "diagnostic_emitter",
11+
srcs = ["diagnostic_emitter.cpp"],
12+
hdrs = ["diagnostic_emitter.h"],
13+
deps = ["@llvm-project//llvm:Support"],
14+
)
15+
16+
cc_test(
17+
name = "diagnostic_emitter_test",
18+
srcs = ["diagnostic_emitter_test.cpp"],
19+
deps = [
20+
":diagnostic_emitter",
21+
"@llvm-project//llvm:Support",
22+
"@llvm-project//llvm:gmock",
23+
"@llvm-project//llvm:gtest",
24+
"@llvm-project//llvm:gtest_main",
25+
],
26+
)

diagnostics/diagnostic_emitter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "diagnostics/diagnostic_emitter.h"
6+
7+
namespace Carbon {} // namespace Carbon

diagnostics/diagnostic_emitter.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef DIAGNOSTICS_DIAGNOSTICEMITTER_H_
6+
#define DIAGNOSTICS_DIAGNOSTICEMITTER_H_
7+
8+
#include <functional>
9+
#include <string>
10+
11+
#include "llvm/ADT/Any.h"
12+
#include "llvm/ADT/STLExtras.h"
13+
#include "llvm/ADT/SmallVector.h"
14+
#include "llvm/ADT/StringRef.h"
15+
#include "llvm/Support/raw_ostream.h"
16+
17+
namespace Carbon {
18+
19+
// An instance of a single error or warning. Information about the diagnostic
20+
// can be recorded into it for more complex consumers.
21+
//
22+
// TODO: turn this into a much more reasonable API when we add some actual
23+
// uses of it.
24+
struct Diagnostic {
25+
llvm::StringRef short_name;
26+
std::string message;
27+
};
28+
29+
// Manages the creation of reports, the testing if diagnostics are enabled, and
30+
// the collection of reports.
31+
class DiagnosticEmitter {
32+
public:
33+
using Callback = std::function<void(const Diagnostic&)>;
34+
35+
explicit DiagnosticEmitter(Callback callback)
36+
: callback_(std::move(callback)) {}
37+
~DiagnosticEmitter() {}
38+
39+
// Emits an error unconditionally. `F` is guaranteed to be called.
40+
template <typename DiagnosticT>
41+
void EmitError(
42+
llvm::function_ref<void(typename DiagnosticT::Substitutions&)> f) {
43+
typename DiagnosticT::Substitutions substitutions;
44+
f(substitutions);
45+
callback_({.short_name = DiagnosticT::ShortName,
46+
.message = DiagnosticT::Format(substitutions)});
47+
}
48+
49+
// Emits a warning if `F` returns true. `F` may or may not be called if the
50+
// warning is disabled.
51+
template <typename DiagnosticT>
52+
void EmitWarningIf(
53+
llvm::function_ref<bool(typename DiagnosticT::Substitutions&)> f) {
54+
// TODO(kfm): check if this warning is enabled
55+
typename DiagnosticT::Substitutions substitutions;
56+
if (f(substitutions)) {
57+
callback_({.short_name = DiagnosticT::ShortName,
58+
.message = DiagnosticT::Format(substitutions)});
59+
}
60+
}
61+
62+
private:
63+
Callback callback_;
64+
};
65+
66+
inline auto ConsoleDiagnosticEmitter() -> DiagnosticEmitter& {
67+
static auto* emitter = new DiagnosticEmitter(
68+
[](const Diagnostic& d) { llvm::errs() << d.message << "\n"; });
69+
return *emitter;
70+
}
71+
72+
inline auto NullDiagnosticEmitter() -> DiagnosticEmitter& {
73+
static auto* emitter = new DiagnosticEmitter([](const Diagnostic&) {});
74+
return *emitter;
75+
}
76+
77+
} // namespace Carbon
78+
79+
#endif // DIAGNOSTICS_DIAGNOSTICEMITTER_H_
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "diagnostics/diagnostic_emitter.h"
6+
7+
#include "gmock/gmock.h"
8+
#include "gtest/gtest.h"
9+
#include "llvm/ADT/StringRef.h"
10+
#include "llvm/Support/FormatVariadic.h"
11+
12+
namespace Carbon {
13+
namespace {
14+
15+
using namespace ::testing;
16+
17+
struct FakeDiagnostic {
18+
static constexpr llvm::StringLiteral ShortName = "fake-diagnostic";
19+
// TODO: consider ways to put the Message into `format` to allow dynamic
20+
// selection of the message.
21+
static constexpr llvm::StringLiteral Message = "{0}";
22+
23+
struct Substitutions {
24+
std::string message;
25+
};
26+
static auto Format(const Substitutions& substitutions) -> std::string {
27+
// Work around a bug in Clang's unused const variable warning by marking it
28+
// used here with a no-op.
29+
static_cast<void>(ShortName);
30+
31+
return llvm::formatv(Message.data(), substitutions.message).str();
32+
}
33+
};
34+
35+
TEST(DiagTest, EmitErrors) {
36+
std::vector<std::string> reported;
37+
38+
DiagnosticEmitter emitter([&](const Diagnostic& diagnostic) {
39+
EXPECT_THAT(diagnostic.short_name, Eq("fake-diagnostic"));
40+
reported.push_back(diagnostic.message);
41+
});
42+
43+
emitter.EmitError<FakeDiagnostic>(
44+
[](FakeDiagnostic::Substitutions& diagnostic) {
45+
diagnostic.message = "M1";
46+
});
47+
emitter.EmitError<FakeDiagnostic>(
48+
[](FakeDiagnostic::Substitutions& diagnostic) {
49+
diagnostic.message = "M2";
50+
});
51+
52+
EXPECT_THAT(reported, ElementsAre("M1", "M2"));
53+
}
54+
55+
TEST(DiagTest, EmitWarnings) {
56+
std::vector<std::string> reported;
57+
58+
DiagnosticEmitter emitter([&](const Diagnostic& diagnostic) {
59+
EXPECT_THAT(diagnostic.short_name, Eq("fake-diagnostic"));
60+
reported.push_back(diagnostic.message);
61+
});
62+
63+
emitter.EmitWarningIf<FakeDiagnostic>(
64+
[](FakeDiagnostic::Substitutions& diagnostic) {
65+
diagnostic.message = "M1";
66+
return true;
67+
});
68+
emitter.EmitWarningIf<FakeDiagnostic>(
69+
[](FakeDiagnostic::Substitutions& diagnostic) {
70+
diagnostic.message = "M2";
71+
return false;
72+
});
73+
emitter.EmitWarningIf<FakeDiagnostic>(
74+
[](FakeDiagnostic::Substitutions& diagnostic) {
75+
diagnostic.message = "M3";
76+
return true;
77+
});
78+
79+
EXPECT_THAT(reported, ElementsAre("M1", "M3"));
80+
}
81+
82+
} // namespace
83+
} // namespace Carbon

0 commit comments

Comments
 (0)