@@ -265,6 +265,46 @@ static void reportSemanticAnnotations(const SourceTextInfo &IFaceInfo,
265
265
}
266
266
}
267
267
268
+ namespace {
269
+ // / A diagnostic consumer that picks up module loading errors.
270
+ class ModuleLoadingErrorConsumer final : public DiagnosticConsumer {
271
+ llvm::SmallVector<std::string, 2 > DiagMessages;
272
+
273
+ void handleDiagnostic (SourceManager &SM,
274
+ const DiagnosticInfo &Info) override {
275
+ // Only record errors for now. In the future it might be useful to pick up
276
+ // some notes, but some notes are just noise.
277
+ if (Info.Kind != DiagnosticKind::Error)
278
+ return ;
279
+
280
+ std::string Message;
281
+ {
282
+ llvm::raw_string_ostream Out (Message);
283
+ DiagnosticEngine::formatDiagnosticText (Out, Info.FormatString ,
284
+ Info.FormatArgs );
285
+ }
286
+ // We're only interested in the first and last errors. For a clang module
287
+ // failure, the first error will be the reason why the module failed to
288
+ // load, and the last error will be a generic "could not build Obj-C module"
289
+ // error. For a Swift module, we'll typically only emit one error.
290
+ //
291
+ // NOTE: Currently when loading transitive dependencies for a Swift module,
292
+ // we'll only diagnose the root failure, and not record the error for the
293
+ // top-level module failure, as we stop emitting errors after a fatal error
294
+ // has been recorded. This is currently fine for our use case though, as
295
+ // we already include the top-level module name in the error we hand back.
296
+ if (DiagMessages.size () < 2 ) {
297
+ DiagMessages.emplace_back (std::move (Message));
298
+ } else {
299
+ DiagMessages.back () = std::move (Message);
300
+ }
301
+ }
302
+
303
+ public:
304
+ ArrayRef<std::string> getDiagMessages () { return DiagMessages; }
305
+ };
306
+ } // end anonymous namespace
307
+
268
308
static bool getModuleInterfaceInfo (ASTContext &Ctx,
269
309
StringRef ModuleName,
270
310
Optional<StringRef> Group,
@@ -280,21 +320,35 @@ static bool getModuleInterfaceInfo(ASTContext &Ctx,
280
320
return true ;
281
321
}
282
322
283
- // Get the (sub)module to generate.
284
- Mod = Ctx.getModuleByName (ModuleName);
285
- if (!Mod) {
286
- ErrMsg = " Could not load module: " ;
287
- ErrMsg += ModuleName;
288
- return true ;
289
- }
290
- if (Mod->failedToLoad ()) {
291
- // We might fail to load the underlying Clang module
292
- // for a Swift overlay module like 'CxxStdlib', or a mixed-language
293
- // framework. Make sure an error is reported in this case, so that we can
294
- // either retry to load with C++ interoperability enabled, and if that
295
- // fails, we can report this to the user.
296
- ErrMsg = " Could not load underlying module for: " ;
297
- ErrMsg += ModuleName;
323
+ // Get the (sub)module to generate, recording the errors emitted.
324
+ ModuleLoadingErrorConsumer DiagConsumer;
325
+ {
326
+ DiagnosticConsumerRAII R (Ctx.Diags , DiagConsumer);
327
+ Mod = Ctx.getModuleByName (ModuleName);
328
+ }
329
+
330
+ // Check to see if we either couldn't find the module, or we failed to load
331
+ // it, and report an error message back that includes the diagnostics we
332
+ // collected, which should help pinpoint what the issue was. Note we do this
333
+ // even if `Mod` is null, as the clang importer currently returns nullptr
334
+ // when a module fails to load, and there may be interesting errors to
335
+ // collect there.
336
+ // Note that us failing here also means the caller may retry with e.g C++
337
+ // interoperability enabled.
338
+ if (!Mod || Mod->failedToLoad ()) {
339
+ llvm::raw_string_ostream OS (ErrMsg);
340
+
341
+ OS << " Could not load module: " ;
342
+ OS << ModuleName;
343
+ auto ModuleErrs = DiagConsumer.getDiagMessages ();
344
+ if (!ModuleErrs.empty ()) {
345
+ // We print the errors in reverse, as they are typically emitted in
346
+ // a bottom-up manner by module loading, and a top-down presentation
347
+ // makes more sense.
348
+ OS << " (" ;
349
+ llvm::interleaveComma (llvm::reverse (ModuleErrs), OS);
350
+ OS << " )" ;
351
+ }
298
352
return true ;
299
353
}
300
354
0 commit comments