Skip to content

Commit b738bc4

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 1257db7 commit b738bc4

16 files changed

+434
-163
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/// SWIFTSCAN_VERSION_MINOR should increase when there are API additions.
2626
/// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes.
2727
#define SWIFTSCAN_VERSION_MAJOR 0
28-
#define SWIFTSCAN_VERSION_MINOR 8
28+
#define SWIFTSCAN_VERSION_MINOR 9
2929

3030
SWIFTSCAN_BEGIN_DECLS
3131

@@ -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,22 @@ 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+
425441
//=== Scanner Cache Operations --------------------------------------------===//
426442
// The following operations expose an implementation detail of the dependency
427443
// 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: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,39 @@ namespace dependencies {
129129
bool);
130130
}
131131

132+
struct ScannerImportStatementInfo {
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+
ScannerImportStatementInfo(std::string importIdentifier)
147+
: importLocations(),
148+
importIdentifier(importIdentifier) {}
149+
150+
ScannerImportStatementInfo(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.
@@ -141,25 +174,27 @@ class ModuleDependencyInfoStorageBase {
141174
: dependencyKind(dependencyKind), moduleCacheKey(moduleCacheKey.str()),
142175
resolved(false), finalized(false) {}
143176

144-
ModuleDependencyInfoStorageBase(ModuleDependencyKind dependencyKind,
145-
const std::vector<std::string> &moduleImports,
146-
const std::vector<std::string> &optionalModuleImports,
147-
StringRef moduleCacheKey = "")
177+
ModuleDependencyInfoStorageBase(
178+
ModuleDependencyKind dependencyKind,
179+
const std::vector<ScannerImportStatementInfo> &moduleImports,
180+
const std::vector<ScannerImportStatementInfo> &optionalModuleImports,
181+
StringRef moduleCacheKey = "")
148182
: dependencyKind(dependencyKind), moduleImports(moduleImports),
149183
optionalModuleImports(optionalModuleImports),
150-
moduleCacheKey(moduleCacheKey.str()), resolved(false), finalized(false) {}
184+
moduleCacheKey(moduleCacheKey.str()), resolved(false),
185+
finalized(false) {}
151186

152187
virtual ModuleDependencyInfoStorageBase *clone() const = 0;
153188

154189
virtual ~ModuleDependencyInfoStorageBase();
155190

156191
/// The set of modules on which this module depends.
157-
std::vector<std::string> moduleImports;
192+
std::vector<ScannerImportStatementInfo> moduleImports;
158193

159194
/// The set of modules which constitute optional module
160195
/// dependencies for this module, such as `@_implementationOnly`
161196
/// or `internal` imports.
162-
std::vector<std::string> optionalModuleImports;
197+
std::vector<ScannerImportStatementInfo> optionalModuleImports;
163198

164199
/// The set of modules on which this module depends, resolved
165200
/// to Module IDs, qualified by module kind: Swift, Clang, etc.
@@ -320,21 +355,23 @@ class SwiftSourceModuleDependenciesStorage :
320355
}
321356
};
322357

323-
/// Describes the dependencies of a pre-built Swift module (with no .swiftinterface).
358+
/// Describes the dependencies of a pre-built Swift module (with no
359+
/// .swiftinterface).
324360
///
325361
/// This class is mostly an implementation detail for \c ModuleDependencyInfo.
326-
class SwiftBinaryModuleDependencyStorage : public ModuleDependencyInfoStorageBase {
362+
class SwiftBinaryModuleDependencyStorage
363+
: public ModuleDependencyInfoStorageBase {
327364
public:
328-
SwiftBinaryModuleDependencyStorage(const std::string &compiledModulePath,
329-
const std::string &moduleDocPath,
330-
const std::string &sourceInfoPath,
331-
const std::vector<std::string> &moduleImports,
332-
const std::vector<std::string> &optionalModuleImports,
333-
const std::string &headerImport,
334-
const bool isFramework,
335-
const std::string &moduleCacheKey)
365+
SwiftBinaryModuleDependencyStorage(
366+
const std::string &compiledModulePath, const std::string &moduleDocPath,
367+
const std::string &sourceInfoPath,
368+
const std::vector<ScannerImportStatementInfo> &moduleImports,
369+
const std::vector<ScannerImportStatementInfo> &optionalModuleImports,
370+
const std::string &headerImport, const bool isFramework,
371+
const std::string &moduleCacheKey)
336372
: ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary,
337-
moduleImports, optionalModuleImports, moduleCacheKey),
373+
moduleImports, optionalModuleImports,
374+
moduleCacheKey),
338375
compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath),
339376
sourceInfoPath(sourceInfoPath), headerImport(headerImport),
340377
isFramework(isFramework) {}
@@ -516,8 +553,8 @@ class ModuleDependencyInfo {
516553
const std::string &compiledModulePath,
517554
const std::string &moduleDocPath,
518555
const std::string &sourceInfoPath,
519-
const std::vector<std::string> &moduleImports,
520-
const std::vector<std::string> &optionalModuleImports,
556+
const std::vector<ScannerImportStatementInfo> &moduleImports,
557+
const std::vector<ScannerImportStatementInfo> &optionalModuleImports,
521558
const std::string &headerImport,
522559
bool isFramework, const std::string &moduleCacheKey) {
523560
return ModuleDependencyInfo(
@@ -567,12 +604,12 @@ class ModuleDependencyInfo {
567604
}
568605

569606
/// Retrieve the module-level imports.
570-
ArrayRef<std::string> getModuleImports() const {
607+
ArrayRef<ScannerImportStatementInfo> getModuleImports() const {
571608
return storage->moduleImports;
572609
}
573610

574611
/// Retrieve the module-level optional imports.
575-
ArrayRef<std::string> getOptionalModuleImports() const {
612+
ArrayRef<ScannerImportStatementInfo> getOptionalModuleImports() const {
576613
return storage->optionalModuleImports;
577614
}
578615

@@ -749,31 +786,24 @@ class ModuleDependencyInfo {
749786
void addOptionalModuleImport(StringRef module,
750787
llvm::StringSet<> *alreadyAddedModules = nullptr);
751788

789+
/// Add all of the module imports in the given source
790+
/// file to the set of module imports.
791+
void addModuleImports(const SourceFile &sourceFile,
792+
llvm::StringSet<> &alreadyAddedModules,
793+
const SourceManager *sourceManager);
752794

753795
/// 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);
796+
void addModuleImport(ImportPath::Module module,
797+
llvm::StringSet<> *alreadyAddedModules = nullptr,
798+
const SourceManager *sourceManager = nullptr,
799+
SourceLoc sourceLocation = SourceLoc());
756800

757801
/// 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-
}
802+
void addModuleImport(StringRef module,
803+
llvm::StringSet<> *alreadyAddedModules = nullptr,
804+
const SourceManager *sourceManager = nullptr,
805+
SourceLoc sourceLocation = SourceLoc());
772806

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);
777807
/// Add a kind-qualified module dependency ID to the set of
778808
/// module dependencies.
779809
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ class DependencyScanDiagnosticCollector : public DiagnosticConsumer {
3737
struct ScannerDiagnosticInfo {
3838
std::string Message;
3939
llvm::SourceMgr::DiagKind Severity;
40+
std::optional<ScannerImportStatementInfo::ImportDiagnosticLocationInfo> ImportLocation;
4041
};
42+
std::vector<ScannerDiagnosticInfo> Diagnostics;
4143

4244
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;
43-
std::vector<ScannerDiagnosticInfo> Diagnostics;
4445

4546
protected:
4647
virtual void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info);

lib/AST/ModuleDependencies.cpp

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,67 @@ 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 scannerImportLocToDiagnosticLocInfo =
127+
[&sourceManager](SourceLoc sourceLocation) {
128+
auto lineAndColumnNumbers =
129+
sourceManager->getLineAndColumnInBuffer(sourceLocation);
130+
return ScannerImportStatementInfo::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+
scannerImportLocToDiagnosticLocInfo(sourceLocation));
144+
break;
145+
}
146+
}
147+
}
148+
} else {
149+
if (alreadyAddedModules)
150+
alreadyAddedModules->insert(module);
151+
152+
if (validSourceLocation)
153+
storage->moduleImports.push_back(ScannerImportStatementInfo(
154+
module.str(), scannerImportLocToDiagnosticLocInfo(sourceLocation)));
155+
else
156+
storage->moduleImports.push_back(
157+
ScannerImportStatementInfo(module.str()));
158+
}
127159
}
128160

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

150201
// Ignore/diagnose tautological imports akin to import resolution
151202
if (!swift::dependencies::checkImportNotTautological(
152-
realPath, importDecl->getLoc(), sf, importDecl->isExported()))
203+
realPath, importDecl->getLoc(), sourceFile,
204+
importDecl->isExported()))
153205
continue;
154206

155-
addModuleImport(realPath, &alreadyAddedModules);
207+
addModuleImport(realPath, &alreadyAddedModules,
208+
sourceManager, importDecl->getLoc());
156209

157210
// Additionally, keep track of which dependencies of a Source
158211
// module are `@Testable`.
@@ -161,7 +214,7 @@ void ModuleDependencyInfo::addModuleImport(
161214
addTestableImport(realPath);
162215
}
163216

164-
auto fileName = sf.getFilename();
217+
auto fileName = sourceFile.getFilename();
165218
if (fileName.empty())
166219
return;
167220

0 commit comments

Comments
 (0)