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.

This commit is contained in:
Artem Chikin
2025-06-03 15:34:12 -07:00
parent e67be5ad7a
commit e37071bcd4
7 changed files with 69 additions and 48 deletions

View File

@@ -50,6 +50,7 @@ GROUP(ExistentialAny, "existential-any")
GROUP(ExistentialMemberAccess, "existential-member-access-limitations") GROUP(ExistentialMemberAccess, "existential-member-access-limitations")
GROUP(IsolatedConformances, "isolated-conformances") GROUP(IsolatedConformances, "isolated-conformances")
GROUP(MemberImportVisibility, "member-import-visibility") GROUP(MemberImportVisibility, "member-import-visibility")
GROUP(MissingModuleOnKnownPaths, "missing-module-on-known-paths")
GROUP(MultipleInheritance, "multiple-inheritance") GROUP(MultipleInheritance, "multiple-inheritance")
GROUP(MutableGlobalVariable, "mutable-global-variable") GROUP(MutableGlobalVariable, "mutable-global-variable")
GROUP(NominalTypes, "nominal-types") GROUP(NominalTypes, "nominal-types")

View File

@@ -2332,6 +2332,8 @@ ERROR(alignment_not_power_of_two,none,
// Dependency Scanning // 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 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, NOTE(unresolved_import_location,none,
"also imported here", ()) "also imported here", ())
NOTE(dependency_as_imported_by_main_module,none, 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, NOTE(dependency_as_imported_by, none,
"a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool)) "a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool))
NOTE(inherited_search_path_resolves_module,none, NOTE(inherited_search_path_resolves_module,none,
"'%0' can be found on a search path used to build module '%1': '%2'. " "'%0' can be found using a search path that was specified when building module '%1' ('%2'). "
"These search paths are not inherited by the current compilation.", (StringRef, StringRef, StringRef)) "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_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 scan failure: %0", (StringRef))

View File

@@ -203,8 +203,10 @@ private:
/// Assuming the \c `moduleImport` failed to resolve, /// Assuming the \c `moduleImport` failed to resolve,
/// iterate over all binary Swift module dependencies with serialized /// iterate over all binary Swift module dependencies with serialized
/// search paths and attempt to diagnose if the failed-to-resolve module /// search paths and attempt to diagnose if the failed-to-resolve module
/// can be found on any of them. /// can be found on any of them. Returns the path containing
void attemptToFindResolvingSerializedSearchPath( /// the module, if one is found.
std::optional<std::pair<ModuleDependencyID, std::string>>
attemptToFindResolvingSerializedSearchPath(
const ScannerImportStatementInfo &moduleImport, const ScannerImportStatementInfo &moduleImport,
const ModuleDependenciesCache &cache, const SourceLoc &importLoc); const ModuleDependenciesCache &cache, const SourceLoc &importLoc);

View File

