Skip to content

Commit c43205c

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 d691198 commit c43205c

File tree

9 files changed

+99
-20
lines changed

9 files changed

+99
-20
lines changed

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

624662
void ModuleDependencyScanner::resolveBridgingHeaderDependencies(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-compiler-version: Apple Swift version 5.9
3+
// swift-module-flags: -target arm64-apple-macos10.13 -enable-library-evolution -swift-version 5 -module-name ScannerTestKit
4+
import Swift
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-compiler-version: Apple Swift version 5.9
3+
// swift-module-flags: -target arm64e-apple-macos10.13 -enable-library-evolution -swift-version 5 -module-name ScannerTestKit
4+
import Swift
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-compiler-version: Apple Swift version 5.9
3+
// swift-module-flags: -target x86_64-apple-macos10.13 -enable-library-evolution -swift-version 5 -module-name ScannerTestKit
4+
import Swift
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void funcAux(void);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include "WithAuxClangModule/AuxClangModule.h"
2+
void funcWithAux(void);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
framework module WithAuxClangModule {
2+
header "WithAuxClangModule.h"
3+
export *
4+
}
5+
6+
framework module AuxClangModule {
7+
header "AuxClangModule.h"
8+
export *
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// REQUIRES: OS=macosx
2+
// RUN: %empty-directory(%t)
3+
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -F %S/Inputs/Frameworks -verify
4+
// Check the contents of the JSON output
5+
// RUN: %validate-json %t/deps.json | %FileCheck %s
6+
7+
// Ensure that round-trip serialization does not affect result
8+
// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -F %S/Inputs/Frameworks -verify
9+
// RUN: %validate-json %t/deps.json | %FileCheck %s
10+
11+
import WithAuxClangModule
12+
import AuxClangModule
13+
14+
// CHECK: "mainModuleName": "deps"
15+
// CHECK: directDependencies
16+
// CHECK-DAG: "clang": "WithAuxClangModule"
17+
// CHECK-DAG: "clang": "AuxClangModule"
18+
// CHECK-DAG: "swift": "Swift"
19+
// CHECK-DAG: "swift": "SwiftOnoneSupport"
20+
// CHECK: ],
Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
1+
// REQUIRES: OS=macosx
12
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -Xcc -Xclang
3+
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -F %S/Inputs/Frameworks
34
// Check the contents of the JSON output
45
// RUN: %validate-json %t/deps.json | %FileCheck %s
56

67
// Ensure that round-trip serialization does not affect result
7-
// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -Xcc -Xclang
8+
// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -F %S/Inputs/Frameworks
89
// RUN: %validate-json %t/deps.json | %FileCheck %s
910

10-
// REQUIRES: OS=macosx
11-
12-
import CryptoKit
11+
import ScannerTestKit
1312

1413
// CHECK: "mainModuleName": "deps"
1514
// CHECK: directDependencies
16-
// CHECK-DAG: "swift": "CryptoKit"
15+
// CHECK-DAG: "swift": "ScannerTestKit"
1716
// CHECK-DAG: "swift": "Swift"
1817
// CHECK-DAG: "swift": "SwiftOnoneSupport"
19-
// CHECK-DAG: "swift": "_Concurrency"
20-
// CHECK-DAG: "swift": "_StringProcessing"
2118
// CHECK: ],
2219

2320
// CHECK: "isFramework": true

0 commit comments

Comments
 (0)