Skip to content

Commit 4a7e534

Browse files
committed
[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.
1 parent 15cab3a commit 4a7e534

15 files changed

+358
-127
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ typedef struct swiftscan_import_set_s *swiftscan_import_set_t;
5555
/// Opaque container to contain the info of a diagnostics emitted by the scanner.
5656
typedef struct swiftscan_diagnostic_info_s *swiftscan_diagnostic_info_t;
5757

58+
/// Opaque container to contain the info of a source location.
59+
typedef struct swiftscan_source_location_s *swiftscan_source_location_t;
60+
5861
/// Full Dependency Graph (Result)
5962
typedef struct {
6063
swiftscan_dependency_info_t *modules;
@@ -419,9 +422,25 @@ swiftscan_diagnostic_get_message(swiftscan_diagnostic_info_t diagnostic);
419422
SWIFTSCAN_PUBLIC swiftscan_diagnostic_severity_t
420423
swiftscan_diagnostic_get_severity(swiftscan_diagnostic_info_t diagnostic);
421424

425+
SWIFTSCAN_PUBLIC swiftscan_source_location_t
426+
swiftscan_diagnostic_get_source_location(swiftscan_diagnostic_info_t diagnostic);
427+
422428
SWIFTSCAN_PUBLIC void
423429
swiftscan_diagnostics_set_dispose(swiftscan_diagnostic_set_t* diagnostics);
424430

431+
//=== Source Location -----------------------------------------------------===//
432+
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
433+
swiftscan_source_location_get_buffer_identifier(swiftscan_source_location_t source_location);
434+
435+
SWIFTSCAN_PUBLIC int64_t
436+
swiftscan_source_location_get_line_number(swiftscan_source_location_t source_location);
437+
438+
SWIFTSCAN_PUBLIC int64_t
439+
swiftscan_source_location_get_column_number(swiftscan_source_location_t source_location);
440+
441+
SWIFTSCAN_PUBLIC void
442+
swiftscan_source_location_dispose(swiftscan_source_location_t source_location);
443+
425444
//=== Scanner Cache Operations --------------------------------------------===//
426445
// The following operations expose an implementation detail of the dependency
427446
// scanner: its module dependencies cache. This is done in order

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,6 +2217,8 @@ ERROR(alignment_not_power_of_two,none,
22172217

22182218
// Dependency Scanning
22192219
ERROR(dependency_scan_module_not_found, none, "Unable to find module dependency: '%0'", (StringRef))
2220+
NOTE(unresolved_import_location,none,
2221+
"also imported here", ())
22202222
NOTE(dependency_as_imported_by_main_module,none,
22212223
"a dependency of main module '%0'", (StringRef))
22222224
NOTE(dependency_as_imported_by, none,

include/swift/AST/ModuleDependencies.h

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,39 @@ namespace dependencies {
129129
bool);
130130
}
131131

132+
struct ImportStatementInfo {
133+
struct ImportDiagnosticLocationInfo {
134+
ImportDiagnosticLocationInfo() = delete;
135+
ImportDiagnosticLocationInfo(std::string bufferIdentifier,
136+
uint32_t lineNumber,
137+
uint32_t columnNumber)
138+
: bufferIdentifier(bufferIdentifier),
139+
lineNumber(lineNumber),
140+
columnNumber(columnNumber) {}
141+
std::string bufferIdentifier;
142+
uint32_t lineNumber;
143+
uint32_t columnNumber;
144+
};
145+
146+
ImportStatementInfo(std::string importIdentifier)
147+
: importLocations(),
148+
importIdentifier(importIdentifier) {}
149+
150+
ImportStatementInfo(std::string importIdentifier,
151+
ImportDiagnosticLocationInfo location)
152+
: importLocations({location}),
153+
importIdentifier(importIdentifier) {}
154+
155+
void addImportLocation(ImportDiagnosticLocationInfo location) {
156+
importLocations.push_back(location);
157+
}
158+
159+
// Buffer, line & column number of the import statement
160+
SmallVector<ImportDiagnosticLocationInfo, 4> importLocations;
161+
// Imported module string. e.g. "Foo.Bar" in 'import Foo.Bar'
162+
std::string importIdentifier;
163+
};
164+
132165
/// Base class for the variant storage of ModuleDependencyInfo.
133166
///
134167
/// This class is mostly an implementation detail for \c ModuleDependencyInfo.
@@ -142,8 +175,8 @@ class ModuleDependencyInfoStorageBase {
142175
resolved(false), finalized(false) {}
143176

144177
ModuleDependencyInfoStorageBase(ModuleDependencyKind dependencyKind,
145-
const std::vector<std::string> &moduleImports,
146-
const std::vector<std::string> &optionalModuleImports,
178+
const std::vector<ImportStatementInfo> &moduleImports,
179+
const std::vector<ImportStatementInfo> &optionalModuleImports,
147180
StringRef moduleCacheKey = "")
148181
: dependencyKind(dependencyKind), moduleImports(moduleImports),
149182
optionalModuleImports(optionalModuleImports),
@@ -154,12 +187,12 @@ class ModuleDependencyInfoStorageBase {
154187
virtual ~ModuleDependencyInfoStorageBase();
155188

156189
/// The set of modules on which this module depends.
157-
std::vector<std::string> moduleImports;
190+
std::vector<ImportStatementInfo> moduleImports;
158191

159192
/// The set of modules which constitute optional module
160193
/// dependencies for this module, such as `@_implementationOnly`
161194
/// or `internal` imports.
162-
std::vector<std::string> optionalModuleImports;
195+
std::vector<ImportStatementInfo> optionalModuleImports;
163196

164197
/// The set of modules on which this module depends, resolved
165198
/// to Module IDs, qualified by module kind: Swift, Clang, etc.
@@ -328,8 +361,8 @@ class SwiftBinaryModuleDependencyStorage : public ModuleDependencyInfoStorageBas
328361
SwiftBinaryModuleDependencyStorage(const std::string &compiledModulePath,
329362
const std::string &moduleDocPath,
330363
const std::string &sourceInfoPath,
331-
const std::vector<std::string> &moduleImports,
332-
const std::vector<std::string> &optionalModuleImports,
364+
const std::vector<ImportStatementInfo> &moduleImports,
365+
const std::vector<ImportStatementInfo> &optionalModuleImports,
333366
const std::string &headerImport,
334367
const bool isFramework,
335368
const std::string &moduleCacheKey)
@@ -516,8 +549,8 @@ class ModuleDependencyInfo {
516549
const std::string &compiledModulePath,
517550
const std::string &moduleDocPath,
518551
const std::string &sourceInfoPath,
519-
const std::vector<std::string> &moduleImports,
520-
const std::vector<std::string> &optionalModuleImports,
552+
const std::vector<ImportStatementInfo> &moduleImports,
553+
const std::vector<ImportStatementInfo> &optionalModuleImports,
521554
const std::string &headerImport,
522555
bool isFramework, const std::string &moduleCacheKey) {
523556
return ModuleDependencyInfo(
@@ -567,12 +600,12 @@ class ModuleDependencyInfo {
567600
}
568601

569602
/// Retrieve the module-level imports.
570-
ArrayRef<std::string> getModuleImports() const {
603+
ArrayRef<ImportStatementInfo> getModuleImports() const {
571604
return storage->moduleImports;
572605
}
573606

574607
/// Retrieve the module-level optional imports.
575-
ArrayRef<std::string> getOptionalModuleImports() const {
608+
ArrayRef<ImportStatementInfo> getOptionalModuleImports() const {
576609
return storage->optionalModuleImports;
577610
}
578611

@@ -749,31 +782,24 @@ class ModuleDependencyInfo {
749782
void addOptionalModuleImport(StringRef module,
750783
llvm::StringSet<> *alreadyAddedModules = nullptr);
751784

785+
/// Add all of the module imports in the given source
786+
/// file to the set of module imports.
787+
void addModuleImports(const SourceFile &sourceFile,
788+
llvm::StringSet<> &alreadyAddedModules,
789+
const SourceManager *sourceManager);
752790

753791
/// Add a dependency on the given module, if it was not already in the set.
754-
void addModuleImport(StringRef module,
755-
llvm::StringSet<> *alreadyAddedModules = nullptr);
792+
void addModuleImport(ImportPath::Module module,
793+
llvm::StringSet<> *alreadyAddedModules = nullptr,
794+
const SourceManager *sourceManager = nullptr,
795+
SourceLoc sourceLocation = SourceLoc());
756796

757797
/// Add a dependency on the given module, if it was not already in the set.
758-
void addModuleImport(ImportPath::Module module,
759-
llvm::StringSet<> *alreadyAddedModules = nullptr) {
760-
std::string ImportedModuleName = module.front().Item.str().str();
761-
auto submodulePath = module.getSubmodulePath();
762-
if (submodulePath.size() > 0 && !submodulePath[0].Item.empty()) {
763-
auto submoduleComponent = submodulePath[0];
764-
// Special case: a submodule named "Foo.Private" can be moved to a top-level
765-
// module named "Foo_Private". ClangImporter has special support for this.
766-
if (submoduleComponent.Item.str() == "Private")
767-
addOptionalModuleImport(ImportedModuleName + "_Private", alreadyAddedModules);
768-
}
769-
770-
addModuleImport(ImportedModuleName, alreadyAddedModules);
771-
}
798+
void addModuleImport(StringRef module,
799+
llvm::StringSet<> *alreadyAddedModules = nullptr,
800+
const SourceManager *sourceManager = nullptr,
801+
SourceLoc sourceLocation = SourceLoc());
772802

773-
/// Add all of the module imports in the given source
774-
/// file to the set of module imports.
775-
void addModuleImport(const SourceFile &sf,
776-
llvm::StringSet<> &alreadyAddedModules);
777803
/// Add a kind-qualified module dependency ID to the set of
778804
/// module dependencies.
779805
void addModuleDependency(ModuleDependencyID dependencyID);

include/swift/DependencyScan/DependencyScanImpl.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
namespace swift {
2222
namespace dependencies {
2323
class DependencyScanningTool;
24-
}
24+
} // namespace dependencies
2525
} // namespace swift
2626

2727
struct swiftscan_dependency_graph_s {
@@ -212,7 +212,13 @@ struct swiftscan_scan_invocation_s {
212212
struct swiftscan_diagnostic_info_s {
213213
swiftscan_string_ref_t message;
214214
swiftscan_diagnostic_severity_t severity;
215-
// TODO: SourceLoc
215+
swiftscan_source_location_t source_location;
216+
};
217+
218+
struct swiftscan_source_location_s {
219+
swiftscan_string_ref_t buffer_identifier;
220+
uint32_t line_number;
221+
uint32_t column_number;
216222
};
217223

218224
#endif // SWIFT_C_DEPENDENCY_SCAN_IMPL_H

include/swift/DependencyScan/DependencyScanningTool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class DependencyScanDiagnosticCollector : public DiagnosticConsumer {
3737
struct ScannerDiagnosticInfo {
3838
std::string Message;
3939
llvm::SourceMgr::DiagKind Severity;
40+
std::optional<ImportStatementInfo::ImportDiagnosticLocationInfo> ImportLocation;
4041
};
4142

4243
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;

include/swift/DependencyScan/StringUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift-c/CommonString/CommonString.h"
1414
#include "llvm/ADT/StringRef.h"
1515
#include "llvm/ADT/StringSet.h"
16+
#include "llvm/ADT/SmallVector.h"
1617
#include <string>
1718
#include <vector>
1819

lib/AST/ModuleDependencies.cpp

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,62 @@ void ModuleDependencyInfo::addOptionalModuleImport(
121121
}
122122

123123
void ModuleDependencyInfo::addModuleImport(
124-
StringRef module, llvm::StringSet<> *alreadyAddedModules) {
125-
if (!alreadyAddedModules || alreadyAddedModules->insert(module).second)
126-
storage->moduleImports.push_back(module.str());
124+
StringRef module, llvm::StringSet<> *alreadyAddedModules,
125+
const SourceManager *sourceManager, SourceLoc sourceLocation) {
126+
auto sourceLocToDiagnosticLocInfo =
127+
[&sourceManager](SourceLoc sourceLocation) {
128+
auto lineAndColumnNumbers =
129+
sourceManager->getLineAndColumnInBuffer(sourceLocation);
130+
return ImportStatementInfo::ImportDiagnosticLocationInfo(
131+
sourceManager->getDisplayNameForLoc(sourceLocation).str(),
132+
lineAndColumnNumbers.first, lineAndColumnNumbers.second);
133+
};
134+
bool validSourceLocation = sourceManager && sourceLocation.isValid() &&
135+
sourceManager->isOwning(sourceLocation);
136+
137+
if (alreadyAddedModules && alreadyAddedModules->contains(module)) {
138+
if (validSourceLocation) {
139+
// Find a prior import of this module and add import location
140+
for (auto &existingImport : storage->moduleImports) {
141+
if (existingImport.importIdentifier == module) {
142+
existingImport.addImportLocation(
143+
sourceLocToDiagnosticLocInfo(sourceLocation));
144+
break;
145+
}
146+
}
147+
}
148+
} else {
149+
if (validSourceLocation)
150+
storage->moduleImports.push_back(ImportStatementInfo(
151+
module.str(), sourceLocToDiagnosticLocInfo(sourceLocation)));
152+
else
153+
storage->moduleImports.push_back(ImportStatementInfo(module.str()));
154+
}
127155
}
128156

129157
void ModuleDependencyInfo::addModuleImport(
130-
const SourceFile &sf, llvm::StringSet<> &alreadyAddedModules) {
158+
ImportPath::Module module, llvm::StringSet<> *alreadyAddedModules,
159+
const SourceManager *sourceManager, SourceLoc sourceLocation) {
160+
std::string ImportedModuleName = module.front().Item.str().str();
161+
auto submodulePath = module.getSubmodulePath();
162+
if (submodulePath.size() > 0 && !submodulePath[0].Item.empty()) {
163+
auto submoduleComponent = submodulePath[0];
164+
// Special case: a submodule named "Foo.Private" can be moved to a top-level
165+
// module named "Foo_Private". ClangImporter has special support for this.
166+
if (submoduleComponent.Item.str() == "Private")
167+
addOptionalModuleImport(ImportedModuleName + "_Private", alreadyAddedModules);
168+
}
169+
170+
addModuleImport(ImportedModuleName, alreadyAddedModules,
171+
sourceManager, sourceLocation);
172+
}
173+
174+
void ModuleDependencyInfo::addModuleImports(
175+
const SourceFile &sourceFile, llvm::StringSet<> &alreadyAddedModules,
176+
const SourceManager *sourceManager) {
131177
// Add all of the module dependencies.
132178
SmallVector<Decl *, 32> decls;
133-
sf.getTopLevelDecls(decls);
179+
sourceFile.getTopLevelDecls(decls);
134180
for (auto decl : decls) {
135181
auto importDecl = dyn_cast<ImportDecl>(decl);
136182
if (!importDecl)
@@ -149,10 +195,12 @@ void ModuleDependencyInfo::addModuleImport(
149195

150196
// Ignore/diagnose tautological imports akin to import resolution
151197
if (!swift::dependencies::checkImportNotTautological(
152-
realPath, importDecl->getLoc(), sf, importDecl->isExported()))
198+
realPath, importDecl->getLoc(), sourceFile,
199+
importDecl->isExported()))
153200
continue;
154201

155-
addModuleImport(realPath, &alreadyAddedModules);
202+
addModuleImport(realPath, &alreadyAddedModules,
203+
sourceManager, importDecl->getLoc());
156204

157205
// Additionally, keep track of which dependencies of a Source
158206
// module are `@Testable`.
@@ -161,7 +209,7 @@ void ModuleDependencyInfo::addModuleImport(
161209
addTestableImport(realPath);
162210
}
163211

164-
auto fileName = sf.getFilename();
212+
auto fileName = sourceFile.getFilename();
165213
if (fileName.empty())
166214
return;
167215

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,14 @@ void DependencyScanDiagnosticCollector::addDiagnostic(
112112
DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString,
113113
Info.FormatArgs);
114114
auto Msg = SM.GetMessage(Info.Loc, SMKind, Text, Ranges, FixIts);
115-
Diagnostics.push_back(ScannerDiagnosticInfo{Msg.getMessage().str(), SMKind});
115+
116+
auto bufferIdentifier = SM.getDisplayNameForLoc(Info.Loc);
117+
auto lineAndColumnNumbers = SM.getLineAndColumnInBuffer(Info.Loc);
118+
auto importLocation = ImportStatementInfo::ImportDiagnosticLocationInfo(
119+
bufferIdentifier.str(), lineAndColumnNumbers.first,
120+
lineAndColumnNumbers.second);
121+
Diagnostics.push_back(
122+
ScannerDiagnosticInfo{Msg.getMessage().str(), SMKind, importLocation});
116123
}
117124

118125
void LockingDependencyScanDiagnosticCollector::addDiagnostic(

0 commit comments

Comments
 (0)