From 5dc42ffb0c50700860c4e92ed5b8373e124a8fba Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 2 Jun 2025 17:27:03 -0700 Subject: [PATCH 1/3] [Dependency Scanning] On failure to locate a module, attempt to diagnose if binary dependencies contain search paths with this module. Unlike with implicitly-built modules (prior to Swift 6 mode), explicitly-built modules require that all search paths be specified explicitly and no longer inherit search paths serialized into discovered Swift binary modules. This behavior was never intentional and is considered a bug. This change adds a diagnostic note to a scan failure: for each binary Swift module dependency, the scanner will attempt to execute a dependency scanning query for each serialized search path inside that module. If such diagnostic query returns a result, a diagnostic will be emitted to inform the user that the dependency may be found in the search path configuration of another Swift binary module dependency, specifying which search path contains the "missing" module, and stating that such search paths are not automatically inherited by the current compilation. --- include/swift/AST/DiagnosticsSema.def | 3 + include/swift/AST/ModuleDependencies.h | 27 +- .../DependencyScan/ModuleDependencyScanner.h | 16 ++ .../ModuleDependencyCacheSerialization.cpp | 1 + .../ModuleDependencyScanner.cpp | 254 +++++++++++++----- lib/Serialization/ModuleFileSharedCore.h | 4 + lib/Serialization/SerializedModuleLoader.cpp | 8 +- ...ing-module-found-in-serialized-paths.swift | 34 +++ 8 files changed, 262 insertions(+), 85 deletions(-) create mode 100644 test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8f0d4308f03df..af11f38ca0516 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2338,6 +2338,9 @@ NOTE(dependency_as_imported_by_main_module,none, "a dependency of main module '%0'", (StringRef)) NOTE(dependency_as_imported_by, none, "a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool)) +NOTE(inherited_search_path_resolves_module,none, + "'%0' can be found on search path used to build module '%1': '%2'. " + "These search paths are not inherited by the current compilation.", (StringRef, StringRef, StringRef)) ERROR(clang_dependency_scan_error, none, "Clang dependency scanner failure: %0", (StringRef)) ERROR(clang_header_dependency_scan_error, none, "Bridging header dependency scan failure: %0", (StringRef)) diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 86e8071aad3fc..e6d0581dab5a4 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -22,6 +22,7 @@ #include "swift/AST/LinkLibrary.h" #include "swift/Basic/CXXStdlibKind.h" #include "swift/Basic/LLVM.h" +#include "swift/Serialization/Validation.h" #include "clang/CAS/CASOptions.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" @@ -406,15 +407,18 @@ class SwiftBinaryModuleDependencyStorage StringRef sourceInfoPath, ArrayRef moduleImports, ArrayRef optionalModuleImports, - ArrayRef linkLibraries, StringRef headerImport, - StringRef definingModuleInterface, bool isFramework, bool isStatic, - StringRef moduleCacheKey, StringRef userModuleVersion) + ArrayRef linkLibraries, + ArrayRef serializedSearchPaths, + StringRef headerImport, StringRef definingModuleInterface, + bool isFramework, bool isStatic, StringRef moduleCacheKey, + StringRef userModuleVersion) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary, moduleImports, optionalModuleImports, linkLibraries, moduleCacheKey), compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath), headerImport(headerImport), definingModuleInterfacePath(definingModuleInterface), + serializedSearchPaths(serializedSearchPaths), isFramework(isFramework), isStatic(isStatic), userModuleVersion(userModuleVersion) {} @@ -441,6 +445,9 @@ class SwiftBinaryModuleDependencyStorage /// Source files on which the header inputs depend. std::vector headerSourceFiles; + /// Search paths this module was built with which got serialized + std::vector serializedSearchPaths; + /// (Clang) modules on which the header inputs depend. std::vector headerModuleDependencies; @@ -613,15 +620,17 @@ class ModuleDependencyInfo { StringRef sourceInfoPath, ArrayRef moduleImports, ArrayRef optionalModuleImports, - ArrayRef linkLibraries, StringRef headerImport, - StringRef definingModuleInterface, bool isFramework, - bool isStatic, StringRef moduleCacheKey, StringRef userModuleVer) { + ArrayRef linkLibraries, + ArrayRef serializedSearchPaths, + StringRef headerImport, StringRef definingModuleInterface, + bool isFramework, bool isStatic, StringRef moduleCacheKey, + StringRef userModuleVer) { return ModuleDependencyInfo( std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath, moduleImports, - optionalModuleImports, linkLibraries, headerImport, - definingModuleInterface,isFramework, isStatic, moduleCacheKey, - userModuleVer)); + optionalModuleImports, linkLibraries, serializedSearchPaths, + headerImport, definingModuleInterface,isFramework, isStatic, + moduleCacheKey, userModuleVer)); } /// Describe the main Swift module. diff --git a/include/swift/DependencyScan/ModuleDependencyScanner.h b/include/swift/DependencyScan/ModuleDependencyScanner.h index a090940930dda..46a854db25402 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -190,8 +190,24 @@ class ModuleDependencyScanner { template auto withDependencyScanningWorker(Function &&F, Args &&...ArgList); + /// Use the scanner's ASTContext to construct an `Identifier` + /// for a given module name. Identifier getModuleImportIdentifier(StringRef moduleName); + /// Diagnose scanner failure and attempt to reconstruct the dependency + /// path from the main module to the missing dependency. + void diagnoseScannerFailure(const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, + std::optional dependencyOf); + + /// Assuming the \c `moduleImport` failed to resolve, + /// iterate over all binary Swift module dependencies with serialized + /// search paths and attempt to diagnose if the failed-to-resolve module + /// can be found on any of them. + void attemptToFindResolvingSerializedSearchPath( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, const SourceLoc &importLoc); + private: const CompilerInvocation &ScanCompilerInvocation; ASTContext &ScanASTContext; diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index 8aec122f6307e..e4f95b63969c1 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -690,6 +690,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( auto moduleDep = ModuleDependencyInfo::forSwiftBinaryModule( *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath, importStatements, optionalImportStatements, linkLibraries, + {}, // TODO: serialized search path serialization *headerImport, *definingInterfacePath, isFramework, isStatic, *moduleCacheKey, *userModuleVersion); diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 9c5823d49c0b5..066abc8c99964 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -101,78 +101,6 @@ findPathToDependency(ModuleDependencyID dependency, return dependencyPath; } -// Diagnose scanner failure and attempt to reconstruct the dependency -// path from the main module to the missing dependency. -static void -diagnoseScannerFailure(const ScannerImportStatementInfo &moduleImport, - DiagnosticEngine &Diags, - const ModuleDependenciesCache &cache, - std::optional dependencyOf) { - SourceLoc importLoc = SourceLoc(); - if (!moduleImport.importLocations.empty()) { - auto locInfo = moduleImport.importLocations[0]; - importLoc = Diags.SourceMgr.getLocFromExternalSource(locInfo.bufferIdentifier, - locInfo.lineNumber, - locInfo.columnNumber); - } - - Diags.diagnose(importLoc, diag::dependency_scan_module_not_found, - moduleImport.importIdentifier); - if (dependencyOf.has_value()) { - auto path = findPathToDependency(dependencyOf.value(), cache); - // We may fail to construct a path in some cases, such as a Swift overlay of - // a Clang module dependnecy. - if (path.empty()) - path = {dependencyOf.value()}; - - for (auto it = path.rbegin(), end = path.rend(); it != end; ++it) { - const auto &entry = *it; - auto optionalEntryNode = cache.findDependency(entry); - assert(optionalEntryNode.has_value()); - auto entryNode = optionalEntryNode.value(); - std::string moduleFilePath = ""; - bool isClang = false; - switch (entryNode->getKind()) { - case swift::ModuleDependencyKind::SwiftSource: - Diags.diagnose(importLoc, diag::dependency_as_imported_by_main_module, - entry.ModuleName); - continue; - case swift::ModuleDependencyKind::SwiftInterface: - moduleFilePath = - entryNode->getAsSwiftInterfaceModule()->swiftInterfaceFile; - break; - case swift::ModuleDependencyKind::SwiftBinary: - moduleFilePath = - entryNode->getAsSwiftBinaryModule()->compiledModulePath; - break; - case swift::ModuleDependencyKind::SwiftPlaceholder: - moduleFilePath = - entryNode->getAsPlaceholderDependencyModule()->compiledModulePath; - break; - case swift::ModuleDependencyKind::Clang: - moduleFilePath = entryNode->getAsClangModule()->moduleMapFile; - isClang = true; - break; - default: - llvm_unreachable("Unexpected dependency kind"); - } - - Diags.diagnose(importLoc, diag::dependency_as_imported_by, - entry.ModuleName, moduleFilePath, isClang); - } - } - - if (moduleImport.importLocations.size() > 1) { - for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) { - auto locInfo = moduleImport.importLocations[i]; - auto importLoc = Diags.SourceMgr.getLocFromExternalSource(locInfo.bufferIdentifier, - locInfo.lineNumber, - locInfo.columnNumber); - Diags.diagnose(importLoc, diag::unresolved_import_location); - } - } -} - static bool isSwiftDependencyKind(ModuleDependencyKind Kind) { return Kind == ModuleDependencyKind::SwiftInterface || Kind == ModuleDependencyKind::SwiftSource || @@ -545,7 +473,7 @@ ModuleDependencyScanner::ModuleDependencyScanner( /// Find all of the imported Clang modules starting with the given module name. static void findAllImportedClangModules(StringRef moduleName, - ModuleDependenciesCache &cache, + const ModuleDependenciesCache &cache, std::vector &allModules, llvm::StringSet<> &knownModules) { if (!knownModules.insert(moduleName).second) @@ -562,6 +490,16 @@ static void findAllImportedClangModules(StringRef moduleName, knownModules); } +static std::set +collectBinarySwiftDeps(const ModuleDependenciesCache &cache) { + std::set binarySwiftModuleDepIDs; + auto binaryDepsMap = cache.getDependenciesMap(ModuleDependencyKind::SwiftBinary); + for (const auto &binaryDepName : binaryDepsMap.keys()) + binarySwiftModuleDepIDs.insert(ModuleDependencyID{binaryDepName.str(), + ModuleDependencyKind::SwiftBinary}); + return binarySwiftModuleDepIDs; +} + llvm::ErrorOr ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { // Main module file name. @@ -1154,7 +1092,7 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( if (optionalCachedModuleInfo.has_value()) importedClangDependencies.insert(unresolvedModuleID); else - diagnoseScannerFailure(unresolvedImport, Diagnostics, cache, moduleID); + diagnoseScannerFailure(unresolvedImport, cache, moduleID); } if (!importedClangDependencies.empty()) @@ -1706,3 +1644,171 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining( return llvm::Error::success(); } + +void ModuleDependencyScanner::diagnoseScannerFailure( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, + std::optional dependencyOf) { + SourceLoc importLoc = SourceLoc(); + if (!moduleImport.importLocations.empty()) { + auto locInfo = moduleImport.importLocations[0]; + importLoc = Diagnostics.SourceMgr.getLocFromExternalSource( + locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber); + } + + Diagnostics.diagnose(importLoc, diag::dependency_scan_module_not_found, + moduleImport.importIdentifier); + if (dependencyOf.has_value()) { + auto path = findPathToDependency(dependencyOf.value(), cache); + // We may fail to construct a path in some cases, such as a Swift overlay of + // a Clang module dependnecy. + if (path.empty()) + path = {dependencyOf.value()}; + + for (auto it = path.rbegin(), end = path.rend(); it != end; ++it) { + const auto &entry = *it; + auto optionalEntryNode = cache.findDependency(entry); + assert(optionalEntryNode.has_value()); + auto entryNode = optionalEntryNode.value(); + std::string moduleFilePath = ""; + bool isClang = false; + switch (entryNode->getKind()) { + case swift::ModuleDependencyKind::SwiftSource: + Diagnostics.diagnose(importLoc, + diag::dependency_as_imported_by_main_module, + entry.ModuleName); + continue; + case swift::ModuleDependencyKind::SwiftInterface: + moduleFilePath = + entryNode->getAsSwiftInterfaceModule()->swiftInterfaceFile; + break; + case swift::ModuleDependencyKind::SwiftBinary: + moduleFilePath = + entryNode->getAsSwiftBinaryModule()->compiledModulePath; + break; + case swift::ModuleDependencyKind::SwiftPlaceholder: + moduleFilePath = + entryNode->getAsPlaceholderDependencyModule()->compiledModulePath; + break; + case swift::ModuleDependencyKind::Clang: + moduleFilePath = entryNode->getAsClangModule()->moduleMapFile; + isClang = true; + break; + default: + llvm_unreachable("Unexpected dependency kind"); + } + + Diagnostics.diagnose(importLoc, diag::dependency_as_imported_by, + entry.ModuleName, moduleFilePath, isClang); + } + } + + if (moduleImport.importLocations.size() > 1) { + for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) { + auto locInfo = moduleImport.importLocations[i]; + auto importLoc = Diagnostics.SourceMgr.getLocFromExternalSource( + locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber); + Diagnostics.diagnose(importLoc, diag::unresolved_import_location); + } + } + + attemptToFindResolvingSerializedSearchPath(moduleImport, cache, importLoc); +} + +static std::string getModuleDefiningPath(const ModuleDependencyInfo &info) { + std::string path = ""; + switch (info.getKind()) { + case swift::ModuleDependencyKind::SwiftInterface: + path = info.getAsSwiftInterfaceModule()->swiftInterfaceFile; + break; + case swift::ModuleDependencyKind::SwiftBinary: + path = info.getAsSwiftBinaryModule()->compiledModulePath; + break; + case swift::ModuleDependencyKind::SwiftPlaceholder: + path = info.getAsPlaceholderDependencyModule()->compiledModulePath; + break; + case swift::ModuleDependencyKind::Clang: + path = info.getAsClangModule()->moduleMapFile; + break; + case swift::ModuleDependencyKind::SwiftSource: + default: + llvm_unreachable("Unexpected dependency kind"); + } + + // Relative to the `module.modulemap` or `.swiftinterface` or `.swiftmodule`, + // the defininig path is the parent directory of the file. +<<<<<<< Updated upstream + path = llvm::sys::path::parent_path(path); + + // If the defining path is the top-level `.swiftmodule` directory, + // take one more step up. + if (llvm::sys::path::extension(path) == ".swiftmodule") + path = llvm::sys::path::parent_path(path); + + // If the defining path is under `.framework/Modules/` directory, + // return the parent path containing the framework. + if (llvm::sys::path::filename(path) == "Modules" && llvm::sys::path::extension(llvm::sys::path::parent_path(path)) == ".framework") + path = llvm::sys::path::parent_path(llvm::sys::path::parent_path(path)); + + return path; +======= + return llvm::sys::path::parent_path(path).str(); +>>>>>>> Stashed changes +} + +void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, const SourceLoc &importLoc) { + std::set binarySwiftModuleDepIDs = + collectBinarySwiftDeps(cache); + + for (const auto &binaryDepID : binarySwiftModuleDepIDs) { + auto binaryModInfo = + cache.findKnownDependency(binaryDepID).getAsSwiftBinaryModule(); + assert(binaryModInfo); + if (binaryModInfo->serializedSearchPaths.empty()) + continue; + + // Note: this will permanently mutate this worker with additional search + // paths. That's fine because we are diagnosing a scan failure here, but + // worth being aware of. + withDependencyScanningWorker( + [&binaryModInfo, &moduleImport, &cache, &binaryDepID, &importLoc, + this](ModuleDependencyScanningWorker *ScanningWorker) { + ModuleDependencyVector result; + for (const auto &sp : binaryModInfo->serializedSearchPaths) + ScanningWorker->workerASTContext->addSearchPath( + sp.Path, sp.IsFramework, sp.IsSystem); + + result = ScanningWorker->scanFilesystemForSwiftModuleDependency( + getModuleImportIdentifier(moduleImport.importIdentifier), + cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), + cache.getScanService().getPrefixMapper()); + if (!result.empty()) { + Diagnostics.diagnose( + importLoc, diag::inherited_search_path_resolves_module, + moduleImport.importIdentifier, binaryDepID.ModuleName, + getModuleDefiningPath(result[0].second)); + } + + result = ScanningWorker->scanFilesystemForClangModuleDependency( + getModuleImportIdentifier(moduleImport.importIdentifier), + cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), {}, + cache.getScanService().getPrefixMapper()); +<<<<<<< Updated upstream + if (!result.empty()) { + Diagnostics.diagnose( + importLoc, diag::inherited_search_path_resolves_module, + moduleImport.importIdentifier, binaryDepID.ModuleName, + getModuleDefiningPath(result[0].second)); + } + return result; +======= + if (!result.empty()) + return std::make_pair(binaryDepID, + getModuleDefiningPath(result[0].second)); + return std::nullopt; +>>>>>>> Stashed changes + }); + } +} diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index 278561f859e2d..f63578c5b6381 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -659,6 +659,10 @@ class ModuleFileSharedCore { return MacroModuleNames; } + ArrayRef getSearchPaths() const { + return SearchPaths; + } + /// Get embedded bridging header. std::string getEmbeddedHeader() const { // Don't include the '\0' in the end. diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index d56fe1b4fa852..409cc50b2ac95 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -638,11 +638,15 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework, loadedModuleFile->resolveModuleDefiningFilePath(Ctx.SearchPathOpts.getSDKPath()); std::string userModuleVer = loadedModuleFile->getUserModuleVersion().getAsString(); + std::vector serializedSearchPaths; + llvm::copy(loadedModuleFile->getSearchPaths(), std::back_inserter(serializedSearchPaths)); + // Map the set of dependencies over to the "module dependencies". auto dependencies = ModuleDependencyInfo::forSwiftBinaryModule( modulePath.str(), moduleDocPath, sourceInfoPath, moduleImports, - optionalModuleImports, linkLibraries, importedHeader, - definingModulePath, isFramework, loadedModuleFile->isStaticLibrary(), + optionalModuleImports, linkLibraries, serializedSearchPaths, + importedHeader, definingModulePath, isFramework, + loadedModuleFile->isStaticLibrary(), /*module-cache-key*/ "", userModuleVer); for (auto ¯o : loadedModuleFile->getExternalMacros()) { diff --git a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift new file mode 100644 index 0000000000000..db990970ae7ba --- /dev/null +++ b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift @@ -0,0 +1,34 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) +// RUN: %empty-directory(%t/deps) +// RUN: %empty-directory(%t/moreDeps) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/deps/B.swiftmodule -module-cache-path %t/module-cache %t/B.swift -module-name B -I %t/moreDeps + +// RUN: %target-swift-frontend -scan-dependencies -o %t/deps.json %t/client.swift -I %t/deps -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import &> %t/output.txt +// RUN: cat %t/output.txt | %FileCheck %s + +// CHECK: error: Unable to find module dependency: 'C' +// CHECK: note: a dependency of Swift module 'B': '{{.*}}B.swiftmodule' +// CHECK: note: a dependency of main module 'deps' +// CHECK: note: 'C' can be found on search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. + +//--- moreDeps/C.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name C -enable-library-evolution +public struct structC {} + +//--- deps/A.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name A -enable-library-evolution +public func funcA() {} + +//--- B.swift +public import C +public func funcB(_ input: structC) {} + +//--- client.swift +import A +import B + From e67be5ad7a4bf8eeae9bfc8a99cffca190f215c5 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 3 Jun 2025 10:10:42 -0700 Subject: [PATCH 2/3] [Dependency Scanning] Serialized Swift binary module serialized search paths --- include/swift/AST/DiagnosticsSema.def | 2 +- .../SerializedModuleDependencyCacheFormat.h | 17 ++- .../ModuleDependencyCacheSerialization.cpp | 127 +++++++++++++++++- ...ing-module-found-in-serialized-paths.swift | 44 ++++++ ...ing-module-found-in-serialized-paths.swift | 2 +- 5 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index af11f38ca0516..0a2b83f4489d1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2339,7 +2339,7 @@ NOTE(dependency_as_imported_by_main_module,none, NOTE(dependency_as_imported_by, none, "a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool)) NOTE(inherited_search_path_resolves_module,none, - "'%0' can be found on search path used to build module '%1': '%2'. " + "'%0' can be found on a search path used to build module '%1': '%2'. " "These search paths are not inherited by the current compilation.", (StringRef, StringRef, StringRef)) ERROR(clang_dependency_scan_error, none, "Clang dependency scanner failure: %0", (StringRef)) ERROR(clang_header_dependency_scan_error, none, "Bridging header dependency scan failure: %0", (StringRef)) diff --git a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h index b12f8a28ebaa8..8a5c4d074cd90 100644 --- a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h +++ b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h @@ -41,7 +41,7 @@ using llvm::BCVBR; const unsigned char MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE[] = {'I', 'M', 'D','C'}; const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 9; /// Increment this on every change. -const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 2; +const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 3; /// Various identifiers in this format will rely on having their strings mapped /// using this ID. @@ -78,6 +78,7 @@ using ModuleCacheKeyIDField = IdentifierIDField; using ImportArrayIDField = IdentifierIDField; using LinkLibrariesArrayIDField = IdentifierIDField; using MacroDependenciesArrayIDField = IdentifierIDField; +using SearchPathArrayIDField = IdentifierIDField; using FlagIDArrayIDField = IdentifierIDField; using DependencyIDArrayIDField = IdentifierIDField; using SourceLocationIDArrayIDField = IdentifierIDField; @@ -101,6 +102,8 @@ enum { LINK_LIBRARY_ARRAY_NODE, MACRO_DEPENDENCY_NODE, MACRO_DEPENDENCY_ARRAY_NODE, + SEARCH_PATH_NODE, + SEARCH_PATH_ARRAY_NODE, IMPORT_STATEMENT_NODE, IMPORT_STATEMENT_ARRAY_NODE, OPTIONAL_IMPORT_STATEMENT_ARRAY_NODE, @@ -169,6 +172,17 @@ using MacroDependencyLayout = using MacroDependencyArrayLayout = BCRecordLayout; +// A record for a serialized search pathof a given dependency +// node (Swift binary module dependency only). +using SearchPathLayout = + BCRecordLayout; +using SearchPathArrayLayout = + BCRecordLayout; + // A record capturing information about a given 'import' statement // captured in a dependency node, including its source location. using ImportStatementLayout = @@ -248,6 +262,7 @@ using SwiftBinaryModuleDetailsLayout = FileIDField, // definingInterfacePath IdentifierIDField, // headerModuleDependencies FileIDArrayIDField, // headerSourceFiles + SearchPathArrayIDField, // serializedSearchPaths IsFrameworkField, // isFramework IsStaticField, // isStatic IdentifierIDField, // moduleCacheKey diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index e4f95b63969c1..1ca3d8e444ad2 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -45,9 +45,11 @@ class ModuleDependenciesCacheDeserializer { std::vector LinkLibraries; std::vector> ArraysOfLinkLibraryIDs; std::vector> MacroDependencies; + std::vector SearchPaths; std::vector> ArraysOfMacroDependenciesIDs; std::vector ImportStatements; std::vector> ArraysOfImportStatementIDs; + std::vector> ArraysOfSearchPathIDs; std::vector> ArraysOfOptionalImportStatementIDs; llvm::BitstreamCursor Cursor; @@ -66,6 +68,8 @@ class ModuleDependenciesCacheDeserializer { std::optional> getLinkLibraryArray(unsigned n); std::optional>> getMacroDependenciesArray(unsigned n); + std::optional> + getSearchPathArray(unsigned n); std::optional> getImportStatementInfoArray(unsigned n); std::optional> @@ -389,6 +393,24 @@ bool ModuleDependenciesCacheDeserializer::readGraph( break; } + case SEARCH_PATH_NODE: { + unsigned pathStrID; + bool isFramework, isSystem; + SearchPathLayout::readRecord(Scratch, pathStrID, isFramework, isSystem); + auto pathStr = getIdentifier(pathStrID); + if (!pathStr) + llvm::report_fatal_error("Bad search path: no path string"); + SearchPaths.push_back({*pathStr, isFramework, isSystem}); + break; + } + + case SEARCH_PATH_ARRAY_NODE: { + ArrayRef identifierIDs; + SearchPathArrayLayout::readRecord(Scratch, identifierIDs); + ArraysOfSearchPathIDs.push_back(identifierIDs.vec()); + break; + } + case IMPORT_STATEMENT_NODE: { unsigned importIdentifierID, bufferIdentifierID; unsigned lineNumber, columnNumber; @@ -653,14 +675,15 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error( "Unexpected SWIFT_BINARY_MODULE_DETAILS_NODE record"); unsigned compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID, - headerImportID, definingInterfacePathID, + headerImportID, definingInterfacePathID, searchPathArrayID, headerModuleDependenciesArrayID, headerImportsSourceFilesArrayID, isFramework, isStatic, moduleCacheKeyID, userModuleVersionID; SwiftBinaryModuleDetailsLayout::readRecord( Scratch, compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID, headerImportID, definingInterfacePathID, headerModuleDependenciesArrayID, headerImportsSourceFilesArrayID, - isFramework, isStatic, moduleCacheKeyID, userModuleVersionID); + searchPathArrayID, isFramework, isStatic, moduleCacheKeyID, + userModuleVersionID); auto compiledModulePath = getIdentifier(compiledModulePathID); if (!compiledModulePath) @@ -685,14 +708,17 @@ bool ModuleDependenciesCacheDeserializer::readGraph( if (!definingInterfacePath) llvm::report_fatal_error( "Bad binary direct dependencies: no defining interface path"); + auto searchPaths = getSearchPathArray(searchPathArrayID); + if (!searchPaths) + llvm::report_fatal_error( + "Bad binary direct dependencies: no serialized search paths"); // Form the dependencies storage object auto moduleDep = ModuleDependencyInfo::forSwiftBinaryModule( *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath, importStatements, optionalImportStatements, linkLibraries, - {}, // TODO: serialized search path serialization - *headerImport, *definingInterfacePath, isFramework, isStatic, - *moduleCacheKey, *userModuleVersion); + *searchPaths, *headerImport, *definingInterfacePath, isFramework, + isStatic, *moduleCacheKey, *userModuleVersion); addCommonDependencyInfo(moduleDep); addSwiftCommonDependencyInfo(moduleDep); @@ -909,6 +935,25 @@ ModuleDependenciesCacheDeserializer::getMacroDependenciesArray(unsigned n) { return result; } +std::optional> +ModuleDependenciesCacheDeserializer::getSearchPathArray(unsigned n) { + if (n == 0) + return std::vector(); + + --n; + if (n >= ArraysOfSearchPathIDs.size()) + return std::nullopt; + + auto &llIDs = ArraysOfSearchPathIDs[n]; + + auto IDtoLLMap = [this](unsigned index) { return SearchPaths[index]; }; + std::vector result; + result.reserve(llIDs.size()); + std::transform(llIDs.begin(), llIDs.end(), std::back_inserter(result), + IDtoLLMap); + return result; +} + std::optional> ModuleDependenciesCacheDeserializer::getImportStatementInfoArray(unsigned n) { if (n == 0) @@ -1084,6 +1129,7 @@ class ModuleDependenciesCacheSerializer { std::unordered_map LinkLibraryArrayIDsMap; std::unordered_map MacroDependenciesArrayIDsMap; + std::unordered_map SearchPathArrayIDsMap; std::unordered_map ImportInfosArrayIDsMap; std::unordered_map OptionalImportInfosArrayIDsMap; @@ -1110,6 +1156,7 @@ class ModuleDependenciesCacheSerializer { ModuleIdentifierArrayKind arrayKind) const; unsigned getLinkLibrariesArrayID(ModuleDependencyID moduleID) const; unsigned getMacroDependenciesArrayID(ModuleDependencyID moduleID) const; + unsigned getSearchPathArrayID(ModuleDependencyID moduleID) const; unsigned getImportStatementsArrayID(ModuleDependencyID moduleID) const; unsigned getOptionalImportStatementsArrayID(ModuleDependencyID moduleID) const; @@ -1146,6 +1193,10 @@ class ModuleDependenciesCacheSerializer { unsigned writeMacroDependencies(const ModuleDependencyInfo &dependencyInfo); void writeMacroDependenciesArray(unsigned startIndex, unsigned count); + void writeSearchPaths(const ModuleDependenciesCache &cache); + unsigned writeSearchPaths(const SwiftBinaryModuleDependencyStorage &dependencyInfo); + void writeSearchPathsArray(unsigned startIndex, unsigned count); + void writeImportStatementInfos(const ModuleDependenciesCache &cache); unsigned writeImportStatementInfos(const ModuleDependencyInfo &dependencyInfo, bool optional); @@ -1206,6 +1257,8 @@ void ModuleDependenciesCacheSerializer::writeBlockInfoBlock() { BLOCK_RECORD(graph_block, LINK_LIBRARY_ARRAY_NODE); BLOCK_RECORD(graph_block, MACRO_DEPENDENCY_NODE); BLOCK_RECORD(graph_block, MACRO_DEPENDENCY_ARRAY_NODE); + BLOCK_RECORD(graph_block, SEARCH_PATH_NODE); + BLOCK_RECORD(graph_block, SEARCH_PATH_ARRAY_NODE); BLOCK_RECORD(graph_block, IMPORT_STATEMENT_NODE); BLOCK_RECORD(graph_block, IMPORT_STATEMENT_ARRAY_NODE); BLOCK_RECORD(graph_block, OPTIONAL_IMPORT_STATEMENT_ARRAY_NODE); @@ -1367,6 +1420,52 @@ void ModuleDependenciesCacheSerializer::writeMacroDependenciesArray( Out, ScratchRecord, AbbrCodes[MacroDependencyArrayLayout::Code], vec); } +void ModuleDependenciesCacheSerializer::writeSearchPaths(const ModuleDependenciesCache &cache) { + unsigned lastSPIndex = 0; + std::map> + moduleSearchPathArrayMap; + + auto modMap = cache.getDependenciesMap(ModuleDependencyKind::SwiftBinary); + for (const auto &entry : modMap) { + ModuleDependencyID moduleID = {entry.getKey().str(), ModuleDependencyKind::SwiftBinary}; + auto optionalDependencyInfo = cache.findDependency(moduleID); + assert(optionalDependencyInfo && "Expected dependency info."); + auto dependencyInfo = *optionalDependencyInfo; + unsigned numSPs = writeSearchPaths(*dependencyInfo->getAsSwiftBinaryModule()); + moduleSearchPathArrayMap.insert({moduleID, std::make_pair(lastSPIndex, numSPs)}); + lastSPIndex += numSPs; + } + + unsigned lastSPArrayIndex = 1; + for (const auto &entry : modMap) { + ModuleDependencyID moduleID = {entry.getKey().str(), ModuleDependencyKind::SwiftBinary}; + auto entries = moduleSearchPathArrayMap.at(moduleID); + if (entries.second == 0) + continue; + writeSearchPathsArray(entries.first, entries.second); + SearchPathArrayIDsMap.insert({moduleID, lastSPArrayIndex++}); + } +} +unsigned ModuleDependenciesCacheSerializer::writeSearchPaths(const SwiftBinaryModuleDependencyStorage &dependencyInfo) { + using namespace graph_block; + for (const auto &searchPath : dependencyInfo.serializedSearchPaths) { + SearchPathLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[SearchPathLayout::Code], + getIdentifier(searchPath.Path), + searchPath.IsFramework, + searchPath.IsSystem); + } + return dependencyInfo.serializedSearchPaths.size(); +} + +void ModuleDependenciesCacheSerializer::writeSearchPathsArray(unsigned startIndex, unsigned count) { + using namespace graph_block; + std::vector vec(count); + std::iota(vec.begin(), vec.end(), startIndex); + SearchPathArrayLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[SearchPathArrayLayout::Code], vec); +} + void ModuleDependenciesCacheSerializer::writeImportStatementInfos( const ModuleDependenciesCache &cache) { unsigned lastImportInfoIndex = 0; @@ -1559,10 +1658,10 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo( getIdentifierArrayID( moduleID, ModuleIdentifierArrayKind::HeaderInputDependencySourceFiles), + getSearchPathArrayID(moduleID), swiftBinDeps->isFramework, swiftBinDeps->isStatic, getIdentifier(swiftBinDeps->moduleCacheKey), getIdentifier(swiftBinDeps->userModuleVersion)); - break; } case swift::ModuleDependencyKind::SwiftPlaceholder: { @@ -1699,6 +1798,15 @@ unsigned ModuleDependenciesCacheSerializer::getMacroDependenciesArrayID( return iter->second; } +unsigned ModuleDependenciesCacheSerializer::getSearchPathArrayID( + ModuleDependencyID moduleID) const { + auto iter = SearchPathArrayIDsMap.find(moduleID); + if (iter == SearchPathArrayIDsMap.end()) + return 0; + + return iter->second; +} + unsigned ModuleDependenciesCacheSerializer::getImportStatementsArrayID( ModuleDependencyID moduleID) const { auto iter = ImportInfosArrayIDsMap.find(moduleID); @@ -1820,6 +1928,8 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays( moduleID, ModuleIdentifierArrayKind::HeaderInputDependencySourceFiles, swiftBinDeps->headerSourceFiles); + llvm::for_each(swiftBinDeps->serializedSearchPaths, + [this](auto &sp) { addIdentifier(sp.Path); }); break; } case swift::ModuleDependencyKind::SwiftPlaceholder: { @@ -1899,6 +2009,8 @@ void ModuleDependenciesCacheSerializer::writeInterModuleDependenciesCache( registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); @@ -1933,6 +2045,9 @@ void ModuleDependenciesCacheSerializer::writeInterModuleDependenciesCache( // Write all the arrays of macro dependency infos for this graph writeMacroDependencies(cache); + // Write all the arrays of binary-module-serialized search paths + writeSearchPaths(cache); + // Write the core graph for (auto kind = ModuleDependencyKind::FirstKind; kind != ModuleDependencyKind::LastKind; ++kind) { diff --git a/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift b/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift new file mode 100644 index 0000000000000..3138e14d54b41 --- /dev/null +++ b/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) +// RUN: %empty-directory(%t/deps) +// RUN: %empty-directory(%t/moreDeps) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/deps/B.swiftmodule -module-cache-path %t/module-cache %t/B.swift -module-name B -I %t/moreDeps + +// Put the dependency module into a location discoverable by the first scan which will succeed and serialize scanner state +// RUN: cp %t/moreDeps/C.swiftinterface %t/deps/C.swiftinterface + +// RUN: %target-swift-frontend -scan-dependencies -o %t/deps.json %t/client.swift -I %t/deps -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -Rdependency-scan-cache -serialize-dependency-scan-cache -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache &> %t/initial_output.txt + +// Remove the 'C' dependency module into a location not discoverable by the second scan in order to trigger a failure and use serialized scanner state +// to emit the diagnostic +// RUN: rm %t/deps/C.swiftinterface + +// RUN: %target-swift-frontend -scan-dependencies -o %t/deps.json %t/client.swift -I %t/deps -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -Rdependency-scan-cache -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -validate-prior-dependency-scan-cache &> %t/output.txt +// RUN: cat %t/output.txt | %FileCheck %s + +// CHECK: remark: Incremental module scan: Re-using serialized module scanning dependency cache from: '{{.*}}cache.moddepcache'. +// CHECK: remark: Incremental module scan: Dependency info for module 'C' invalidated due to a modified input since last scan: '{{.*}}deps{{/|\\}}C.swiftinterface'. +// CHECK: remark: Incremental module scan: Dependency info for module 'deps' invalidated due to an out-of-date dependency. +// CHECK: error: Unable to find module dependency: 'C' +// CHECK: note: a dependency of main module 'deps' +// CHECK: note: 'C' can be found on a search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. + +//--- moreDeps/C.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name C -enable-library-evolution +public struct structC {} + +//--- deps/A.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name A -enable-library-evolution +public func funcA() {} + +//--- B.swift +public func funcB() {} + +//--- client.swift +import A +import B +import C diff --git a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift index db990970ae7ba..b2160f434e59a 100644 --- a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift +++ b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift @@ -12,7 +12,7 @@ // CHECK: error: Unable to find module dependency: 'C' // CHECK: note: a dependency of Swift module 'B': '{{.*}}B.swiftmodule' // CHECK: note: a dependency of main module 'deps' -// CHECK: note: 'C' can be found on search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. +// CHECK: note: 'C' can be found on a search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. //--- moreDeps/C.swiftinterface // swift-interface-format-version: 1.0 From e37071bcd48f9b2a2ac6bc7581cac138ff7b94cd Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 3 Jun 2025 15:34:12 -0700 Subject: [PATCH 3/3] Add a new diagnostic group and documentation for the module-not-found failure which specifies that a missing module dependency can be found on a search path serialized in another Swift binary module dependency. --- include/swift/AST/DiagnosticGroups.def | 1 + include/swift/AST/DiagnosticsSema.def | 7 +- .../DependencyScan/ModuleDependencyScanner.h | 6 +- .../ModuleDependencyScanner.cpp | 77 +++++++++---------- ...ing-module-found-in-serialized-paths.swift | 4 +- ...ing-module-found-in-serialized-paths.swift | 4 +- .../missing-module-on-known-paths.md | 18 +++++ 7 files changed, 69 insertions(+), 48 deletions(-) create mode 100644 userdocs/diagnostics/missing-module-on-known-paths.md diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def index f5eab826de0f8..9a84cef6fccc0 100644 --- a/include/swift/AST/DiagnosticGroups.def +++ b/include/swift/AST/DiagnosticGroups.def @@ -50,6 +50,7 @@ GROUP(ExistentialAny, "existential-any") GROUP(ExistentialMemberAccess, "existential-member-access-limitations") GROUP(IsolatedConformances, "isolated-conformances") GROUP(MemberImportVisibility, "member-import-visibility") +GROUP(MissingModuleOnKnownPaths, "missing-module-on-known-paths") GROUP(MultipleInheritance, "multiple-inheritance") GROUP(MutableGlobalVariable, "mutable-global-variable") GROUP(NominalTypes, "nominal-types") diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0a2b83f4489d1..a44f3090976b2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2332,6 +2332,8 @@ ERROR(alignment_not_power_of_two,none, // Dependency Scanning ERROR(dependency_scan_module_not_found, none, "Unable to find module dependency: '%0'", (StringRef)) +GROUPED_ERROR(dependency_scan_module_not_found_on_specified_search_paths, MissingModuleOnKnownPaths, none, + "Compilation search paths unable to resolve module dependency: '%0'", (StringRef)) NOTE(unresolved_import_location,none, "also imported here", ()) NOTE(dependency_as_imported_by_main_module,none, @@ -2339,8 +2341,9 @@ NOTE(dependency_as_imported_by_main_module,none, NOTE(dependency_as_imported_by, none, "a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool)) NOTE(inherited_search_path_resolves_module,none, - "'%0' can be found on a search path used to build module '%1': '%2'. " - "These search paths are not inherited by the current compilation.", (StringRef, StringRef, StringRef)) + "'%0' can be found using a search path that was specified when building module '%1' ('%2'). " + "This search path was not explicitly specified on the current compilation.", (StringRef, StringRef, StringRef)) + ERROR(clang_dependency_scan_error, none, "Clang dependency scanner failure: %0", (StringRef)) ERROR(clang_header_dependency_scan_error, none, "Bridging header dependency scan failure: %0", (StringRef)) diff --git a/include/swift/DependencyScan/ModuleDependencyScanner.h b/include/swift/DependencyScan/ModuleDependencyScanner.h index 46a854db25402..58b91d6253d19 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -203,8 +203,10 @@ class ModuleDependencyScanner { /// Assuming the \c `moduleImport` failed to resolve, /// iterate over all binary Swift module dependencies with serialized /// search paths and attempt to diagnose if the failed-to-resolve module - /// can be found on any of them. - void attemptToFindResolvingSerializedSearchPath( + /// can be found on any of them. Returns the path containing + /// the module, if one is found. + std::optional> + attemptToFindResolvingSerializedSearchPath( const ScannerImportStatementInfo &moduleImport, const ModuleDependenciesCache &cache, const SourceLoc &importLoc); diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 066abc8c99964..fadd930d05c2d 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -1656,8 +1656,27 @@ void ModuleDependencyScanner::diagnoseScannerFailure( locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber); } - Diagnostics.diagnose(importLoc, diag::dependency_scan_module_not_found, - moduleImport.importIdentifier); + // Attempt to determine if any of the binary Swift module dependencies contain + // serialized search paths where the missing module may be found. If yes, + // emit a specialized diagnostic letting the user know which search path + // is missing in current compilation. + auto resolvedModuleDefiningPath = attemptToFindResolvingSerializedSearchPath( + moduleImport, cache, importLoc); + if (resolvedModuleDefiningPath) { + Diagnostics.diagnose( + importLoc, + diag::dependency_scan_module_not_found_on_specified_search_paths, + moduleImport.importIdentifier); + Diagnostics.diagnose(importLoc, diag::inherited_search_path_resolves_module, + moduleImport.importIdentifier, + resolvedModuleDefiningPath->first.ModuleName, + resolvedModuleDefiningPath->second); + } else + Diagnostics.diagnose(importLoc, diag::dependency_scan_module_not_found, + moduleImport.importIdentifier); + + // Emit notes for every link in the dependency chain from the root + // module-under-scan to the module whose import failed to resolve. if (dependencyOf.has_value()) { auto path = findPathToDependency(dependencyOf.value(), cache); // We may fail to construct a path in some cases, such as a Swift overlay of @@ -1703,6 +1722,8 @@ void ModuleDependencyScanner::diagnoseScannerFailure( } } + // Emit notes for every other location where the failed-to-resolve + // module is imported. if (moduleImport.importLocations.size() > 1) { for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) { auto locInfo = moduleImport.importLocations[i]; @@ -1711,8 +1732,6 @@ void ModuleDependencyScanner::diagnoseScannerFailure( Diagnostics.diagnose(importLoc, diag::unresolved_import_location); } } - - attemptToFindResolvingSerializedSearchPath(moduleImport, cache, importLoc); } static std::string getModuleDefiningPath(const ModuleDependencyInfo &info) { @@ -1737,31 +1756,17 @@ static std::string getModuleDefiningPath(const ModuleDependencyInfo &info) { // Relative to the `module.modulemap` or `.swiftinterface` or `.swiftmodule`, // the defininig path is the parent directory of the file. -<<<<<<< Updated upstream - path = llvm::sys::path::parent_path(path); - - // If the defining path is the top-level `.swiftmodule` directory, - // take one more step up. - if (llvm::sys::path::extension(path) == ".swiftmodule") - path = llvm::sys::path::parent_path(path); - - // If the defining path is under `.framework/Modules/` directory, - // return the parent path containing the framework. - if (llvm::sys::path::filename(path) == "Modules" && llvm::sys::path::extension(llvm::sys::path::parent_path(path)) == ".framework") - path = llvm::sys::path::parent_path(llvm::sys::path::parent_path(path)); - - return path; -======= return llvm::sys::path::parent_path(path).str(); ->>>>>>> Stashed changes } -void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( +std::optional> +ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( const ScannerImportStatementInfo &moduleImport, const ModuleDependenciesCache &cache, const SourceLoc &importLoc) { std::set binarySwiftModuleDepIDs = collectBinarySwiftDeps(cache); + std::optional> result; for (const auto &binaryDepID : binarySwiftModuleDepIDs) { auto binaryModInfo = cache.findKnownDependency(binaryDepID).getAsSwiftBinaryModule(); @@ -1772,9 +1777,10 @@ void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( // Note: this will permanently mutate this worker with additional search // paths. That's fine because we are diagnosing a scan failure here, but // worth being aware of. - withDependencyScanningWorker( - [&binaryModInfo, &moduleImport, &cache, &binaryDepID, &importLoc, - this](ModuleDependencyScanningWorker *ScanningWorker) { + result = withDependencyScanningWorker( + [&binaryModInfo, &moduleImport, &cache, this, + &binaryDepID](ModuleDependencyScanningWorker *ScanningWorker) + -> std::optional> { ModuleDependencyVector result; for (const auto &sp : binaryModInfo->serializedSearchPaths) ScanningWorker->workerASTContext->addSearchPath( @@ -1784,31 +1790,22 @@ void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( getModuleImportIdentifier(moduleImport.importIdentifier), cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), cache.getScanService().getPrefixMapper()); - if (!result.empty()) { - Diagnostics.diagnose( - importLoc, diag::inherited_search_path_resolves_module, - moduleImport.importIdentifier, binaryDepID.ModuleName, - getModuleDefiningPath(result[0].second)); - } + if (!result.empty()) + return std::make_pair(binaryDepID, + getModuleDefiningPath(result[0].second)); result = ScanningWorker->scanFilesystemForClangModuleDependency( getModuleImportIdentifier(moduleImport.importIdentifier), cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), {}, cache.getScanService().getPrefixMapper()); -<<<<<<< Updated upstream - if (!result.empty()) { - Diagnostics.diagnose( - importLoc, diag::inherited_search_path_resolves_module, - moduleImport.importIdentifier, binaryDepID.ModuleName, - getModuleDefiningPath(result[0].second)); - } - return result; -======= if (!result.empty()) return std::make_pair(binaryDepID, getModuleDefiningPath(result[0].second)); return std::nullopt; ->>>>>>> Stashed changes }); + if (result) + break; } + + return result; } diff --git a/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift b/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift index 3138e14d54b41..8d0e0467c4d07 100644 --- a/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift +++ b/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift @@ -21,9 +21,9 @@ // CHECK: remark: Incremental module scan: Re-using serialized module scanning dependency cache from: '{{.*}}cache.moddepcache'. // CHECK: remark: Incremental module scan: Dependency info for module 'C' invalidated due to a modified input since last scan: '{{.*}}deps{{/|\\}}C.swiftinterface'. // CHECK: remark: Incremental module scan: Dependency info for module 'deps' invalidated due to an out-of-date dependency. -// CHECK: error: Unable to find module dependency: 'C' +// CHECK: error: Compilation search paths unable to resolve module dependency: 'C' +// CHECK: note: 'C' can be found using a search path that was specified when building module 'B' ('{{.*}}moreDeps'). This search path was not explicitly specified on the current compilation. // CHECK: note: a dependency of main module 'deps' -// CHECK: note: 'C' can be found on a search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. //--- moreDeps/C.swiftinterface // swift-interface-format-version: 1.0 diff --git a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift index b2160f434e59a..453fc951ec790 100644 --- a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift +++ b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift @@ -9,10 +9,10 @@ // RUN: %target-swift-frontend -scan-dependencies -o %t/deps.json %t/client.swift -I %t/deps -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import &> %t/output.txt // RUN: cat %t/output.txt | %FileCheck %s -// CHECK: error: Unable to find module dependency: 'C' +// CHECK: error: Compilation search paths unable to resolve module dependency: 'C' +// CHECK: note: 'C' can be found using a search path that was specified when building module 'B' ('{{.*}}moreDeps'). This search path was not explicitly specified on the current compilation. // CHECK: note: a dependency of Swift module 'B': '{{.*}}B.swiftmodule' // CHECK: note: a dependency of main module 'deps' -// CHECK: note: 'C' can be found on a search path used to build module 'B': '{{.*}}moreDeps'. These search paths are not inherited by the current compilation. //--- moreDeps/C.swiftinterface // swift-interface-format-version: 1.0 diff --git a/userdocs/diagnostics/missing-module-on-known-paths.md b/userdocs/diagnostics/missing-module-on-known-paths.md new file mode 100644 index 0000000000000..ed178e0c88f43 --- /dev/null +++ b/userdocs/diagnostics/missing-module-on-known-paths.md @@ -0,0 +1,18 @@ +# Missing Module On Known Path From A Dependency Note (`MissingModuleOnKnownPaths`) + + +This diagnostic group covers notes related to displaying information about a missing module dependency which the compiler is able to locate as present on a search path found in a loaded Swift binary module, but which is not specified to the current compilation. + +As one example of a potential missing module diagnostic, suppose an imported module `Foo` is resolved to a Swift binary module which itself depends on module `Bar` and was built with an additional search path where `Bar` is located, and suppose that the client which imports `Foo` does not specify this search path: + +``` +import Foo +``` + +The Swift compiler would emit a module-not-found error and a note to inform the user of the missing search path containing `Bar` which was found serialized in `Foo`'s binary Swift module: + +``` +error: Compilation search paths unable to resolve module dependency: 'Bar' [#MissingModuleOnKnownPaths] +note: 'Bar' can be found using a search path that was specified when building module 'Foo' (''). This search path was not specified on the current compilation. +``` +Some prior versions of the Swift compiler erroneously inherited search paths from loaded binary Swift modules and used them to resolve other, subsequently-encountered module dependencies. All search paths required to resolve direct and transitive module dependencies must be explicitly specified on the compiler invocation which will encounter these dependencies.