Skip to content

Commit bd2483b

Browse files
authored
Don't use Check::Context when emitting C++ diagnostics. (#5910)
Diagnostics can get flushed after the context is destroyed. Should fix an issue found by msan.
1 parent fd4dbc5 commit bd2483b

File tree

1 file changed

+67
-57
lines changed

1 file changed

+67
-57
lines changed

toolchain/check/import_cpp.cpp

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,12 @@ static auto AddIdentifierName(Context& context, llvm::StringRef name)
9393

9494
// Adds the given source location and an `ImportIRInst` referring to it in
9595
// `ImportIRId::Cpp`.
96-
static auto AddImportIRInst(Context& context,
96+
static auto AddImportIRInst(SemIR::File& file,
9797
clang::SourceLocation clang_source_loc)
9898
-> SemIR::ImportIRInstId {
9999
SemIR::ClangSourceLocId clang_source_loc_id =
100-
context.sem_ir().clang_source_locs().Add(clang_source_loc);
101-
return context.import_ir_insts().Add(
102-
SemIR::ImportIRInst(clang_source_loc_id));
100+
file.clang_source_locs().Add(clang_source_loc);
101+
return file.import_ir_insts().Add(SemIR::ImportIRInst(clang_source_loc_id));
103102
}
104103

105104
namespace {
@@ -116,15 +115,22 @@ namespace {
116115
// wrong C++ diagnostics, and at the end of the Carbon program.
117116
class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
118117
public:
119-
// Creates an instance with the location that triggers calling Clang.
120-
// `context` must not be null.
118+
// Creates an instance with the location that triggers calling Clang. The
119+
// `context` is not stored here, and the diagnostics consumer is expected to
120+
// outlive it.
121121
explicit CarbonClangDiagnosticConsumer(
122-
Context* context, std::shared_ptr<clang::CompilerInvocation> invocation)
123-
: context_(context), invocation_(std::move(invocation)) {
124-
context->emitter().AddFlushFn([this] { EmitDiagnostics(); });
122+
Context& context, std::shared_ptr<clang::CompilerInvocation> invocation)
123+
: sem_ir_(&context.sem_ir()),
124+
emitter_(&context.emitter()),
125+
invocation_(std::move(invocation)) {
126+
emitter_->AddFlushFn([this] { EmitDiagnostics(); });
125127
}
126128

127129
~CarbonClangDiagnosticConsumer() override {
130+
// Do not inspect `emitter_` here; it's typically destroyed before the
131+
// consumer is.
132+
// TODO: If Clang produces diagnostics after check finishes, they'll get
133+
// added to the list of pending diagnostics and never emitted.
128134
CARBON_CHECK(diagnostic_infos_.empty(),
129135
"Missing flush before destroying diagnostic consumer");
130136
}
@@ -136,7 +142,7 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
136142
DiagnosticConsumer::HandleDiagnostic(diag_level, info);
137143

138144
SemIR::ImportIRInstId clang_import_ir_inst_id =
139-
AddImportIRInst(*context_, info.getLocation());
145+
AddImportIRInst(*sem_ir_, info.getLocation());
140146

141147
llvm::SmallString<256> message;
142148
info.FormatDiagnostic(message);
@@ -163,57 +169,57 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
163169
.snippet = snippet_stream.TakeStr()});
164170
}
165171

