@@ -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
105104namespace {
@@ -116,15 +115,22 @@ namespace {
116115// wrong C++ diagnostics, and at the end of the Carbon program.
117116class 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,
587596static 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