Merge pull request #82939 from artemcm/62_DepScanExplicitInput

[6.2 🍒][Dependency Scanning] Consider `-swift-module-file` inputs when looking for dependencies
This commit is contained in:
Artem Chikin
2025-08-12 23:00:13 -07:00
committed by GitHub
17 changed files with 139 additions and 24 deletions

View File

@@ -213,6 +213,9 @@ ERROR(error_stdlib_module_name,none,
"module name \"%0\" is reserved for the standard library"
"%select{|; use -module-name flag to specify an alternate name}1",
(StringRef, bool))
WARNING(warn_multiple_module_inputs_same_name,none,
"multiple Swift module file inputs with identifier \"%0\": replacing '%1' with '%2'",
(StringRef, StringRef, StringRef))
ERROR(error_bad_export_as_name,none,
"export-as name \"%0\" is not a valid identifier",

View File

@@ -375,6 +375,7 @@ public:
StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper *mapper = nullptr,
bool isTestableImport = false) = 0;

View File

@@ -519,7 +519,7 @@ public:
/// Module inputs specified with -swift-module-input,
/// <ModuleName, Path to .swiftmodule file>
std::vector<std::pair<std::string, std::string>> ExplicitSwiftModuleInputs;
llvm::StringMap<std::string> ExplicitSwiftModuleInputs;
/// A map of placeholder Swift module dependency information.
std::string PlaceholderDependencyModuleMap;

View File

@@ -508,6 +508,7 @@ public:
getModuleDependencies(Identifier moduleName, StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper *mapper,
bool isTestableImport = false) override;

View File

@@ -101,6 +101,9 @@ private:
std::vector<std::string> swiftModuleClangCC1CommandLineArgs;
// Working directory for clang module lookup queries
std::string clangScanningWorkingDirectoryPath;
/// Module inputs specified with -swift-module-input,
llvm::StringMap<std::string> explicitSwiftModuleInputs;
// CAS instance.
std::shared_ptr<llvm::cas::ObjectStore> CAS;

View File

@@ -176,7 +176,7 @@ public:
create(ASTContext &ctx,
DependencyTracker *tracker, ModuleLoadingMode loadMode,
StringRef ExplicitSwiftModuleMap,
const std::vector<std::pair<std::string, std::string>> &ExplicitSwiftModuleInputs,
const llvm::StringMap<std::string> &ExplicitSwiftModuleInputs,
bool IgnoreSwiftSourceInfoFile);
/// Append visible module names to \p names. Note that names are possibly
@@ -224,8 +224,7 @@ public:
create(ASTContext &ctx, llvm::cas::ObjectStore &CAS,
llvm::cas::ActionCache &cache, DependencyTracker *tracker,
ModuleLoadingMode loadMode, StringRef ExplicitSwiftModuleMap,
const std::vector<std::pair<std::string, std::string>>
&ExplicitSwiftModuleInputs,
const llvm::StringMap<std::string> &ExplicitSwiftModuleInputs,
bool IgnoreSwiftSourceInfoFile);
/// Append visible module names to \p names. Note that names are possibly

View File

@@ -102,6 +102,7 @@ public:
getModuleDependencies(Identifier moduleName, StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper *mapper,
bool isTestableImport) override;

View File