172+
// Returns the diagnostic to use for a given Clang diagnostic level.
173+
static auto GetDiagnostic(clang::DiagnosticsEngine::Level level)
174+
-> const Diagnostics::DiagnosticBase<std::string>& {
175+
switch (level) {
176+
case clang::DiagnosticsEngine::Ignored: {
177+
CARBON_FATAL("Emitting an ignored diagnostic");
178+
break;
179+
}
180+
case clang::DiagnosticsEngine::Note: {
181+
CARBON_DIAGNOSTIC(CppInteropParseNote, Note, "{0}", std::string);
182+
return CppInteropParseNote;
183+
}
184+
case clang::DiagnosticsEngine::Remark:
185+
case clang::DiagnosticsEngine::Warning: {
186+
// TODO: Add a distinct Remark level to Carbon diagnostics, and stop
187+
// mapping remarks to warnings.
188+
CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning, "{0}", std::string);
189+
return CppInteropParseWarning;
190+
}
191+
case clang::DiagnosticsEngine::Error:
192+
case clang::DiagnosticsEngine::Fatal: {
193+
CARBON_DIAGNOSTIC(CppInteropParseError, Error, "{0}", std::string);
194+
return CppInteropParseError;
195+
}
196+
}
197+
}
198+
166199
// Outputs Carbon diagnostics based on the collected Clang diagnostics. Must
167200
// be called after the AST is set in the context.
168201
auto EmitDiagnostics() -> void {
169-
CARBON_CHECK(context_->sem_ir().cpp_ast(),
202+
CARBON_CHECK(sem_ir_->cpp_ast(),
170203
"Attempted to emit diagnostics before the AST Unit is loaded");
171204

172205
for (size_t i = 0; i != diagnostic_infos_.size(); ++i) {
173206
const ClangDiagnosticInfo& info = diagnostic_infos_[i];
174-
switch (info.level) {
175-
case clang::DiagnosticsEngine::Ignored:
176-
case clang::DiagnosticsEngine::Note:
177-
case clang::DiagnosticsEngine::Remark: {
178-
context_->TODO(
179-
SemIR::LocId(info.import_ir_inst_id),
180-
llvm::formatv(
181-
"Unsupported: C++ diagnostic level for diagnostic\n{0}",
182-
info.message));
183-
break;
184-
}
185-
case clang::DiagnosticsEngine::Warning:
186-
case clang::DiagnosticsEngine::Error:
187-
case clang::DiagnosticsEngine::Fatal: {
188-
CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning, "{0}",
189-
std::string);
190-
CARBON_DIAGNOSTIC(CppInteropParseError, Error, "{0}", std::string);
191-
auto builder = context_->emitter().Build(
192-
SemIR::LocId(info.import_ir_inst_id),
193-
info.level == clang::DiagnosticsEngine::Warning
194-
? CppInteropParseWarning
195-
: CppInteropParseError,
196-
info.message);
197-
builder.OverrideSnippet(info.snippet);
198-
for (;
199-
i + 1 < diagnostic_infos_.size() &&
200-
diagnostic_infos_[i + 1].level == clang::DiagnosticsEngine::Note;
201-
++i) {
202-
const ClangDiagnosticInfo& note_info = diagnostic_infos_[i + 1];
203-
CARBON_DIAGNOSTIC(CppInteropParseNote, Note, "{0}", std::string);
204-
builder
205-
.Note(SemIR::LocId(note_info.import_ir_inst_id),
206-
CppInteropParseNote, note_info.message)
207-
.OverrideSnippet(note_info.snippet);
208-
}
209-
// TODO: This will apply all current Carbon annotation functions. We
210-
// should instead track how Clang's context notes and Carbon's
211-
// annotation functions are interleaved, and interleave the notes in
212-
// the same order.
213-
builder.Emit();
214-
break;
215-
}
207+
auto builder = emitter_->Build(SemIR::LocId(info.import_ir_inst_id),
208+
GetDiagnostic(info.level), info.message);
209+
builder.OverrideSnippet(info.snippet);
210+
for (; i + 1 < diagnostic_infos_.size() &&
211+
diagnostic_infos_[i + 1].level == clang::DiagnosticsEngine::Note;
212+
++i) {
213+
const ClangDiagnosticInfo& note_info = diagnostic_infos_[i + 1];
214+
builder
215+
.Note(SemIR::LocId(note_info.import_ir_inst_id),
216+
GetDiagnostic(note_info.level), note_info.message)
217+
.OverrideSnippet(note_info.snippet);
216218
}
219+
// TODO: This will apply all current Carbon annotation functions. We
220+
// should instead track how Clang's context notes and Carbon's annotation
221+
// functions are interleaved, and interleave the notes in the same order.
222+
builder.Emit();
217223
}
218224
diagnostic_infos_.clear();
219225
}
@@ -267,8 +273,11 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
267273
std::string snippet;
268274
};
269275

270-
// The type-checking context in which we're running Clang.
271-
Context* context_;
276+
// The Carbon file that this C++ compilation is attached to.
277+
SemIR::File* sem_ir_;
278+
279+
// The diagnostic emitter that we're emitting diagnostics into.
280+
DiagnosticEmitterBase* emitter_;
272281

273282
// The compiler invocation that is producing the diagnostics.
274283
std::shared_ptr<clang::CompilerInvocation> invocation_;
@@ -316,7 +325,7 @@ static auto GenerateAst(
316325
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
317326
clang::CompilerInstance::createDiagnostics(
318327
*fs, invocation->getDiagnosticOpts(),
319-
new CarbonClangDiagnosticConsumer(&context, invocation),
328+
new CarbonClangDiagnosticConsumer(context, invocation),
320329
/*ShouldOwnClient=*/true));
321330

322331
// Extract the input from the frontend invocation and make sure it makes
@@ -587,7 +596,8 @@ static auto BuildClassDecl(Context& context,
587596
static auto ImportCXXRecordDecl(Context& context,
588597
clang::CXXRecordDecl* clang_decl)
589598
-> SemIR::InstId {
590-
auto import_ir_inst_id = AddImportIRInst(context, clang_decl->getLocation());
599+
auto import_ir_inst_id =
600+
AddImportIRInst(context.sem_ir(), clang_decl->getLocation());
591601

592602
auto [class_id, class_inst_id] = BuildClassDecl(
593603
context, import_ir_inst_id, GetParentNameScopeId(context, clang_decl),

0 commit comments

Comments
 (0)