@@ -1656,8 +1656,27 @@ void ModuleDependencyScanner::diagnoseScannerFailure(
locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber); locInfo.bufferIdentifier, locInfo.lineNumber, locInfo.columnNumber);
} }
Diagnostics.diagnose(importLoc, diag::dependency_scan_module_not_found, // Attempt to determine if any of the binary Swift module dependencies contain
moduleImport.importIdentifier); // 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()) { if (dependencyOf.has_value()) {
auto path = findPathToDependency(dependencyOf.value(), cache); auto path = findPathToDependency(dependencyOf.value(), cache);
// We may fail to construct a path in some cases, such as a Swift overlay of // 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) { if (moduleImport.importLocations.size() > 1) {
for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) { for (size_t i = 1; i < moduleImport.importLocations.size(); ++i) {
auto locInfo = moduleImport.importLocations[i]; auto locInfo = moduleImport.importLocations[i];
@@ -1711,8 +1732,6 @@ void ModuleDependencyScanner::diagnoseScannerFailure(
Diagnostics.diagnose(importLoc, diag::unresolved_import_location); Diagnostics.diagnose(importLoc, diag::unresolved_import_location);
} }
} }
attemptToFindResolvingSerializedSearchPath(moduleImport, cache, importLoc);
} }
static std::string getModuleDefiningPath(const ModuleDependencyInfo &info) { 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`, // Relative to the `module.modulemap` or `.swiftinterface` or `.swiftmodule`,
// the defininig path is the parent directory of the file. // 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(); return llvm::sys::path::parent_path(path).str();
>>>>>>> Stashed changes
} }
void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath( std::optional<std::pair<ModuleDependencyID, std::string>>
ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath(
const ScannerImportStatementInfo &moduleImport, const ScannerImportStatementInfo &moduleImport,
const ModuleDependenciesCache &cache, const SourceLoc &importLoc) { const ModuleDependenciesCache &cache, const SourceLoc &importLoc) {
std::set<ModuleDependencyID> binarySwiftModuleDepIDs = std::set<ModuleDependencyID> binarySwiftModuleDepIDs =
collectBinarySwiftDeps(cache); collectBinarySwiftDeps(cache);
std::optional<std::pair<ModuleDependencyID, std::string>> result;
for (const auto &binaryDepID : binarySwiftModuleDepIDs) { for (const auto &binaryDepID : binarySwiftModuleDepIDs) {
auto binaryModInfo = auto binaryModInfo =
cache.findKnownDependency(binaryDepID).getAsSwiftBinaryModule(); cache.findKnownDependency(binaryDepID).getAsSwiftBinaryModule();
@@ -1772,9 +1777,10 @@ void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath(
// Note: this will permanently mutate this worker with additional search // Note: this will permanently mutate this worker with additional search
// paths. That's fine because we are diagnosing a scan failure here, but // paths. That's fine because we are diagnosing a scan failure here, but
// worth being aware of. // worth being aware of.
withDependencyScanningWorker( result = withDependencyScanningWorker(
[&binaryModInfo, &moduleImport, &cache, &binaryDepID, &importLoc, [&binaryModInfo, &moduleImport, &cache, this,
this](ModuleDependencyScanningWorker *ScanningWorker) { &binaryDepID](ModuleDependencyScanningWorker *ScanningWorker)
-> std::optional<std::pair<ModuleDependencyID, std::string>> {
ModuleDependencyVector result; ModuleDependencyVector result;
for (const auto &sp : binaryModInfo->serializedSearchPaths) for (const auto &sp : binaryModInfo->serializedSearchPaths)
ScanningWorker->workerASTContext->addSearchPath( ScanningWorker->workerASTContext->addSearchPath(
@@ -1784,31 +1790,22 @@ void ModuleDependencyScanner::attemptToFindResolvingSerializedSearchPath(
getModuleImportIdentifier(moduleImport.importIdentifier), getModuleImportIdentifier(moduleImport.importIdentifier),
cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(),
cache.getScanService().getPrefixMapper()); cache.getScanService().getPrefixMapper());
if (!result.empty()) { if (!result.empty())
Diagnostics.diagnose( return std::make_pair(binaryDepID,
importLoc, diag::inherited_search_path_resolves_module, getModuleDefiningPath(result[0].second));
moduleImport.importIdentifier, binaryDepID.ModuleName,
getModuleDefiningPath(result[0].second));
}
result = ScanningWorker->scanFilesystemForClangModuleDependency( result = ScanningWorker->scanFilesystemForClangModuleDependency(
getModuleImportIdentifier(moduleImport.importIdentifier), getModuleImportIdentifier(moduleImport.importIdentifier),
cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), {}, cache.getModuleOutputPath(), cache.getSDKModuleOutputPath(), {},
cache.getScanService().getPrefixMapper()); 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()) if (!result.empty())
return std::make_pair(binaryDepID, return std::make_pair(binaryDepID,
getModuleDefiningPath(result[0].second)); getModuleDefiningPath(result[0].second));
return std::nullopt; return std::nullopt;
>>>>>>> Stashed changes
}); });
if (result)
break;
} }
return result;
} }

View File

@@ -21,9 +21,9 @@
// CHECK: remark: Incremental module scan: Re-using serialized module scanning dependency cache from: '{{.*}}cache.moddepcache'. // 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 '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: 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: 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 //--- moreDeps/C.swiftinterface
// swift-interface-format-version: 1.0 // swift-interface-format-version: 1.0

View File

@@ -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: %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 // 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 Swift module 'B': '{{.*}}B.swiftmodule'
// CHECK: note: a dependency of main module 'deps' // 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 //--- moreDeps/C.swiftinterface
// swift-interface-format-version: 1.0 // swift-interface-format-version: 1.0

View File

@@ -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' ('<Search Path>'). 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.