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 8f0d4308f03df..a44f3090976b2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2332,12 +2332,18 @@ 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, "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 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/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..58b91d6253d19 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -190,8 +190,26 @@ 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. Returns the path containing + /// the module, if one is found. + std::optional> + attemptToFindResolvingSerializedSearchPath( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, const SourceLoc &importLoc); + private: const CompilerInvocation &ScanCompilerInvocation; ASTContext &ScanASTContext; 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 8aec122f6307e..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,13 +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, - *headerImport, *definingInterfacePath, isFramework, isStatic, - *moduleCacheKey, *userModuleVersion); + *searchPaths, *headerImport, *definingInterfacePath, isFramework, + isStatic, *moduleCacheKey, *userModuleVersion); addCommonDependencyInfo(moduleDep); addSwiftCommonDependencyInfo(moduleDep); @@ -908,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) @@ -1083,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; @@ -1109,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; @@ -1145,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); @@ -1205,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); @@ -1366,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; @@ -1558,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: { @@ -1698,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); @@ -1819,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: { @@ -1898,6 +2009,8 @@ void ModuleDependenciesCacheSerializer::writeInterModuleDependenciesCache( registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); registerRecordAbbr(); @@ -1932,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/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 9c5823d49c0b5..fadd930d05c2d 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,168 @@ 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); + } + + // 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 + // 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); + } + } + + // 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]; + auto importLoc = Diagnostics.SourceMgr.getLocFromExternalSource( + locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber); + Diagnostics.diagnose(importLoc, diag::unresolved_import_location); + } + } +} + +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. + return llvm::sys::path::parent_path(path).str(); +} + +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(); + 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. + result = withDependencyScanningWorker( + [&binaryModInfo, &moduleImport, &cache, this, + &binaryDepID](ModuleDependencyScanningWorker *ScanningWorker) + -> std::optional> { + 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()) + return std::make_pair(binaryDepID, + getModuleDefiningPath(result[0].second)); + + result = ScanningWorker->scanFilesystemForClangModuleDependency( + getModuleImportIdentifier(moduleImport.importIdentifier), + cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), {}, + cache.getScanService().getPrefixMapper()); + if (!result.empty()) + return std::make_pair(binaryDepID, + getModuleDefiningPath(result[0].second)); + return std::nullopt; + }); + if (result) + break; + } + + return result; +} 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/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..8d0e0467c4d07 --- /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: 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' + +//--- 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 new file mode 100644 index 0000000000000..453fc951ec790 --- /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: 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' + +//--- 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 + 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.