@@ -105,13 +105,29 @@ static auto AddImportIRInst(Context& context,
105
105
namespace {
106
106
107
107
// Used to convert Clang diagnostics to Carbon diagnostics.
108
+ //
109
+ // Handling of Clang notes is a little subtle: as far as Clang is concerned,
110
+ // notes are separate diagnostics, not connected to the error or warning that
111
+ // precedes them. But in Carbon's diagnostics system, notes are part of the
112
+ // enclosing diagnostic. To handle this, we buffer Clang diagnostics until we
113
+ // reach a point where we know we're not in the middle of a diagnostic, and then
114
+ // emit a diagnostic along with all of its notes. This is triggered when adding
115
+ // or removing a Carbon context note, which could otherwise get attached to the
116
+ // wrong C++ diagnostics, and at the end of the Carbon program.
108
117
class CarbonClangDiagnosticConsumer : public clang ::DiagnosticConsumer {
109
118
public:
110
119
// Creates an instance with the location that triggers calling Clang.
111
120
// `context` must not be null.
112
121
explicit CarbonClangDiagnosticConsumer (
113
122
Context* context, std::shared_ptr<clang::CompilerInvocation> invocation)
114
- : context_(context), invocation_(std::move(invocation)) {}
123
+ : context_(context), invocation_(std::move(invocation)) {
124
+ context->emitter ().AddFlushFn ([this ] { EmitDiagnostics (); });
125
+ }
126
+
127
+ ~CarbonClangDiagnosticConsumer () override {
128
+ CARBON_CHECK (diagnostic_infos_.empty (),
129
+ " Missing flush before destroying diagnostic consumer" );
130
+ }
115
131
116
132
// Generates a Carbon warning for each Clang warning and a Carbon error for
117
133
// each Clang error or fatal.
@@ -136,6 +152,8 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
136
152
return ;
137
153
}
138
154
155
+ // TODO: This includes the full clang diagnostic, including the source
156
+ // location, resulting in the location appearing twice in the output.
139
157
RawStringOstream diagnostics_stream;
140
158
clang::TextDiagnostic text_diagnostic (diagnostics_stream,
141
159
invocation_->getLangOpts (),
@@ -154,7 +172,11 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
154
172
// Outputs Carbon diagnostics based on the collected Clang diagnostics. Must
155
173
// be called after the AST is set in the context.
156
174
auto EmitDiagnostics () -> void {
157
- for (const ClangDiagnosticInfo& info : diagnostic_infos_) {
175
+ CARBON_CHECK (context_->sem_ir ().cpp_ast (),
176
+ " Attempted to emit diagnostics before the AST Unit is loaded" );
177
+
178
+ for (size_t i = 0 ; i != diagnostic_infos_.size (); ++i) {
179
+ const ClangDiagnosticInfo& info = diagnostic_infos_[i];
158
180
switch (info.level ) {
159
181
case clang::DiagnosticsEngine::Ignored:
160
182
case clang::DiagnosticsEngine::Note:
@@ -172,16 +194,27 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
172
194
CARBON_DIAGNOSTIC (CppInteropParseWarning, Warning, " {0}" ,
173
195
std::string);
174
196
CARBON_DIAGNOSTIC (CppInteropParseError, Error, " {0}" , std::string);
175
- context_->emitter ().Emit (
197
+ auto builder = context_->emitter ().Build (
176
198
SemIR::LocId (info.import_ir_inst_id ),
177
199
info.level == clang::DiagnosticsEngine::Warning
178
200
? CppInteropParseWarning
179
201
: CppInteropParseError,
180
202
info.message );
203
+ for (;
204
+ i + 1 < diagnostic_infos_.size () &&
205
+ diagnostic_infos_[i + 1 ].level == clang::DiagnosticsEngine::Note;
206
+ ++i) {
207
+ const ClangDiagnosticInfo& note_info = diagnostic_infos_[i + 1 ];
208
+ CARBON_DIAGNOSTIC (CppInteropParseNote, Note, " {0}" , std::string);
209
+ builder.Note (SemIR::LocId (note_info.import_ir_inst_id ),
210
+ CppInteropParseNote, note_info.message );
211
+ }
212
+ builder.Emit ();
181
213
break ;
182
214
}
183
215
}
184
216
}
217
+ diagnostic_infos_.clear ();
185
218
}
186
219
187
220
private:
@@ -223,12 +256,11 @@ static auto GenerateAst(Context& context,
223
256
std::shared_ptr<clang::CompilerInvocation> invocation)
224
257
-> std::pair<std::unique_ptr<clang::ASTUnit>, bool> {
225
258
// Build a diagnostics engine.
226
- auto diagnostics_consumer =
227
- std::make_unique<CarbonClangDiagnosticConsumer>(&context, invocation);
228
259
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags (
229
260
clang::CompilerInstance::createDiagnostics (
230
- *fs, invocation->getDiagnosticOpts (), diagnostics_consumer.get (),
231
- /* ShouldOwnClient=*/ false ));
261
+ *fs, invocation->getDiagnosticOpts (),
262
+ new CarbonClangDiagnosticConsumer (&context, invocation),
263
+ /* ShouldOwnClient=*/ true ));
232
264
233
265
// Extract the input from the frontend invocation and make sure it makes
234
266
// sense.
@@ -246,6 +278,8 @@ static auto GenerateAst(Context& context,
246
278
invocation->getPreprocessorOpts ().addRemappedFile (file_name,
247
279
includes_buffer.get ());
248
280
281
+ clang::DiagnosticErrorTrap trap (*diags);
282
+
249
283
// Create the AST unit.
250
284
auto ast = clang::ASTUnit::LoadFromCompilerInvocation (
251
285
invocation, std::make_shared<clang::PCHContainerOperations>(), nullptr ,
@@ -260,15 +294,9 @@ static auto GenerateAst(Context& context,
260
294
context.sem_ir ().set_cpp_ast (ast.get ());
261
295
262
296
// Emit any diagnostics we queued up while building the AST.
263
- diagnostics_consumer->EmitDiagnostics ();
264
- bool any_errors = diagnostics_consumer->getNumErrors () > 0 ;
297
+ context.emitter ().Flush ();
265
298
266
- // Transfer ownership of the consumer to the AST unit, in case more
267
- // diagnostics are produced by AST queries.
268
- ast->getDiagnostics ().setClient (diagnostics_consumer.release (),
269
- /* ShouldOwnClient=*/ true );
270
-
271
- return {std::move (ast), !ast || any_errors};
299
+ return {std::move (ast), !ast || trap.hasErrorOccurred ()};
272
300
}
273
301
274
302
// Adds a namespace for the `Cpp` import and returns its `NameScopeId`.
@@ -1436,12 +1464,18 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
1436
1464
return SemIR::ScopeLookupResult::MakeNotFound ();
1437
1465
}
1438
1466
1467
+ // Access checks are performed separately by the Carbon name lookup logic.
1468
+ lookup->suppressAccessDiagnostics ();
1469
+
1439
1470
if (!lookup->isSingleResult ()) {
1440
- context.TODO (loc_id,
1441
- llvm::formatv (" Unsupported: Lookup succeeded but couldn't "
1442
- " find a single result; LookupResultKind: {0}" ,
1443
- static_cast <int >(lookup->getResultKind ()))
1444
- .str ());
1471
+ // Clang will diagnose ambiguous lookup results for us.
1472
+ if (!lookup->isAmbiguous ()) {
1473
+ context.TODO (loc_id,
1474
+ llvm::formatv (" Unsupported: Lookup succeeded but couldn't "
1475
+ " find a single result; LookupResultKind: {0}" ,
1476
+ static_cast <int >(lookup->getResultKind ()))
1477
+ .str ());
1478
+ }
1445
1479
context.name_scopes ().AddRequiredName (scope_id, name_id,
1446
1480
SemIR::ErrorInst::InstId);
1447
1481
return SemIR::ScopeLookupResult::MakeError ();
0 commit comments