@@ -548,8 +548,8 @@ void ModuleDependencyScanner::resolveImportDependencies(
548548 // A scanning task to query a module by-name. If the module already exists
549549 // in the cache, do nothing and return.
550550 auto scanForModuleDependency = [this , &cache, &moduleLookupResult](
551- StringRef moduleName, bool onlyClangModule,
552- bool isTestable) {
551+ StringRef moduleName, bool onlyClangModule,
552+ bool isTestable) {
553553 // If this is already in the cache, no work to do here
554554 if (onlyClangModule) {
555555 if (cache.hasDependency (moduleName, ModuleDependencyKind::Clang))
@@ -587,9 +587,10 @@ void ModuleDependencyScanner::resolveImportDependencies(
587587 }
588588 ScanningThreadPool.wait ();
589589
590+ std::vector<std::string> unresolvedImports;
590591 // Aggregate both previously-cached and freshly-scanned module results
591592 auto recordResolvedModuleImport =
592- [this , &cache, &moduleLookupResult, &directDependencies,
593+ [&cache, &moduleLookupResult, &unresolvedImports , &directDependencies,
593594 moduleID](const std::string &moduleName, bool optionalImport) {
594595 bool underlyingClangModule = moduleID.ModuleName == moduleName;
595596 auto lookupResult = moduleLookupResult[moduleName];
@@ -606,19 +607,56 @@ void ModuleDependencyScanner::resolveImportDependencies(
606607 directDependencies.insert ({moduleName, cachedInfo->getKind ()});
607608 } else {
608609 // Cache discovered module dependencies.
609- cache. recordDependencies ( lookupResult.value ());
610- if (! lookupResult.value (). empty ())
610+ if (! lookupResult.value (). empty ()) {
611+ cache. recordDependencies ( lookupResult.value ());
611612 directDependencies.insert (
612613 {moduleName, lookupResult.value ()[0 ].first .Kind });
613- else if (!optionalImport)
614- diagnoseScannerFailure (moduleName, Diagnostics, cache, moduleID);
614+ } else if (!optionalImport) {
615+ // Otherwise, we failed to resolve this dependency. We will try
616+ // again using the cache after all other imports have been resolved.
617+ // If that fails too, a scanning failure will be diagnosed.
618+ unresolvedImports.push_back (moduleName);
619+ }
615620 }
616621 };
617- for (const auto &dependsOn : moduleDependencyInfo->getModuleImports ())
618- recordResolvedModuleImport (dependsOn, false );
619- for (const auto &optionallyDependsOn :
620- moduleDependencyInfo->getOptionalModuleImports ())
621- recordResolvedModuleImport (optionallyDependsOn, true );
622+ for (const auto &import : moduleDependencyInfo->getModuleImports ())
623+ recordResolvedModuleImport (import , /* optionalImport */ false );
624+ for (const auto &import : moduleDependencyInfo->getOptionalModuleImports ())
625+ recordResolvedModuleImport (import , /* optionalImport */ true );
626+
627+ // It is possible that import resolution failed because we are attempting to
628+ // resolve a module which can only be brought in via a modulemap of a
629+ // different Clang module dependency which is not otherwise on the current
630+ // search paths. For example, suppose we are scanning a `.swiftinterface` for
631+ // module `Foo`, which contains:
632+ // -----
633+ // @_exported import Foo
634+ // import Bar
635+ // ...
636+ // -----
637+ // Where `Foo` is the underlying Framework clang module whose .modulemap
638+ // defines an auxiliary module `Bar`. Because Foo is a framework, its
639+ // modulemap is under
640+ // `<some_framework_search_path>/Foo.framework/Modules/module.modulemap`.
641+ // Which means that lookup of `Bar` alone from Swift will not be able to
642+ // locate the module in it. However, the lookup of Foo will itself bring in
643+ // the auxiliary module becuase the Clang scanner instance scanning for clang
644+ // module Foo will be able to find it in the corresponding framework module's
645+ // modulemap and register it as a dependency which means it will be registered
646+ // with the scanner's cache in the step above. To handle such cases, we
647+ // first add all successfully-resolved modules and (for Clang modules) their
648+ // transitive dependencies to the cache, and then attempt to re-query imports
649+ // for which resolution originally failed from the cache. If this fails, then
650+ // the scanner genuinely failed to resolve this dependency.
651+ for (const auto &moduleName : unresolvedImports) {
652+ auto optionalCachedModuleInfo =
653+ cache.findDependency ({moduleName, ModuleDependencyKind::Clang});
654+ if (optionalCachedModuleInfo.has_value ())
655+ directDependencies.insert (
656+ {moduleName, optionalCachedModuleInfo.value ()->getKind ()});
657+ else
658+ diagnoseScannerFailure (moduleName, Diagnostics, cache, moduleID);
659+ }
622660}
623661
624662void ModuleDependencyScanner::resolveBridgingHeaderDependencies (
0 commit comments