Skip to content

Commit 6f486c4

Browse files
committed
[Explicit Module Builds] Add support for libSwiftScan API to query source import details
1 parent 4f82cbd commit 6f486c4

File tree

6 files changed

+203
-3
lines changed

6 files changed

+203
-3
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <stdint.h>
1919

2020
#define SWIFTSCAN_VERSION_MAJOR 2
21-
#define SWIFTSCAN_VERSION_MINOR 1
21+
#define SWIFTSCAN_VERSION_MINOR 2
2222

2323
//=== Public Scanner Data Types -------------------------------------------===//
2424

@@ -44,6 +44,7 @@ typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t;
4444
typedef struct swiftscan_link_library_info_s *swiftscan_link_library_info_t;
4545
typedef struct swiftscan_dependency_graph_s *swiftscan_dependency_graph_t;
4646
typedef struct swiftscan_import_set_s *swiftscan_import_set_t;
47+
typedef struct swiftscan_import_info_s *swiftscan_import_info_t;
4748
typedef struct swiftscan_diagnostic_info_s *swiftscan_diagnostic_info_t;
4849
typedef struct swiftscan_source_location_s *swiftscan_source_location_t;
4950

@@ -53,6 +54,13 @@ typedef enum {
5354
SWIFTSCAN_DIAGNOSTIC_SEVERITY_NOTE = 2,
5455
SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK = 3
5556
} swiftscan_diagnostic_severity_t;
57+
typedef enum {
58+
SWIFTSCAN_ACCESS_LEVEL_PRIVATE = 0,
59+
SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE = 1,
60+
SWIFTSCAN_ACCESS_LEVEL_INTERNAL = 2,
61+
SWIFTSCAN_ACCESS_LEVEL_PACKAGE = 3,
62+
SWIFTSCAN_ACCESS_LEVEL_PUBLIC = 4
63+
} swiftscan_access_level_t;
5664
typedef struct {
5765
swiftscan_diagnostic_info_t *diagnostics;
5866
size_t count;
@@ -65,6 +73,14 @@ typedef struct {
6573
swiftscan_link_library_info_t *link_libraries;
6674
size_t count;
6775
} swiftscan_link_library_set_t;
76+
typedef struct {
77+
swiftscan_import_info_t *imports;
78+
size_t count;
79+
} swiftscan_import_info_set_t;
80+
typedef struct {
81+
swiftscan_source_location_t *source_locations;
82+
size_t count;
83+
} swiftscan_source_location_set_t;
6884

6985
//=== Scanner Invocation Specification ------------------------------------===//
7086

@@ -105,6 +121,8 @@ typedef struct {
105121
(*swiftscan_module_info_get_direct_dependencies)(swiftscan_dependency_info_t);
106122
swiftscan_link_library_set_t *
107123
(*swiftscan_module_info_get_link_libraries)(swiftscan_dependency_graph_t);
124+
swiftscan_import_info_set_t *
125+
(*swiftscan_module_info_get_imports)(swiftscan_dependency_graph_t);
108126
swiftscan_module_details_t
109127
(*swiftscan_module_info_get_details)(swiftscan_dependency_info_t);
110128

@@ -116,6 +134,14 @@ typedef struct {
116134
bool
117135
(*swiftscan_link_library_info_get_should_force_load)(swiftscan_link_library_info_t);
118136

137+
//=== Import Details Functions -------------------------------------------===//
138+
swiftscan_source_location_set_t *
139+
(*swiftscan_import_info_get_source_locations)(swiftscan_import_info_t info);
140+
swiftscan_string_ref_t
141+
(*swiftscan_import_info_get_identifier)(swiftscan_import_info_t info);
142+
swiftscan_access_level_t
143+
(*swiftscan_import_info_get_access_level)(swiftscan_import_info_t info);
144+
119145
//=== Dependency Module Info Details Functions ----------------------------===//
120146
swiftscan_dependency_info_kind_t
121147
(*swiftscan_module_detail_get_kind)(swiftscan_module_details_t);

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ public struct LinkLibraryInfo: Codable, Hashable {
101101
public var shouldForceLoad: Bool
102102
}
103103

104+
/// Source 'import'
105+
public struct ImportInfo : Codable, Hashable {
106+
public enum ImportAccessLevel : Codable, Hashable {
107+
case Private
108+
case FilePrivate
109+
case Internal
110+
case Package
111+
case Public
112+
}
113+
114+
public var importIdentifier: String
115+
public var accessLevel: ImportAccessLevel
116+
public var sourceLocations: [ScannerDiagnosticSourceLocation]
117+
118+
@_spi(Testing) public init(importIdentifier: String,
119+
accessLevel: ImportAccessLevel,
120+
sourceLocations: [ScannerDiagnosticSourceLocation]) {
121+
self.importIdentifier = importIdentifier
122+
self.accessLevel = accessLevel
123+
self.sourceLocations = sourceLocations
124+
}
125+
}
126+
104127
/// Details specific to Swift modules.
105128
public struct SwiftModuleDetails: Codable, Hashable {
106129
/// The module interface from which this module was built, if any.
@@ -212,6 +235,9 @@ public struct ModuleInfo: Codable, Hashable {
212235
/// The set of libraries that need to be linked
213236
public var linkLibraries: [LinkLibraryInfo]?
214237

238+
/// The set of import details of this module
239+
public var importInfos: [ImportInfo]?
240+
215241
/// Specific details of a particular kind of module.
216242
public var details: Details
217243

@@ -236,11 +262,13 @@ public struct ModuleInfo: Codable, Hashable {
236262
sourceFiles: [String]?,
237263
directDependencies: [ModuleDependencyId]?,
238264
linkLibraries: [LinkLibraryInfo]?,
265+
importInfos: [ImportInfo]?,
239266
details: Details) {
240267
self.modulePath = modulePath
241268
self.sourceFiles = sourceFiles
242269
self.directDependencies = directDependencies
243270
self.linkLibraries = linkLibraries
271+
self.importInfos = importInfos
244272
self.details = details
245273
}
246274
}

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ public class InterModuleDependencyOracle {
147147
return swiftScan.supportsLinkLibraries
148148
}
149149

150+
@_spi(Testing) public func supportsImportInfos() throws -> Bool {
151+
guard let swiftScan = swiftScanLibInstance else {
152+
fatalError("Attempting to query supported scanner API with no scanner instance.")
153+
}
154+
return swiftScan.supportsImportInfos
155+
}
156+
150157
@_spi(Testing) public func supportsSeparateImportOnlyDependencise() throws -> Bool {
151158
guard let swiftScan = swiftScanLibInstance else {
152159
fatalError("Attempting to query supported scanner API with no scanner instance.")

Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,22 @@ private extension SwiftScan {
104104
}
105105
}
106106

107+
var importInfos: [ImportInfo] = []
108+
if supportsImportInfos {
109+
let importInfoSetRefOrNull = api.swiftscan_module_info_get_imports(moduleInfoRef)
110+
guard let importInfoSetRef = importInfoSetRefOrNull else {
111+
throw DependencyScanningError.missingField("dependency_graph.imports")
112+
}
113+
let importInfoRefArray = Array(UnsafeBufferPointer(start: importInfoSetRef.pointee.imports,
114+
count: Int(importInfoSetRef.pointee.count)))
115+
for importInfoRefOrNull in importInfoRefArray {
116+
guard let importInfoRef = importInfoRefOrNull else {
117+
throw DependencyScanningError.missingField("dependency_set_t.imports[_]")
118+
}
119+
importInfos.append(try constructImportInfo(from: importInfoRef))
120+
}
121+
}
122+
107123
guard let moduleDetailsRef = api.swiftscan_module_info_get_details(moduleInfoRef) else {
108124
throw DependencyScanningError.missingField("modules[\(moduleId)].details")
109125
}
@@ -113,6 +129,7 @@ private extension SwiftScan {
113129
return (moduleId, ModuleInfo(modulePath: modulePath, sourceFiles: sourceFiles,
114130
directDependencies: directDependencies,
115131
linkLibraries: linkLibraries,
132+
importInfos: importInfos,
116133
details: details))
117134
}
118135

@@ -122,6 +139,41 @@ private extension SwiftScan {
122139
shouldForceLoad: api.swiftscan_link_library_info_get_should_force_load(linkLibraryInfoRef))
123140
}
124141

142+
func constructImportInfo(from importInfoRef: swiftscan_import_info_t) throws -> ImportInfo {
143+
var sourceLocations : [ScannerDiagnosticSourceLocation] = []
144+
145+
let sourceLocationsRefOrNull = api.swiftscan_import_info_get_source_locations(importInfoRef)
146+
guard let sourceLocationsRef = sourceLocationsRefOrNull else {
147+
throw DependencyScanningError.missingField("import_info.source_locations")
148+
}
149+
let sourceLocationsRefArray = Array(UnsafeBufferPointer(start: sourceLocationsRef.pointee.source_locations,
150+
count: Int(sourceLocationsRef.pointee.count)))
151+
for sourceLocationRefOrNull in sourceLocationsRefArray {
152+
guard let sourceLocationRef = sourceLocationRefOrNull else {
153+
throw DependencyScanningError.missingField("import_info.source_locations[_]")
154+
}
155+
sourceLocations.append(try constructSourceLocation(from: sourceLocationRef))
156+
}
157+
158+
let accessLevel = switch api.swiftscan_import_info_get_access_level(importInfoRef) {
159+
case SWIFTSCAN_ACCESS_LEVEL_PRIVATE: ImportInfo.ImportAccessLevel.Private
160+
case SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE: ImportInfo.ImportAccessLevel.FilePrivate
161+
case SWIFTSCAN_ACCESS_LEVEL_INTERNAL: ImportInfo.ImportAccessLevel.Internal
162+
case SWIFTSCAN_ACCESS_LEVEL_PACKAGE: ImportInfo.ImportAccessLevel.Package
163+
case SWIFTSCAN_ACCESS_LEVEL_PUBLIC: ImportInfo.ImportAccessLevel.Public
164+
default: ImportInfo.ImportAccessLevel.Public
165+
}
166+
167+
return ImportInfo(importIdentifier: try toSwiftString(api.swiftscan_import_info_get_identifier(importInfoRef)),
168+
accessLevel: accessLevel, sourceLocations: sourceLocations)
169+
}
170+
171+
func constructSourceLocation(from sourceLocationRef: swiftscan_source_location_t) throws -> ScannerDiagnosticSourceLocation {
172+
return ScannerDiagnosticSourceLocation(bufferIdentifier: try toSwiftString(api.swiftscan_source_location_get_buffer_identifier(sourceLocationRef)),
173+
lineNumber: Int(api.swiftscan_source_location_get_line_number(sourceLocationRef)),
174+
columnNumber: Int(api.swiftscan_source_location_get_column_number(sourceLocationRef)))
175+
}
176+
125177
/// From a reference to a binary-format module info details object info returned by libSwiftScan,
126178
/// construct an instance of an `ModuleInfo`.Details as used by the driver.
127179
/// The object returned by libSwiftScan is a union so ensure to execute dependency-specific queries.

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,21 @@ public enum DependencyScanningError: LocalizedError, DiagnosticData, Equatable {
7272
}
7373
}
7474

75-
public struct ScannerDiagnosticSourceLocation : DiagnosticLocation {
75+
public struct ScannerDiagnosticSourceLocation : DiagnosticLocation, Codable, Hashable {
7676
public var description: String {
7777
return "\(bufferIdentifier):\(lineNumber):\(columnNumber)"
7878
}
7979
public let bufferIdentifier: String
8080
public let lineNumber: Int
8181
public let columnNumber: Int
82+
83+
@_spi(Testing) public init(bufferIdentifier: String,
84+
lineNumber: Int,
85+
columnNumber: Int) {
86+
self.bufferIdentifier = bufferIdentifier
87+
self.lineNumber = lineNumber
88+
self.columnNumber = columnNumber
89+
}
8290
}
8391

8492
public struct ScannerDiagnosticPayload {
@@ -318,6 +326,13 @@ private extension String {
318326
api.swiftscan_link_library_info_get_should_force_load != nil
319327
}
320328

329+
@_spi(Testing) public var supportsImportInfos : Bool {
330+
return api.swiftscan_module_info_get_imports != nil &&
331+
api.swiftscan_import_info_get_source_locations != nil &&
332+
api.swiftscan_import_info_get_identifier != nil &&
333+
api.swiftscan_import_info_get_access_level != nil
334+
}
335+
321336
internal func mapToDriverDiagnosticPayload(_ diagnosticSetRef: UnsafeMutablePointer<swiftscan_diagnostic_set_t>) throws -> [ScannerDiagnosticPayload] {
322337
var result: [ScannerDiagnosticPayload] = []
323338
let diagnosticRefArray = Array(UnsafeBufferPointer(start: diagnosticSetRef.pointee.diagnostics,
@@ -573,6 +588,11 @@ private extension swiftscan_functions_t {
573588
self.swiftscan_link_library_info_get_is_framework = loadOptional("swiftscan_link_library_info_get_is_framework")
574589
self.swiftscan_link_library_info_get_should_force_load = loadOptional("swiftscan_link_library_info_get_should_force_load")
575590

591+
self.swiftscan_module_info_get_imports = loadOptional("swiftscan_module_info_get_imports")
592+
self.swiftscan_import_info_get_source_locations = loadOptional("swiftscan_import_info_get_source_locations")
593+
self.swiftscan_import_info_get_identifier = loadOptional("swiftscan_import_info_get_identifier")
594+
self.swiftscan_import_info_get_access_level = loadOptional("swiftscan_import_info_get_access_level")
595+
576596
// Swift Overlay Dependencies
577597
self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies =
578598
loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies")

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,74 @@ final class ExplicitModuleBuildTests: XCTestCase {
500500
}
501501
}
502502

503+
func testExplicitImportDetails() throws {
504+
try withTemporaryDirectory { path in
505+
let (_, _, toolchain, _) = try getDriverArtifactsForScanning()
506+
507+
let main = path.appending(component: "testExplicitLinkLibraries.swift")
508+
try localFileSystem.writeFileContents(main, bytes:
509+
"""
510+
public import C;
511+
internal import E;
512+
private import G;
513+
internal import C;
514+
"""
515+
)
516+
517+
let cHeadersPath: AbsolutePath =
518+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
519+
.appending(component: "CHeaders")
520+
let swiftModuleInterfacesPath: AbsolutePath =
521+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
522+
.appending(component: "Swift")
523+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
524+
525+
let dependencyOracle = InterModuleDependencyOracle()
526+
let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib())
527+
try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath)
528+
guard try dependencyOracle.supportsImportInfos() else {
529+
throw XCTSkip("libSwiftScan does not support import details reporting.")
530+
}
531+
532+
let args = ["swiftc",
533+
"-I", cHeadersPath.nativePathString(escaped: true),
534+
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
535+
"-explicit-module-build",
536+
"-disable-implicit-concurrency-module-import",
537+
"-disable-implicit-string-processing-module-import",
538+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting
539+
var driver = try Driver(args: args)
540+
let _ = try driver.planBuild()
541+
let dependencyGraph = try XCTUnwrap(driver.explicitDependencyBuildPlanner?.dependencyGraph)
542+
let mainModuleImports = try XCTUnwrap(dependencyGraph.mainModule.importInfos)
543+
XCTAssertEqual(mainModuleImports.count, 5)
544+
XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "Swift",
545+
accessLevel: ImportInfo.ImportAccessLevel.Public,
546+
sourceLocations: [])))
547+
XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "SwiftOnoneSupport",
548+
accessLevel: ImportInfo.ImportAccessLevel.Public,
549+
sourceLocations: [])))
550+
XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "C",
551+
accessLevel: ImportInfo.ImportAccessLevel.Public,
552+
sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true),
553+
lineNumber: 1,
554+
columnNumber: 8),
555+
ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true),
556+
lineNumber: 4,
557+
columnNumber: 8)])))
558+
XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "E",
559+
accessLevel: ImportInfo.ImportAccessLevel.Internal,
560+
sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true),
561+
lineNumber: 2,
562+
columnNumber: 8)])))
563+
XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "G",
564+
accessLevel: ImportInfo.ImportAccessLevel.Private,
565+
sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true),
566+
lineNumber: 3,
567+
columnNumber: 8)])))
568+
}
569+
}
570+
503571
func testExplicitLinkLibraries() throws {
504572
try withTemporaryDirectory { path in
505573
let (_, _, toolchain, _) = try getDriverArtifactsForScanning()
@@ -521,7 +589,6 @@ final class ExplicitModuleBuildTests: XCTestCase {
521589
.appending(component: "Swift")
522590
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
523591

524-
// 2. Run a dependency scan to find the just-built module
525592
let dependencyOracle = InterModuleDependencyOracle()
526593
let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib())
527594
try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath)

0 commit comments

Comments
 (0)