From 8961d8da9aa5c91c6394b5ab7062e7010daf0965 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 24 Jun 2025 09:59:13 -0700 Subject: [PATCH] [Dependency Scanning] Diagnose an error when only finding incompatible Swift binary modules When querying a Swift module, the scanner now also keeps track of all discovered candidate binary modules which are not compatible with current compilation. - If a Swift dependency is successfully resolved to a compatible binary module or a textual interface, a warning is emitted for every incompatible binary Swift module discovered along the way. - If a Swift dependency is not resolved, but incompatible module candidates were found, an error is emitted - while it is likely that the scan would fail downstream, it is also possible that an underlying Clang module dependency (with the same name) is successfuly resolved and the Swift lookup failure is ignored, which is still going to lead to failures most of the time if the client code assumes the presence of the Swift overlay module in this scenario. This change refactors common error reporting by the scanner into a 'ModuleDependencyIssueReporter' class, which also keeps track of all diagnosed failed lookups to avoid repeating diagnostics. --- include/swift/AST/DiagnosticsSema.def | 32 +-- include/swift/AST/ModuleDependencies.h | 4 + .../DependencyScan/ModuleDependencyScanner.h | 64 +++-- include/swift/Serialization/ScanningLoaders.h | 48 +++- .../ModuleDependencyScanner.cpp | 221 ++++++++++++------ lib/Serialization/ScanningLoaders.cpp | 56 ++--- .../bridging_header_error.swift | 2 +- ...ing-module-found-in-serialized-paths.swift | 4 +- .../candidate_binary_module_diagnostics.swift | 22 ++ test/ScanDependencies/clang_scan_error.swift | 2 +- ...ing-module-found-in-serialized-paths.swift | 4 +- test/ScanDependencies/error_path.swift | 4 +- .../error_source_locations.swift | 8 +- .../invalid_binary_module_only.swift | 5 +- .../testable-dependencies.swift | 7 +- 15 files changed, 320 insertions(+), 163 deletions(-) create mode 100644 test/ScanDependencies/candidate_binary_module_diagnostics.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 2fb3d000e0f3f..680d95ba467a5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1290,15 +1290,6 @@ REMARK(module_api_import_aliases,none, "%select{, which reexports definition from %2|}3", (const Decl *, ModuleDecl *, ModuleDecl *, bool)) -REMARK(dependency_scan_skip_module_invalid,none, - "module file '%0' skipped by the dependency scan because it is " - "incompatible with this Swift compiler: %1", (StringRef, StringRef)) -WARNING(skip_module_not_testable,none, - "ignore swiftmodule built without '-enable-testing': %0", (StringRef)) -WARNING(dependency_scan_module_incompatible, none, - "module file '%0' is incompatible with this Swift compiler: %1", - (StringRef, StringRef)) - REMARK(macro_loaded,none, "loaded macro implementation module %0 from " "%select{shared library '%2'|executable '%2'|" @@ -2380,9 +2371,11 @@ ERROR(alignment_not_power_of_two,none, "alignment value must be a power of two", ()) // Dependency Scanning -ERROR(dependency_scan_module_not_found, none, "Unable to find module dependency: '%0'", (StringRef)) +ERROR(dependency_scan_module_not_found, none, "unable to resolve 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)) + "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, @@ -2391,10 +2384,21 @@ 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)) + "This search path was not explicitly specified on the current compilation", (StringRef, StringRef, StringRef)) + +ERROR(dependency_scan_compatible_swift_module_not_found, none, + "unable to resolve Swift module dependency to a compatible module: '%0'", + (StringRef)) +NOTE(dependency_scan_incompatible_module_found,none, + "found incompatible module '%0': %1", (StringRef, StringRef)) + +WARNING(dependency_scan_module_incompatible, none, + "module file '%0' is incompatible with this Swift compiler: %1", + (StringRef, StringRef)) + +ERROR(clang_dependency_scan_error, none, "clang dependency scanning failure: %0", (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)) +ERROR(clang_header_dependency_scan_error, none, "bridging header dependency scanning failure: %0", (StringRef)) // Enum annotations ERROR(indirect_case_without_payload,none, diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 1427845bc760a..dc11457bdc837 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -124,6 +124,10 @@ struct ScannerImportStatementInfo { uint32_t columnNumber; }; + ScannerImportStatementInfo(std::string importIdentifier) + : importIdentifier(importIdentifier), importLocations(), + isExported(false), accessLevel(AccessLevel::Public) {} + ScannerImportStatementInfo(std::string importIdentifier, bool isExported, AccessLevel accessLevel) : importIdentifier(importIdentifier), importLocations(), diff --git a/include/swift/DependencyScan/ModuleDependencyScanner.h b/include/swift/DependencyScan/ModuleDependencyScanner.h index 416fd8f46ed13..6d64b53c68876 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -46,7 +46,7 @@ class ModuleDependencyScanningWorker { &alreadySeenModules); /// Retrieve the module dependencies for the Swift module with the given name. - ModuleDependencyVector scanFilesystemForSwiftModuleDependency( + SwiftModuleScannerQueryResult scanFilesystemForSwiftModuleDependency( Identifier moduleName, bool isTestableImport = false); /// Query dependency information for header dependencies @@ -122,27 +122,67 @@ class SwiftDependencyTracker { SwiftDependencyTracker(std::shared_ptr CAS, llvm::PrefixMapper *Mapper, const CompilerInvocation &CI); - + void startTracking(bool includeCommonDeps = true); void trackFile(const Twine &path); llvm::Expected createTreeFromDependencies(); - + private: llvm::IntrusiveRefCntPtr FS; std::shared_ptr CAS; llvm::PrefixMapper *Mapper; - + struct FileEntry { llvm::cas::ObjectRef FileRef; size_t Size; - + FileEntry(llvm::cas::ObjectRef FileRef, size_t Size) - : FileRef(FileRef), Size(Size) {} + : FileRef(FileRef), Size(Size) {} }; llvm::StringMap CommonFiles; std::map TrackedFiles; }; +class ModuleDependencyIssueReporter { +private: + ModuleDependencyIssueReporter(DiagnosticEngine &Diagnostics) + : Diagnostics(Diagnostics) {} + + /// Diagnose scanner failure and attempt to reconstruct the dependency + /// path from the main module to the missing dependency + void diagnoseModuleNotFoundFailure( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, + std::optional dependencyOf, + std::optional> + resolvingSerializedSearchPath, + std::optional< + std::vector> + foundIncompatibleCandidates = std::nullopt); + + /// Upon query failure, if incompatible binary module + /// candidates were found, emit a failure diagnostic + void diagnoseFailureOnOnlyIncompatibleCandidates( + const ScannerImportStatementInfo &moduleImport, + const std::vector + &candidates, + const ModuleDependenciesCache &cache, + std::optional dependencyOf); + + /// Emit warnings for each discovered binary Swift module + /// which was incompatible with the current compilation + /// when querying \c moduleName + void warnOnIncompatibleCandidates( + StringRef moduleName, + const std::vector + &candidates); + + DiagnosticEngine &Diagnostics; + std::unordered_set ReportedMissing; + // Restrict access to the parent scanner class. + friend class ModuleDependencyScanner; +}; + class ModuleDependencyScanner { public: ModuleDependencyScanner(SwiftDependencyScanningService &ScanningService, @@ -246,7 +286,7 @@ class ModuleDependencyScanner { StringRef mainModuleName, ModuleDependenciesCache &cache, llvm::function_ref action); - /// Performance BridgingHeader Chaining. + /// Perform Bridging Header Chaining. llvm::Error performBridgingHeaderChaining(const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache, @@ -261,12 +301,6 @@ class ModuleDependencyScanner { /// 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 @@ -275,12 +309,12 @@ class ModuleDependencyScanner { std::optional> attemptToFindResolvingSerializedSearchPath( const ScannerImportStatementInfo &moduleImport, - const ModuleDependenciesCache &cache, const SourceLoc &importLoc); + const ModuleDependenciesCache &cache); private: const CompilerInvocation &ScanCompilerInvocation; ASTContext &ScanASTContext; - DiagnosticEngine &Diagnostics; + ModuleDependencyIssueReporter IssueReporter; /// The available pool of workers for filesystem module search unsigned NumThreads; diff --git a/include/swift/Serialization/ScanningLoaders.h b/include/swift/Serialization/ScanningLoaders.h index d37a16a30fd6d..f5f503c14ebd4 100644 --- a/include/swift/Serialization/ScanningLoaders.h +++ b/include/swift/Serialization/ScanningLoaders.h @@ -18,6 +18,28 @@ #include "swift/Serialization/SerializedModuleLoader.h" namespace swift { + +/// Result of looking up a Swift module on the current filesystem +/// search paths. +struct SwiftModuleScannerQueryResult { + struct IncompatibleCandidate { + std::string path; + std::string incompatibilityReason; + }; + + SwiftModuleScannerQueryResult() + : foundDependencyInfo(std::nullopt), incompatibleCandidates() {} + + SwiftModuleScannerQueryResult( + std::optional &&dependencyInfo, + std::vector &&candidates) + : foundDependencyInfo(dependencyInfo), + incompatibleCandidates(candidates) {} + + std::optional foundDependencyInfo; + std::vector incompatibleCandidates; +}; + /// A module "loader" that looks for .swiftinterface and .swiftmodule files /// for the purpose of determining dependencies, but does not attempt to /// load the module files. @@ -59,23 +81,27 @@ class SwiftModuleScanner : public SerializedModuleLoaderBase { /// Swift module compilation commands std::vector swiftModuleClangCC1CommandLineArgs; + /// Constituents of a result of a given Swift module query, + /// reset at the end of every query. + std::optional foundDependencyInfo; + std::vector + incompatibleCandidates; public: - std::optional dependencies; - - SwiftModuleScanner(ASTContext &ctx, ModuleLoadingMode LoadMode, - InterfaceSubContextDelegate &astDelegate, - StringRef moduleOutputPath, StringRef sdkModuleOutputPath, - std::vector swiftModuleClangCC1CommandLineArgs) + SwiftModuleScanner( + ASTContext &ctx, ModuleLoadingMode LoadMode, + InterfaceSubContextDelegate &astDelegate, StringRef moduleOutputPath, + StringRef sdkModuleOutputPath, + std::vector swiftModuleClangCC1CommandLineArgs) : SerializedModuleLoaderBase(ctx, nullptr, LoadMode, /*IgnoreSwiftSourceInfoFile=*/true), - astDelegate(astDelegate), - moduleOutputPath(moduleOutputPath), + astDelegate(astDelegate), moduleOutputPath(moduleOutputPath), sdkModuleOutputPath(sdkModuleOutputPath), - swiftModuleClangCC1CommandLineArgs(swiftModuleClangCC1CommandLineArgs) {} + swiftModuleClangCC1CommandLineArgs(swiftModuleClangCC1CommandLineArgs) { + } /// Perform a filesystem search for a Swift module with a given name - llvm::SmallVector, 1> - lookupSwiftModule(Identifier moduleName, bool isTestableImport); + SwiftModuleScannerQueryResult lookupSwiftModule(Identifier moduleName, + bool isTestableImport); }; } // namespace swift diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 68cf6784152ec..238217c83ca7d 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -297,7 +297,7 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker( swiftModuleClangCC1CommandLineArgs); } -ModuleDependencyVector +SwiftModuleScannerQueryResult ModuleDependencyScanningWorker::scanFilesystemForSwiftModuleDependency( Identifier moduleName, bool isTestableImport) { return swiftModuleScannerLoader->lookupSwiftModule(moduleName, @@ -576,7 +576,7 @@ ModuleDependencyScanner::ModuleDependencyScanner( std::shared_ptr ActionCache, DiagnosticEngine &Diagnostics, bool ParallelScan) : ScanCompilerInvocation(ScanCompilerInvocation), - ScanASTContext(ScanASTContext), Diagnostics(Diagnostics), + ScanASTContext(ScanASTContext), IssueReporter(Diagnostics), NumThreads(ParallelScan ? llvm::hardware_concurrency().compute_thread_count() : 1), @@ -944,8 +944,9 @@ ModuleDependencyScanner::performDependencyScan(ModuleDependencyID rootModuleID, if (ScanCompilerInvocation.getSearchPathOptions().BridgingHeaderChaining) { auto err = performBridgingHeaderChaining(rootModuleID, cache, allModules); if (err) - Diagnostics.diagnose(SourceLoc(), diag::error_scanner_extra, - toString(std::move(err))); + IssueReporter.Diagnostics.diagnose(SourceLoc(), + diag::error_scanner_extra, + toString(std::move(err))); } return allModules.takeVector(); @@ -1145,7 +1146,8 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( std::lock_guard guard(cacheAccessLock); moduleLookupResult.insert_or_assign(moduleName, moduleDependencies); if (!moduleDependencies.empty()) - cache.recordDependencies(moduleDependencies, Diagnostics); + cache.recordDependencies(moduleDependencies, + IssueReporter.Diagnostics); } }; @@ -1228,8 +1230,13 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( auto optionalCachedModuleInfo = cache.findDependency(unresolvedModuleID); if (optionalCachedModuleInfo.has_value()) importedClangDependencies.insert(unresolvedModuleID); - else - diagnoseScannerFailure(unresolvedImport, cache, moduleID); + else { + // Failed to resolve module dependency. + IssueReporter.diagnoseModuleNotFoundFailure( + unresolvedImport, cache, moduleID, + attemptToFindResolvingSerializedSearchPath(unresolvedImport, + cache)); + } } if (!importedClangDependencies.empty()) @@ -1303,10 +1310,7 @@ void ModuleDependencyScanner::resolveSwiftImportsForModule( return; auto moduleDependencyInfo = cache.findKnownDependency(moduleID); - llvm::StringMap> moduleLookupResult; - for (const auto &dependsOn : moduleDependencyInfo.getModuleImports()) - moduleLookupResult.insert( - std::make_pair(dependsOn.importIdentifier, std::nullopt)); + llvm::StringMap moduleLookupResult; std::mutex lookupResultLock; // A scanning task to query a module by-name. If the module already exists @@ -1362,22 +1366,26 @@ void ModuleDependencyScanner::resolveSwiftImportsForModule( if (moduleID.ModuleName == moduleImport.importIdentifier) return; auto lookupResult = moduleLookupResult[moduleImport.importIdentifier]; - // The imported module was found in the cache - if (lookupResult == std::nullopt) { - auto cachedInfo = - cache.findSwiftDependency(moduleImport.importIdentifier); - if (cachedInfo.has_value()) - importedSwiftDependencies.insert( - {moduleImport.importIdentifier, cachedInfo.value()->getKind()}); - } else { - // Cache discovered module dependencies. - if (!lookupResult.value().empty()) { - cache.recordDependencies(lookupResult.value(), Diagnostics); - importedSwiftDependencies.insert( - {moduleImport.importIdentifier, - lookupResult.value()[0].first.Kind}); - } - } + + // Query found module + if (lookupResult.foundDependencyInfo) { + cache.recordDependency(moduleImport.importIdentifier, + *(lookupResult.foundDependencyInfo)); + importedSwiftDependencies.insert( + {moduleImport.importIdentifier, + lookupResult.foundDependencyInfo->getKind()}); + IssueReporter.warnOnIncompatibleCandidates( + moduleImport.importIdentifier, + lookupResult.incompatibleCandidates); + // Module was resolved from a cache + } else if (auto cachedInfo = cache.findSwiftDependency( + moduleImport.importIdentifier)) + importedSwiftDependencies.insert( + {moduleImport.importIdentifier, cachedInfo.value()->getKind()}); + else + IssueReporter.diagnoseFailureOnOnlyIncompatibleCandidates( + moduleImport, lookupResult.incompatibleCandidates, cache, + std::nullopt); }; for (const auto &importInfo : moduleDependencyInfo.getModuleImports()) @@ -1489,26 +1497,32 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule( findAllImportedClangModules(dep.ModuleName, cache, allClangDependencies, knownModules); - llvm::StringMap> - swiftOverlayLookupResult; - for (const auto &clangDep : allClangDependencies) - swiftOverlayLookupResult.insert(std::make_pair(clangDep, std::nullopt)); - + llvm::StringMap swiftOverlayLookupResult; + std::mutex lookupResultLock; + // A scanning task to query a Swift module by-name. If the module already // exists in the cache, do nothing and return. - auto scanForSwiftDependency = [this, &cache, &swiftOverlayLookupResult]( + auto scanForSwiftDependency = [this, &cache, &lookupResultLock, + &swiftOverlayLookupResult]( Identifier moduleIdentifier) { auto moduleName = moduleIdentifier.str(); - if (cache.hasDependency(moduleName, ModuleDependencyKind::SwiftInterface) || - cache.hasDependency(moduleName, ModuleDependencyKind::SwiftBinary)) - return; + { + std::lock_guard guard(lookupResultLock); + if (cache.hasDependency(moduleName, ModuleDependencyKind::SwiftInterface) || + cache.hasDependency(moduleName, ModuleDependencyKind::SwiftBinary)) + return; + } auto moduleDependencies = withDependencyScanningWorker( [moduleIdentifier](ModuleDependencyScanningWorker *ScanningWorker) { return ScanningWorker->scanFilesystemForSwiftModuleDependency( moduleIdentifier, /* isTestableImport */ false); }); - swiftOverlayLookupResult.insert_or_assign(moduleName, moduleDependencies); + + { + std::lock_guard guard(lookupResultLock); + swiftOverlayLookupResult.insert_or_assign(moduleName, moduleDependencies); + } }; // Enque asynchronous lookup tasks @@ -1523,18 +1537,22 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule( moduleID](const std::string &moduleName) { auto lookupResult = swiftOverlayLookupResult[moduleName]; if (moduleName != moduleID.ModuleName) { - if (lookupResult == std::nullopt) { - auto cachedInfo = cache.findSwiftDependency(moduleName); - if (cachedInfo.has_value()) - swiftOverlayDependencies.insert( - {moduleName, cachedInfo.value()->getKind()}); - } else { - // Cache discovered module dependencies. - cache.recordDependencies(lookupResult.value(), Diagnostics); - if (!lookupResult.value().empty()) - swiftOverlayDependencies.insert( - {moduleName, lookupResult.value()[0].first.Kind}); - } + + // Query found module + if (lookupResult.foundDependencyInfo) { + cache.recordDependency(moduleName, *(lookupResult.foundDependencyInfo)); + swiftOverlayDependencies.insert( + {moduleName, lookupResult.foundDependencyInfo->getKind()}); + IssueReporter.warnOnIncompatibleCandidates( + moduleName, lookupResult.incompatibleCandidates); + // Module was resolved from a cache + } else if (auto cachedInfo = cache.findSwiftDependency(moduleName)) + swiftOverlayDependencies.insert( + {moduleName, cachedInfo.value()->getKind()}); + else + IssueReporter.diagnoseFailureOnOnlyIncompatibleCandidates( + ScannerImportStatementInfo(moduleName), + lookupResult.incompatibleCandidates, cache, std::nullopt); } }; for (const auto &clangDep : allClangDependencies) @@ -1783,10 +1801,23 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining( return llvm::Error::success(); } -void ModuleDependencyScanner::diagnoseScannerFailure( +void ModuleDependencyIssueReporter::diagnoseModuleNotFoundFailure( const ScannerImportStatementInfo &moduleImport, const ModuleDependenciesCache &cache, - std::optional dependencyOf) { + std::optional dependencyOf, + std::optional> + resolvingSerializedSearchPath, + std::optional< + std::vector> + foundIncompatibleCandidates) { + // Do not report the same failure multiple times. This can + // happen, for example, when we first report that a valid Swift module is + // missing and then look it up again as a Swift overlay of its underlying + // Clang module. + if (ReportedMissing.find(moduleImport.importIdentifier) != + ReportedMissing.end()) + return; + SourceLoc importLoc = SourceLoc(); if (!moduleImport.importLocations.empty()) { auto locInfo = moduleImport.importLocations[0]; @@ -1794,21 +1825,32 @@ void ModuleDependencyScanner::diagnoseScannerFailure( 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) { + // Emit the top-level error diagnostic, which is one of 3 possibilities: + // 1. Module dependency can not be found but could be resolved if using search + // paths serialized into some binary Swift module dependency + // + // 2. Swift Module dependency can not be found but we did find binary Swift + // module candidates for this module which are not compatible with current + // compilation + // + // 3. All other generic "module not found" cases + if (resolvingSerializedSearchPath) { 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); + resolvingSerializedSearchPath->first.ModuleName, + resolvingSerializedSearchPath->second); + } else if (foundIncompatibleCandidates) { + Diagnostics.diagnose( + importLoc, diag::dependency_scan_compatible_swift_module_not_found, + moduleImport.importIdentifier); + for (const auto &candidate : *foundIncompatibleCandidates) + Diagnostics.diagnose(importLoc, + diag::dependency_scan_incompatible_module_found, + candidate.path, candidate.incompatibilityReason); } else Diagnostics.diagnose(importLoc, diag::dependency_scan_module_not_found, moduleImport.importIdentifier); @@ -1856,7 +1898,7 @@ void ModuleDependencyScanner::diagnoseScannerFailure( } } - // Emit notes for every other location where the failed-to-resolve + // Emit notes for every other known location where the failed-to-resolve // module is imported. if (moduleImport.importLocations.size() > 1) { for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) { @@ -1866,12 +1908,42 @@ void ModuleDependencyScanner::diagnoseScannerFailure( Diagnostics.diagnose(importLoc, diag::unresolved_import_location); } } + + ReportedMissing.insert(moduleImport.importIdentifier); +} + +void ModuleDependencyIssueReporter::diagnoseFailureOnOnlyIncompatibleCandidates( + const ScannerImportStatementInfo &moduleImport, + const std::vector + &candidates, + const ModuleDependenciesCache &cache, + std::optional dependencyOf) { + // If no incompatible candidates were discovered, + // the dependency scanning failure will be caught downstream. + if (candidates.empty()) + return; + + diagnoseModuleNotFoundFailure(moduleImport, cache, dependencyOf, + /* resolvingSerializedSearchPath */ std::nullopt, + candidates); +} + +void ModuleDependencyIssueReporter::warnOnIncompatibleCandidates( + StringRef moduleName, + const std::vector + &candidates) { + // If the dependency was ultimately resolved to a different + // binary module or a textual module, emit warnings about + // having encountered incompatible binary modules. + for (const auto &candidate : candidates) + Diagnostics.diagnose(SourceLoc(), diag::dependency_scan_module_incompatible, + candidate.path, candidate.incompatibilityReason); } std::optional> ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( const ScannerImportStatementInfo &moduleImport, - const ModuleDependenciesCache &cache, const SourceLoc &importLoc) { + const ModuleDependenciesCache &cache) { std::set binarySwiftModuleDepIDs = collectBinarySwiftDeps(cache); @@ -1890,23 +1962,26 @@ ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( [this, &binaryModInfo, &moduleImport, &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), - /* isTestableImport */ false); - if (!result.empty()) - return std::make_pair(binaryDepID, - result[0].second.getModuleDefiningPath()); + auto importIdentifier = + getModuleImportIdentifier(moduleImport.importIdentifier); + SwiftModuleScannerQueryResult swiftResult = + ScanningWorker->scanFilesystemForSwiftModuleDependency( + importIdentifier, /* isTestableImport */ false); + if (swiftResult.foundDependencyInfo) + return std::make_pair( + binaryDepID, + swiftResult.foundDependencyInfo->getModuleDefiningPath()); - result = ScanningWorker->scanFilesystemForClangModuleDependency( - getModuleImportIdentifier(moduleImport.importIdentifier), {}); - if (!result.empty()) + ModuleDependencyVector clangResult = + ScanningWorker->scanFilesystemForClangModuleDependency( + importIdentifier, {}); + if (!clangResult.empty()) return std::make_pair( - binaryDepID, result[0].second.getModuleDefiningPath()); + binaryDepID, clangResult[0].second.getModuleDefiningPath()); return std::nullopt; }); if (result) diff --git a/lib/Serialization/ScanningLoaders.cpp b/lib/Serialization/ScanningLoaders.cpp index bd691d8218476..1fc077f433b76 100644 --- a/lib/Serialization/ScanningLoaders.cpp +++ b/lib/Serialization/ScanningLoaders.cpp @@ -61,26 +61,26 @@ std::error_code SwiftModuleScanner::findModuleFilesInDirectory( isTestableDependencyLookup || !InPath) { if (fs.exists(ModPath)) { // The module file will be loaded directly. - auto dependencies = scanBinaryModuleFile( + auto dependencyInfo = scanBinaryModuleFile( ModuleID.Item, ModPath, IsFramework, isTestableDependencyLookup, /* isCandidateForTextualModule */ false); - if (dependencies) { - this->dependencies = std::move(dependencies.get()); + if (dependencyInfo) { + this->foundDependencyInfo = std::move(dependencyInfo.get()); return std::error_code(); } - return dependencies.getError(); + return dependencyInfo.getError(); } return std::make_error_code(std::errc::no_such_file_or_directory); } assert(InPath); - auto dependencies = scanInterfaceFile(ModuleID.Item, *InPath, IsFramework, - isTestableDependencyLookup); - if (dependencies) { - this->dependencies = std::move(dependencies.get()); + auto dependencyInfo = scanInterfaceFile(ModuleID.Item, *InPath, IsFramework, + isTestableDependencyLookup); + if (dependencyInfo) { + this->foundDependencyInfo = std::move(dependencyInfo.get()); return std::error_code(); } - return dependencies.getError(); + return dependencyInfo.getError(); } static std::vector getCompiledCandidates(ASTContext &ctx, @@ -266,21 +266,14 @@ llvm::ErrorOr SwiftModuleScanner::scanBinaryModuleFile( // it would be helpful to let the user know why the scanner // was not able to use it because the scan will ultimately fail to // resolve this dependency due to this incompatibility. - if (!isCandidateForTextualModule) - Ctx.Diags.diagnose(SourceLoc(), - diag::dependency_scan_module_incompatible, - binaryModulePath.str(), loadFailureReason.value()); - - if (Ctx.LangOpts.EnableModuleLoadingRemarks) - Ctx.Diags.diagnose(SourceLoc(), - diag::dependency_scan_skip_module_invalid, - binaryModulePath.str(), loadFailureReason.value()); + incompatibleCandidates.push_back({binaryModulePath.str(), + loadFailureReason.value()}); return std::make_error_code(std::errc::no_such_file_or_directory); } if (isTestableImport && !loadedModuleFile->isTestable()) { - Ctx.Diags.diagnose(SourceLoc(), diag::skip_module_not_testable, - binaryModulePath.str()); + incompatibleCandidates.push_back({binaryModulePath.str(), + "module built without '-enable-testing'"}); return std::make_error_code(std::errc::no_such_file_or_directory); } } @@ -337,22 +330,21 @@ llvm::ErrorOr SwiftModuleScanner::scanBinaryModuleFile( return std::move(dependencies); } -ModuleDependencyVector +SwiftModuleScannerQueryResult SwiftModuleScanner::lookupSwiftModule(Identifier moduleName, bool isTestableImport) { // When we exit, ensure we clear dependencies discovered on this query - SWIFT_DEFER { dependencies = std::nullopt; }; + SWIFT_DEFER { + foundDependencyInfo = std::nullopt; + incompatibleCandidates = {}; + }; ImportPath::Module::Builder builder(moduleName); auto modulePath = builder.get(); - // Check whether there is a module with this name that we can import. - if (canImportModule(modulePath, SourceLoc(), nullptr, isTestableImport)) { - ModuleDependencyVector moduleDependnecies; - moduleDependnecies.push_back(std::make_pair( - ModuleDependencyID{moduleName.str().str(), dependencies->getKind()}, - *(dependencies))); - return moduleDependnecies; - } - - return {}; + // Execute the check to determine whether there is a module with this name + // that we can import. This check will populate the result fields if one is + // found. + canImportModule(modulePath, SourceLoc(), nullptr, isTestableImport); + return SwiftModuleScannerQueryResult( + std::move(foundDependencyInfo), std::move(incompatibleCandidates)); } diff --git a/test/ScanDependencies/bridging_header_error.swift b/test/ScanDependencies/bridging_header_error.swift index 6aa471f6f6cd3..8a948e239a60c 100644 --- a/test/ScanDependencies/bridging_header_error.swift +++ b/test/ScanDependencies/bridging_header_error.swift @@ -4,4 +4,4 @@ // RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %s -o %t/deps.json -import-objc-header %t/does-this-header-even-exist.h &> %t/diagnostic_output.txt // RUN: cat %t/diagnostic_output.txt | %FileCheck %s -// CHECK: error: Bridging header dependency scan failure: error: no such file or directory: '{{.*}}does-this-header-even-exist.h' +// CHECK: error: bridging header dependency scanning failure: error: no such file or directory: '{{.*}}does-this-header-even-exist.h' 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 8d0e0467c4d07..c078691e1ecd2 100644 --- a/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift +++ b/test/ScanDependencies/cached-missing-module-found-in-serialized-paths.swift @@ -21,8 +21,8 @@ // 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: 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 diff --git a/test/ScanDependencies/candidate_binary_module_diagnostics.swift b/test/ScanDependencies/candidate_binary_module_diagnostics.swift new file mode 100644 index 0000000000000..59c5a6fdf1026 --- /dev/null +++ b/test/ScanDependencies/candidate_binary_module_diagnostics.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) +// RUN: %empty-directory(%t/moduleInputs) +// RUN: %empty-directory(%t/moduleInputs2) +// RUN: split-file %s %t + +// RUN: echo "Not Really a module" >> %t/moduleInputs/FooBar.swiftmodule +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %t/main.swift -o %t/deps.json -I %t/moduleInputs -diagnostic-style llvm -scanner-module-validation 2>&1 | %FileCheck %s -check-prefix=ERROR + +// ERROR: error: unable to resolve Swift module dependency to a compatible module: 'FooBar' +// ERROR: note: found incompatible module '{{.*}}{{/|\\}}moduleInputs{{/|\\}}FooBar.swiftmodule': malformed + +// RUN: %target-swift-frontend -emit-module %t/FooBar.swift -emit-module-path %t/moduleInputs2/FooBar.swiftmodule -module-name FooBar +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %t/main.swift -o %t/deps.json -I %t/moduleInputs -I %t/moduleInputs2 -diagnostic-style llvm -scanner-module-validation 2>&1 | %FileCheck %s -check-prefix=WARNING + +// WARNING: warning: module file '{{.*}}{{/|\\}}moduleInputs{{/|\\}}FooBar.swiftmodule' is incompatible with this Swift compiler: malformed + +//--- main.swift +import FooBar + +//--- FooBar.swift +public func fooBar() {} diff --git a/test/ScanDependencies/clang_scan_error.swift b/test/ScanDependencies/clang_scan_error.swift index 3ecc0b6d6b5cb..f26f4150e5b9b 100644 --- a/test/ScanDependencies/clang_scan_error.swift +++ b/test/ScanDependencies/clang_scan_error.swift @@ -6,7 +6,7 @@ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \ // RUN: -o %t/deps.json -I %t -swift-version 5 2>&1 | %FileCheck %s -// CHECK: error: Bridging header dependency scan failure: {{.*}}bridging.h:1:10: fatal error: 'do-not-exist.h' file not found +// CHECK: error: bridging header dependency scanning failure: {{.*}}bridging.h:1:10: fatal error: 'do-not-exist.h' file not found //--- bridging.h #include "do-not-exist.h" 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 453fc951ec790..f08ce6f5bb6c2 100644 --- a/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift +++ b/test/ScanDependencies/diagnose-missing-module-found-in-serialized-paths.swift @@ -9,8 +9,8 @@ // 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: 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' diff --git a/test/ScanDependencies/error_path.swift b/test/ScanDependencies/error_path.swift index 0e128e2cea40b..6a659d5c6af56 100644 --- a/test/ScanDependencies/error_path.swift +++ b/test/ScanDependencies/error_path.swift @@ -5,11 +5,11 @@ import P -// CHECK: {{.*}}{{/|\\}}Z.swiftinterface:3:8: error: Unable to find module dependency: 'missing_module' +// CHECK: {{.*}}{{/|\\}}Z.swiftinterface:3:8: error: unable to resolve module dependency: 'missing_module' // CHECK-NEXT: 1 | // swift-interface-format-version: 1.0 // CHECK-NEXT: 2 | // swift-module-flags: -module-name Z // CHECK-NEXT: 3 | import missing_module -// CHECK-NEXT: | |- error: Unable to find module dependency: 'missing_module' +// CHECK-NEXT: | |- error: unable to resolve module dependency: 'missing_module' // CHECK-NEXT: | |- note: a dependency of Swift module 'Z': '{{.*}}{{/|\\}}Z.swiftinterface' // CHECK-NEXT: | |- note: a dependency of Swift module 'Y': '{{.*}}{{/|\\}}Y.swiftinterface' // CHECK-NEXT: | |- note: a dependency of Swift module 'P': '{{.*}}{{/|\\}}P.swiftinterface' diff --git a/test/ScanDependencies/error_source_locations.swift b/test/ScanDependencies/error_source_locations.swift index 599864ae4bb2c..2a57a2e51bba6 100644 --- a/test/ScanDependencies/error_source_locations.swift +++ b/test/ScanDependencies/error_source_locations.swift @@ -7,20 +7,20 @@ import P import FooBar -// CHECK: {{.*}}{{/|\\}}error_source_locations.swift:7:8: error: Unable to find module dependency: 'FooBar' +// CHECK: {{.*}}{{/|\\}}error_source_locations.swift:7:8: error: unable to resolve module dependency: 'FooBar' // CHECK-NEXT: 5 | // CHECK-NEXT: 6 | import P // CHECK-NEXT: 7 | import FooBar -// CHECK-NEXT: | |- error: Unable to find module dependency: 'FooBar' +// CHECK-NEXT: | |- error: unable to resolve module dependency: 'FooBar' // CHECK-NEXT: | `- note: a dependency of main module 'deps' // CHECK-NEXT: 8 | // CHECK-NEXT: 9 | -// CHECK: {{.*}}{{/|\\}}Z.swiftinterface:3:8: error: Unable to find module dependency: 'missing_module' +// CHECK: {{.*}}{{/|\\}}Z.swiftinterface:3:8: error: unable to resolve module dependency: 'missing_module' // CHECK-NEXT: 1 | // swift-interface-format-version: 1.0 // CHECK-NEXT: 2 | // swift-module-flags: -module-name Z // CHECK-NEXT: 3 | import missing_module -// CHECK-NEXT: | |- error: Unable to find module dependency: 'missing_module' +// CHECK-NEXT: | |- error: unable to resolve module dependency: 'missing_module' // CHECK-NEXT: | |- note: a dependency of Swift module 'Z': '{{.*}}{{/|\\}}Z.swiftinterface' // CHECK-NEXT: | |- note: a dependency of Swift module 'Y': '{{.*}}{{/|\\}}Y.swiftinterface' // CHECK-NEXT: | |- note: a dependency of Swift module 'P': '{{.*}}{{/|\\}}P.swiftinterface' diff --git a/test/ScanDependencies/invalid_binary_module_only.swift b/test/ScanDependencies/invalid_binary_module_only.swift index a53a7c164d77c..f96d1c7e7bba5 100644 --- a/test/ScanDependencies/invalid_binary_module_only.swift +++ b/test/ScanDependencies/invalid_binary_module_only.swift @@ -8,6 +8,5 @@ import FooBar -// CHECK: warning: module file '{{.*}}{{/|\\}}moduleInputs{{/|\\}}FooBar.swiftmodule' is incompatible with this Swift compiler: malformed -// CHECK: error: Unable to find module dependency: 'FooBar' - +// CHECK: error: unable to resolve Swift module dependency to a compatible module: 'FooBar' +// CHECK: note: found incompatible module '{{.*}}{{/|\\}}moduleInputs{{/|\\}}FooBar.swiftmodule': malformed diff --git a/test/ScanDependencies/testable-dependencies.swift b/test/ScanDependencies/testable-dependencies.swift index b43f2c8ac9359..d575287da8793 100644 --- a/test/ScanDependencies/testable-dependencies.swift +++ b/test/ScanDependencies/testable-dependencies.swift @@ -50,13 +50,14 @@ // TEST4: "swiftPrebuiltExternal": "A" // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps4.json swiftPrebuiltExternal:A directDependencies | %FileCheck %s --check-prefix TEST4-A // TEST4-A: "swift": "B" +// WARN: warning: module file '{{.*}}{{/|\\}}A.swiftmodule' is incompatible with this Swift compiler: module built without '-enable-testing' /// Testable import non-testable build enable testing, warning about the finding then error out. // RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-interface -module-name Test %t/testable.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -enable-testing \ -// RUN: -o %t/deps5.json -I %t/regular -swift-version 5 -Rmodule-loading 2>&1 | %FileCheck %s --check-prefix WARN --check-prefix ERROR -// WARN: warning: ignore swiftmodule built without '-enable-testing' -// ERROR: error: Unable to find module dependency: 'A' +// RUN: -o %t/deps5.json -I %t/regular -swift-version 5 -Rmodule-loading 2>&1 | %FileCheck %s --check-prefix ERROR +// ERROR: error: unable to resolve Swift module dependency to a compatible module: 'A' +// ERROR: note: found incompatible module '{{.*}}{{/|\\}}A.swiftmodule': module built without '-enable-testing' /// Regular import a testable module with no interface, don't load optional dependencies. // RUN: rm %t/testable/A.swiftinterface