Skip to content

Commit b423505

Browse files
committed
[Dependency Scanning] Post-process imports that fail to resolve against the cache entries added by other resolved imports
It is possible that import resolution failed because we are attempting to resolve a module which can only be brought in via a modulemap of a different Clang module dependency which is not otherwise on the current search paths. For example, suppose we are scanning a '.swiftinterface' for module 'Foo', which contains: ''' @_exported import Foo import Bar ... Where 'Foo' is the underlying Framework clang module whose '.modulemap' defines an auxiliary module 'Bar'. Because 'Foo' is a framework, its modulemap is under '<some_framework_search_path>/Foo.framework/Modules/module.modulemap'. Which means that lookup of `Bar` alone from Swift will not be able to locate the module in it. However, the lookup of Foo will itself bring in the auxiliary module becuase the Clang scanner instance scanning for clang module Foo will be able to find it in the corresponding framework module's modulemap and register it as a dependency which means it will be registered with the scanner's cache in the step above. To handle such cases, we first add all successfully-resolved modules and (for Clang modules) their transitive dependencies to the cache, and then attempt to re-query imports for which resolution originally failed from the cache. If this fails, then the scanner genuinely failed to resolve this dependency.
1 parent d047419 commit b423505

File tree

1 file changed

+47
-10
lines changed

1 file changed

+47
-10
lines changed

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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,55 @@ 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 = cache.findDependency(moduleName);
653+
if (optionalCachedModuleInfo.has_value())
654+
directDependencies.insert(
655+
{moduleName, optionalCachedModuleInfo.value()->getKind()});
656+
else
657+
diagnoseScannerFailure(moduleName, Diagnostics, cache, moduleID);
658+
}
622659
}
623660

624661
void ModuleDependencyScanner::resolveBridgingHeaderDependencies(

0 commit comments

Comments
 (0)