@@ -46,6 +46,8 @@ private:
/// Clang-specific (-Xcc) command-line flags to include on
/// Swift module compilation commands
std::vector<std::string> swiftModuleClangCC1CommandLineArgs;
/// Module inputs specified with -swift-module-input
llvm::StringMap<std::string> explicitSwiftModuleInputs;
public:
std::optional<ModuleDependencyInfo> dependencies;
@@ -55,13 +57,15 @@ public:
InterfaceSubContextDelegate &astDelegate,
StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
std::vector<std::string> swiftModuleClangCC1CommandLineArgs,
llvm::StringMap<std::string> explicitSwiftModuleInputs,
ScannerKind kind = MDS_plain)
: SerializedModuleLoaderBase(ctx, nullptr, LoadMode,
/*IgnoreSwiftSourceInfoFile=*/true),
kind(kind), moduleName(moduleName), astDelegate(astDelegate),
moduleOutputPath(moduleOutputPath),
sdkModuleOutputPath(sdkModuleOutputPath),
swiftModuleClangCC1CommandLineArgs(swiftModuleClangCC1CommandLineArgs) {}
swiftModuleClangCC1CommandLineArgs(swiftModuleClangCC1CommandLineArgs),
explicitSwiftModuleInputs(explicitSwiftModuleInputs) {}
std::error_code findModuleFilesInDirectory(
ImportPath::Element ModuleID, const SerializedModuleBaseName &BaseName,
@@ -73,6 +77,10 @@ public:
bool SkipBuildingInterface, bool IsFramework,
bool IsTestableDependencyLookup) override;
bool canImportModule(ImportPath::Module named, SourceLoc loc,
ModuleVersionInfo *versionInfo,
bool isTestableImport) override;
virtual void collectVisibleTopLevelModuleNames(
SmallVectorImpl<Identifier> &names) const override {
llvm_unreachable("Not used");
@@ -105,7 +113,7 @@ public:
StringRef moduleOutputPath,
StringRef sdkModuleOutputPath)
: SwiftModuleScanner(ctx, LoadMode, moduleName, astDelegate,
moduleOutputPath, sdkModuleOutputPath, {},
moduleOutputPath, sdkModuleOutputPath, {}, {},
MDS_placeholder) {
// FIXME: Find a better place for this map to live, to avoid
// doing the parsing on every module.

View File

@@ -266,6 +266,7 @@ public:
getModuleDependencies(Identifier moduleName, StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper *mapper,
bool isTestableImport) override;

View File

@@ -275,6 +275,7 @@ ClangImporter::getModuleDependencies(Identifier moduleName,
StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper *mapper,
bool isTestableImport) {

View File

@@ -259,6 +259,9 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker(
swiftModuleClangCC1CommandLineArgs.push_back("-fno-implicit-module-maps");
}
explicitSwiftModuleInputs =
workerCompilerInvocation->getSearchPathOptions().ExplicitSwiftModuleInputs;
// Set up the Swift interface loader for Swift scanning.
swiftScannerModuleLoader = ModuleInterfaceLoader::create(
*workerASTContext,
@@ -275,8 +278,8 @@ ModuleDependencyScanningWorker::scanFilesystemForSwiftModuleDependency(
bool isTestableImport) {
return swiftScannerModuleLoader->getModuleDependencies(
moduleName, moduleOutputPath, sdkModuleOutputPath,
{}, swiftModuleClangCC1CommandLineArgs, *scanningASTDelegate,
prefixMapper, isTestableImport);
{}, swiftModuleClangCC1CommandLineArgs, explicitSwiftModuleInputs,
*scanningASTDelegate, prefixMapper, isTestableImport);
}
ModuleDependencyVector
@@ -299,8 +302,7 @@ ModuleDependencyScanningWorker::scanFilesystemForClangModuleDependency(
auto clangModuleDependencies = clangScanningTool.getModuleDependencies(
moduleName.str(), clangScanningModuleCommandLineArgs,
clangScanningWorkingDirectoryPath,
alreadySeenModules,
lookupModuleOutput);
alreadySeenModules, lookupModuleOutput);
if (!clangModuleDependencies) {
auto errorStr = toString(clangModuleDependencies.takeError());
// We ignore the "module 'foo' not found" error, the Swift dependency

View File

@@ -2277,7 +2277,7 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts,
static bool validateSwiftModuleFileArgumentAndAdd(const std::string &swiftModuleArgument,
DiagnosticEngine &Diags,
std::vector<std::pair<std::string, std::string>> &ExplicitSwiftModuleInputs) {
llvm::StringMap<std::string> &ExplicitSwiftModuleInputs) {
std::size_t foundDelimeterPos = swiftModuleArgument.find_first_of("=");
if (foundDelimeterPos == std::string::npos) {
Diags.diagnose(SourceLoc(), diag::error_swift_module_file_requires_delimeter,
@@ -2290,7 +2290,15 @@ static bool validateSwiftModuleFileArgumentAndAdd(const std::string &swiftModule
Diags.diagnose(SourceLoc(), diag::error_bad_module_name, moduleName, false);
return true;
}
ExplicitSwiftModuleInputs.emplace_back(std::make_pair(moduleName, modulePath));
auto priorEntryIt = ExplicitSwiftModuleInputs.find(moduleName);
if (priorEntryIt != ExplicitSwiftModuleInputs.end()) {
Diags.diagnose(SourceLoc(), diag::warn_multiple_module_inputs_same_name,
moduleName, priorEntryIt->getValue(), modulePath);
ExplicitSwiftModuleInputs[moduleName] = modulePath;
} else
ExplicitSwiftModuleInputs.insert(std::make_pair(moduleName, modulePath));
return false;
}

View File

@@ -2251,11 +2251,10 @@ struct ExplicitSwiftModuleLoader::Implementation {
}
void addCommandLineExplicitInputs(
const std::vector<std::pair<std::string, std::string>>
&commandLineExplicitInputs) {
const llvm::StringMap<std::string> &commandLineExplicitInputs) {
for (const auto &moduleInput : commandLineExplicitInputs) {
ExplicitSwiftModuleInputInfo entry(moduleInput.second, {}, {}, {});
ExplicitModuleMap.try_emplace(moduleInput.first, std::move(entry));
ExplicitSwiftModuleInputInfo entry(moduleInput.getValue(), {}, {}, {});
ExplicitModuleMap.try_emplace(moduleInput.getKey(), std::move(entry));
}
}
};
@@ -2427,7 +2426,7 @@ std::unique_ptr<ExplicitSwiftModuleLoader>
ExplicitSwiftModuleLoader::create(ASTContext &ctx,
DependencyTracker *tracker, ModuleLoadingMode loadMode,
StringRef ExplicitSwiftModuleMap,
const std::vector<std::pair<std::string, std::string>> &ExplicitSwiftModuleInputs,
const llvm::StringMap<std::string> &ExplicitSwiftModuleInputs,
bool IgnoreSwiftSourceInfoFile) {
auto result = std::unique_ptr<ExplicitSwiftModuleLoader>(
new ExplicitSwiftModuleLoader(ctx, tracker, loadMode,
@@ -2541,11 +2540,10 @@ struct ExplicitCASModuleLoader::Implementation {
}
void addCommandLineExplicitInputs(
const std::vector<std::pair<std::string, std::string>>
&commandLineExplicitInputs) {
const llvm::StringMap<std::string> &commandLineExplicitInputs) {
for (const auto &moduleInput : commandLineExplicitInputs) {
ExplicitSwiftModuleInputInfo entry(moduleInput.second, {}, {}, {});
ExplicitModuleMap.try_emplace(moduleInput.first, std::move(entry));
ExplicitSwiftModuleInputInfo entry(moduleInput.getValue(), {}, {}, {});
ExplicitModuleMap.try_emplace(moduleInput.getKey(), std::move(entry));
}
}
@@ -2782,8 +2780,7 @@ std::unique_ptr<ExplicitCASModuleLoader> ExplicitCASModuleLoader::create(
ASTContext &ctx, llvm::cas::ObjectStore &CAS, llvm::cas::ActionCache &cache,
DependencyTracker *tracker, ModuleLoadingMode loadMode,
StringRef ExplicitSwiftModuleMap,
const std::vector<std::pair<std::string, std::string>>
&ExplicitSwiftModuleInputs,
const llvm::StringMap<std::string> &ExplicitSwiftModuleInputs,
bool IgnoreSwiftSourceInfoFile) {
auto result =
std::unique_ptr<ExplicitCASModuleLoader>(new ExplicitCASModuleLoader(

View File

@@ -159,6 +159,7 @@ SourceLoader::getModuleDependencies(Identifier moduleName,
StringRef moduleOutputPath, StringRef sdkModuleOutputPath,
const llvm::DenseSet<clang::tooling::dependencies::ModuleID> &alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate,
llvm::PrefixMapper* mapper,
bool isTestableImport) {

View File

@@ -83,6 +83,30 @@ std::error_code SwiftModuleScanner::findModuleFilesInDirectory(
return dependencies.getError();
}
bool SwiftModuleScanner::canImportModule(ImportPath::Module path, SourceLoc loc,
ModuleVersionInfo *versionInfo,
bool isTestableDependencyLookup) {
if (path.hasSubmodule())
return false;
// Check explicitly-provided Swift modules with '-swift-module-file'
ImportPath::Element mID = path.front();
auto it =
explicitSwiftModuleInputs.find(Ctx.getRealModuleName(mID.Item).str());
if (it != explicitSwiftModuleInputs.end()) {
auto dependencies = scanModuleFile(it->getValue(), /* IsFramework */ false,
isTestableDependencyLookup,
/* isCandidateForTextualModule */ false);
if (dependencies) {
this->dependencies = std::move(dependencies.get());
return true;
}
}
return SerializedModuleLoaderBase::canImportModule(
path, loc, versionInfo, isTestableDependencyLookup);
}
bool PlaceholderSwiftModuleScanner::findModule(
ImportPath::Element moduleID, SmallVectorImpl<char> *moduleInterfacePath,
SmallVectorImpl<char> *moduleInterfaceSourcePath,
@@ -291,6 +315,7 @@ ModuleDependencyVector SerializedModuleLoaderBase::getModuleDependencies(
const llvm::DenseSet<clang::tooling::dependencies::ModuleID>
&alreadySeenClangModules,
const std::vector<std::string> &swiftModuleClangCC1CommandLineArgs,
const llvm::StringMap<std::string> &explicitSwiftModuleInputs,
InterfaceSubContextDelegate &delegate, llvm::PrefixMapper *mapper,
bool isTestableDependencyLookup) {
ImportPath::Module::Builder builder(moduleName);
@@ -309,7 +334,7 @@ ModuleDependencyVector SerializedModuleLoaderBase::getModuleDependencies(
delegate, moduleOutputPath, sdkModuleOutputPath));
scanners.push_back(std::make_unique<SwiftModuleScanner>(
Ctx, LoadMode, moduleId, delegate, moduleOutputPath, sdkModuleOutputPath,
swiftModuleClangCC1CommandLineArgs,
swiftModuleClangCC1CommandLineArgs, explicitSwiftModuleInputs,
SwiftModuleScanner::MDS_plain));
// Check whether there is a module with this name that we can import.

View File

@@ -0,0 +1,31 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/module-cache)
// RUN: %empty-directory(%t/Inputs/Foo.swiftmodule)
// RUN: split-file %s %t
// Step 1: build swift interface and swift module side by side
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Inputs/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo -user-module-version 22
// Step 2: scan dependency should give us the binary module we specify with 'swift-module-file'
// RUN: %target-swift-frontend -scan-dependencies %t/test.swift -o %t/deps.json -scanner-module-validation -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/%target-swiftmodule-name
// RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=CHECK-INPUT
// Step 3: ensure that versioned canImport still applies with direct -swift-module-file
// RUN: %target-swift-frontend -scan-dependencies %t/test_too_new.swift -o %t/deps.json -scanner-module-validation -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/%target-swiftmodule-name
// RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=CHECK-MISSING
// CHECK-INPUT: "swiftPrebuiltExternal": "Foo"
// CHECK-MISSING-NOT: "swiftPrebuiltExternal": "Foo"
//--- Foo.swift
public func foo() {}
//--- test.swift
#if canImport(Foo)
import Foo
#endif
//--- test_too_new.swift
#if canImport(Foo, _version: 23)
import Foo
#endif

View File

@@ -0,0 +1,33 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/module-cache)
// RUN: %empty-directory(%t/Inputs/Foo.swiftmodule)
// RUN: split-file %s %t
// Step 1: build swift interface and swift module side by side
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Inputs/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo
// Step 2: scan dependency should give us the binary module we specify with 'swift-module-file'
// RUN: %target-swift-frontend -scan-dependencies %t/test.swift -o %t/deps.json -scanner-module-validation -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/%target-swiftmodule-name
// RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=CHECK-INPUT
// Step 3: ensure that if multiple inputs for the same module are specified then a warning is emitted and the latter is preferred
// RUN: echo "Gibberish" > %t/Inputs/Foo.swiftmodule/NotAModule.swiftmodule
// RUN: %target-swift-frontend -scan-dependencies %t/test.swift -o %t/deps.json -scanner-module-validation -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/NotAModule.swiftmodule -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/%target-swiftmodule-name -diagnostic-style llvm 2>&1 | %FileCheck %s -check-prefix=CHECK-WARN-MULTIPLE
// RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=CHECK-INPUT
// Step 4: verify that the usual invalid module candidate diagnostics apply
// RUN: echo "Not Really a module" > %t/Inputs/Foo.swiftmodule/%target-swiftmodule-name
// RUN: %target-swift-frontend -scan-dependencies %t/test.swift -o %t/deps.json -scanner-module-validation -swift-module-file=Foo=%t/Inputs/Foo.swiftmodule/%target-swiftmodule-name -diagnostic-style llvm 2>&1 | %FileCheck %s -check-prefix=CHECK-INVALID-MODULE-DIAG
// CHECK-INPUT: "swiftPrebuiltExternal": "Foo"
// CHECK-WARN-MULTIPLE: warning: multiple Swift module file inputs with identifier "Foo": replacing '{{.*}}NotAModule.swiftmodule'
// CHECK-INVALID-MODULE-DIAG: warning: module file '{{.*}}Foo.swiftmodule{{/|\\}}{{.*}}.swiftmodule' is incompatible with this Swift compiler: malformed
// CHECK-INVALID-MODULE-DIAG: error: Unable to find module dependency: 'Foo'
// CHECK-INVALID-MODULE-DIAG: note: a dependency of main module 'deps'
//--- Foo.swift
public func foo() {}
//--- test.swift
import Foo