@@ -265,6 +265,46 @@ static void reportSemanticAnnotations(const SourceTextInfo &IFaceInfo,
265265 }
266266}
267267
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+
268308static bool getModuleInterfaceInfo (ASTContext &Ctx,
269309 StringRef ModuleName,
270310 Optional<StringRef> Group,
@@ -280,21 +320,35 @@ static bool getModuleInterfaceInfo(ASTContext &Ctx,
280320 return true ;
281321 }
282322
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+ }
298352 return true ;
299353 }
300354
0 commit comments