mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Dependency Scanning] Specify Source Locations For Missing Module Dependencies
This change modifies the dependency scanner to keep track of source locations of each encountered 'import' statement, in order to be able to emit diagnostics with source locations if an import failed to resolve. - Keep track of each 'import' statement's source buffer, line number, and column number when adding it. The dependency scanner utilizes separate compilation instances, and therefore separate Source Managers for scanning `import` statements of user sources and textual interfaces of Swift dependencies. Since import resolution may happen in the main scanner compilation instance while the `import` itself was found by an interface-scanning sub-instance, we cannot simply hold on to the import's `SourceLoc`. - Add libSwiftScan API for diagnostics to carry above source locations to clients.
This commit is contained in:
@@ -54,14 +54,15 @@ static void findPath_dfs(ModuleDependencyID X, ModuleDependencyID Y,
|
||||
std::optional<ModuleDependencyKind> lookupKind = std::nullopt;
|
||||
// Underlying Clang module needs an explicit lookup to avoid confusing it
|
||||
// with the parent Swift module.
|
||||
if ((dep == X.ModuleName && node->isSwiftModule()) || node->isClangModule())
|
||||
if ((dep.importIdentifier == X.ModuleName && node->isSwiftModule()) ||
|
||||
node->isClangModule())
|
||||
lookupKind = ModuleDependencyKind::Clang;
|
||||
|
||||
auto optionalDepNode = cache.findDependency(dep, lookupKind);
|
||||
auto optionalDepNode = cache.findDependency(dep.importIdentifier, lookupKind);
|
||||
if (!optionalDepNode.has_value())
|
||||
continue;
|
||||
auto depNode = optionalDepNode.value();
|
||||
auto depID = ModuleDependencyID{dep, depNode->getKind()};
|
||||
auto depID = ModuleDependencyID{dep.importIdentifier, depNode->getKind()};
|
||||
if (!visited.count(depID)) {
|
||||
findPath_dfs(depID, Y, visited, stack, result, cache);
|
||||
}
|
||||
@@ -89,11 +90,20 @@ findPathToDependency(ModuleDependencyID dependency,
|
||||
// Diagnose scanner failure and attempt to reconstruct the dependency
|
||||
// path from the main module to the missing dependency.
|
||||
static void
|
||||
diagnoseScannerFailure(StringRef moduleName, DiagnosticEngine &Diags,
|
||||
diagnoseScannerFailure(const ScannerImportStatementInfo &moduleImport,
|
||||
DiagnosticEngine &Diags,
|
||||
const ModuleDependenciesCache &cache,
|
||||
std::optional<ModuleDependencyID> dependencyOf) {
|
||||
Diags.diagnose(SourceLoc(), diag::dependency_scan_module_not_found,
|
||||
moduleName);
|
||||
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
|
||||
@@ -110,7 +120,7 @@ diagnoseScannerFailure(StringRef moduleName, DiagnosticEngine &Diags,
|
||||
bool isClang = false;
|
||||
switch (entryNode->getKind()) {
|
||||
case swift::ModuleDependencyKind::SwiftSource:
|
||||
Diags.diagnose(SourceLoc(), diag::dependency_as_imported_by_main_module,
|
||||
Diags.diagnose(importLoc, diag::dependency_as_imported_by_main_module,
|
||||
entry.ModuleName);
|
||||
continue;
|
||||
case swift::ModuleDependencyKind::SwiftInterface:
|
||||
@@ -133,13 +143,20 @@ diagnoseScannerFailure(StringRef moduleName, DiagnosticEngine &Diags,
|
||||
llvm_unreachable("Unexpected dependency kind");
|
||||
}
|
||||
|
||||
// TODO: Consider turning the module file (interface or modulemap) into
|
||||
// the SourceLoc of this diagnostic instead. Ideally with the location of
|
||||
// the `import` statement that this dependency originates from.
|
||||
Diags.diagnose(SourceLoc(), diag::dependency_as_imported_by,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleDependencyScanningWorker::ModuleDependencyScanningWorker(
|
||||
@@ -402,13 +419,15 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) {
|
||||
// Add any implicit module names.
|
||||
for (const auto &import : importInfo.AdditionalUnloadedImports) {
|
||||
mainDependencies.addModuleImport(import.module.getModulePath(),
|
||||
&alreadyAddedModules);
|
||||
&alreadyAddedModules,
|
||||
&ScanASTContext.SourceMgr);
|
||||
}
|
||||
|
||||
// Already-loaded, implicitly imported module names.
|
||||
for (const auto &import : importInfo.AdditionalImports) {
|
||||
mainDependencies.addModuleImport(
|
||||
import.module.importedModule->getNameStr(), &alreadyAddedModules);
|
||||
import.module.importedModule->getNameStr(),
|
||||
&alreadyAddedModules);
|
||||
}
|
||||
|
||||
// Add the bridging header.
|
||||
@@ -428,18 +447,21 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) {
|
||||
// to be impored in the source.
|
||||
for (const auto &tbdSymbolModule :
|
||||
ScanCompilerInvocation.getTBDGenOptions().embedSymbolsFromModules) {
|
||||
mainDependencies.addModuleImport(tbdSymbolModule, &alreadyAddedModules);
|
||||
mainDependencies.addModuleImport(tbdSymbolModule,
|
||||
&alreadyAddedModules);
|
||||
}
|
||||
}
|
||||
|
||||
// Add source-specified `import` dependencies
|
||||
{
|
||||
for (auto fileUnit : mainModule->getFiles()) {
|
||||
auto sf = dyn_cast<SourceFile>(fileUnit);
|
||||
if (!sf)
|
||||
auto sourceFile = dyn_cast<SourceFile>(fileUnit);
|
||||
if (!sourceFile)
|
||||
continue;
|
||||
|
||||
mainDependencies.addModuleImport(*sf, alreadyAddedModules);
|
||||
mainDependencies.addModuleImports(*sourceFile,
|
||||
alreadyAddedModules,
|
||||
&ScanASTContext.SourceMgr);
|
||||
}
|
||||
|
||||
// Add all the successful canImport checks from the ASTContext as part of
|
||||
@@ -448,8 +470,9 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) {
|
||||
// SourceFiles.
|
||||
for (auto &Module :
|
||||
mainModule->getASTContext().getSuccessfulCanImportCheckNames())
|
||||
mainDependencies.addModuleImport(Module.first(), &alreadyAddedModules);
|
||||
}
|
||||
mainDependencies.addModuleImport(Module.first(),
|
||||
&alreadyAddedModules);
|
||||
}
|
||||
|
||||
return mainDependencies;
|
||||
}
|
||||
@@ -569,8 +592,9 @@ void ModuleDependencyScanner::resolveImportDependencies(
|
||||
auto moduleDependencyInfo = optionalModuleDependencyInfo.value();
|
||||
|
||||
llvm::StringMap<std::optional<ModuleDependencyVector>> moduleLookupResult;
|
||||
// ACTDOO: Import refactor
|
||||
for (const auto &dependsOn : moduleDependencyInfo->getModuleImports())
|
||||
moduleLookupResult.insert(std::make_pair(dependsOn, std::nullopt));
|
||||
moduleLookupResult.insert(std::make_pair(dependsOn.importIdentifier, std::nullopt));
|
||||
|
||||
// A scanning task to query a module by-name. If the module already exists
|
||||
// in the cache, do nothing and return.
|
||||
@@ -599,51 +623,55 @@ void ModuleDependencyScanner::resolveImportDependencies(
|
||||
moduleLookupResult.insert_or_assign(moduleName, moduleDependencies);
|
||||
};
|
||||
|
||||
// ACTDOO: Import refactor
|
||||
// Enque asynchronous lookup tasks
|
||||
for (const auto &dependsOn : moduleDependencyInfo->getModuleImports()) {
|
||||
bool underlyingClangModuleLookup = moduleID.ModuleName == dependsOn;
|
||||
bool isTestable = moduleDependencyInfo->isTestableImport(dependsOn);
|
||||
ScanningThreadPool.async(scanForModuleDependency, getModuleImportIdentifier(dependsOn),
|
||||
bool underlyingClangModuleLookup = moduleID.ModuleName == dependsOn.importIdentifier;
|
||||
bool isTestable = moduleDependencyInfo->isTestableImport(dependsOn.importIdentifier);
|
||||
ScanningThreadPool.async(scanForModuleDependency, getModuleImportIdentifier(dependsOn.importIdentifier),
|
||||
underlyingClangModuleLookup, isTestable);
|
||||
}
|
||||
for (const auto &dependsOn :
|
||||
moduleDependencyInfo->getOptionalModuleImports()) {
|
||||
bool underlyingClangModuleLookup = moduleID.ModuleName == dependsOn;
|
||||
bool isTestable = moduleDependencyInfo->isTestableImport(dependsOn);
|
||||
ScanningThreadPool.async(scanForModuleDependency, getModuleImportIdentifier(dependsOn),
|
||||
bool underlyingClangModuleLookup = moduleID.ModuleName == dependsOn.importIdentifier;
|
||||
bool isTestable = moduleDependencyInfo->isTestableImport(dependsOn.importIdentifier);
|
||||
ScanningThreadPool.async(scanForModuleDependency, getModuleImportIdentifier(dependsOn.importIdentifier),
|
||||
underlyingClangModuleLookup, isTestable);
|
||||
}
|
||||
ScanningThreadPool.wait();
|
||||
|
||||
std::vector<std::string> unresolvedImports;
|
||||
std::vector<ScannerImportStatementInfo> unresolvedImports;
|
||||
// Aggregate both previously-cached and freshly-scanned module results
|
||||
auto recordResolvedModuleImport =
|
||||
[&cache, &moduleLookupResult, &unresolvedImports, &directDependencies,
|
||||
moduleID](const std::string &moduleName, bool optionalImport) {
|
||||
bool underlyingClangModule = moduleID.ModuleName == moduleName;
|
||||
auto lookupResult = moduleLookupResult[moduleName];
|
||||
moduleID](const ScannerImportStatementInfo &moduleImport, bool optionalImport) {
|
||||
bool underlyingClangModule = moduleID.ModuleName == moduleImport.importIdentifier;
|
||||
auto lookupResult = moduleLookupResult[moduleImport.importIdentifier];
|
||||
// The imported module was found in the cache
|
||||
if (lookupResult == std::nullopt) {
|
||||
const ModuleDependencyInfo *cachedInfo;
|
||||
if (underlyingClangModule)
|
||||
cachedInfo =
|
||||
cache.findDependency(moduleName, ModuleDependencyKind::Clang)
|
||||
.value();
|
||||
cache.findDependency(moduleImport.importIdentifier,
|
||||
ModuleDependencyKind::Clang).value();
|
||||
else
|
||||
cachedInfo = cache.findDependency(moduleName).value();
|
||||
cachedInfo =
|
||||
cache.findDependency(moduleImport.importIdentifier).value();
|
||||
assert(cachedInfo && "Expected cached dependency info");
|
||||
directDependencies.insert({moduleName, cachedInfo->getKind()});
|
||||
directDependencies.insert({moduleImport.importIdentifier,
|
||||
cachedInfo->getKind()});
|
||||
} else {
|
||||
// Cache discovered module dependencies.
|
||||
if (!lookupResult.value().empty()) {
|
||||
cache.recordDependencies(lookupResult.value());
|
||||
directDependencies.insert(
|
||||
{moduleName, lookupResult.value()[0].first.Kind});
|
||||
{moduleImport.importIdentifier,
|
||||
lookupResult.value()[0].first.Kind});
|
||||
} else if (!optionalImport) {
|
||||
// Otherwise, we failed to resolve this dependency. We will try
|
||||
// again using the cache after all other imports have been resolved.
|
||||
// If that fails too, a scanning failure will be diagnosed.
|
||||
unresolvedImports.push_back(moduleName);
|
||||
unresolvedImports.push_back(moduleImport);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -676,14 +704,16 @@ void ModuleDependencyScanner::resolveImportDependencies(
|
||||
// transitive dependencies to the cache, and then attempt to re-query imports
|
||||
// for which resolution originally failed from the cache. If this fails, then
|
||||
// the scanner genuinely failed to resolve this dependency.
|
||||
for (const auto &moduleName : unresolvedImports) {
|
||||
for (const auto &moduleImport : unresolvedImports) {
|
||||
auto optionalCachedModuleInfo =
|
||||
cache.findDependency({moduleName, ModuleDependencyKind::Clang});
|
||||
cache.findDependency({moduleImport.importIdentifier,
|
||||
ModuleDependencyKind::Clang});
|
||||
if (optionalCachedModuleInfo.has_value())
|
||||
directDependencies.insert(
|
||||
{moduleName, optionalCachedModuleInfo.value()->getKind()});
|
||||
{moduleImport.importIdentifier,
|
||||
optionalCachedModuleInfo.value()->getKind()});
|
||||
else
|
||||
diagnoseScannerFailure(moduleName, Diagnostics, cache, moduleID);
|
||||
diagnoseScannerFailure(moduleImport, Diagnostics, cache, moduleID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user