diff --git a/Sources/CSwiftScan/include/swiftscan_header.h b/Sources/CSwiftScan/include/swiftscan_header.h index 91de3f563..792f98ad5 100644 --- a/Sources/CSwiftScan/include/swiftscan_header.h +++ b/Sources/CSwiftScan/include/swiftscan_header.h @@ -18,7 +18,7 @@ #include #define SWIFTSCAN_VERSION_MAJOR 2 -#define SWIFTSCAN_VERSION_MINOR 1 +#define SWIFTSCAN_VERSION_MINOR 2 //=== Public Scanner Data Types -------------------------------------------===// @@ -43,6 +43,7 @@ typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t; typedef struct swiftscan_link_library_info_s *swiftscan_link_library_info_t; typedef struct swiftscan_dependency_graph_s *swiftscan_dependency_graph_t; typedef struct swiftscan_import_set_s *swiftscan_import_set_t; +typedef struct swiftscan_import_info_s *swiftscan_import_info_t; typedef struct swiftscan_diagnostic_info_s *swiftscan_diagnostic_info_t; typedef struct swiftscan_source_location_s *swiftscan_source_location_t; @@ -52,6 +53,13 @@ typedef enum { SWIFTSCAN_DIAGNOSTIC_SEVERITY_NOTE = 2, SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK = 3 } swiftscan_diagnostic_severity_t; +typedef enum { + SWIFTSCAN_ACCESS_LEVEL_PRIVATE = 0, + SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE = 1, + SWIFTSCAN_ACCESS_LEVEL_INTERNAL = 2, + SWIFTSCAN_ACCESS_LEVEL_PACKAGE = 3, + SWIFTSCAN_ACCESS_LEVEL_PUBLIC = 4 +} swiftscan_access_level_t; typedef struct { swiftscan_diagnostic_info_t *diagnostics; size_t count; @@ -64,6 +72,14 @@ typedef struct { swiftscan_link_library_info_t *link_libraries; size_t count; } swiftscan_link_library_set_t; +typedef struct { + swiftscan_import_info_t *imports; + size_t count; +} swiftscan_import_info_set_t; +typedef struct { + swiftscan_source_location_t *source_locations; + size_t count; +} swiftscan_source_location_set_t; //=== Scanner Invocation Specification ------------------------------------===// @@ -104,6 +120,8 @@ typedef struct { (*swiftscan_module_info_get_direct_dependencies)(swiftscan_dependency_info_t); swiftscan_link_library_set_t * (*swiftscan_module_info_get_link_libraries)(swiftscan_dependency_graph_t); + swiftscan_import_info_set_t * + (*swiftscan_module_info_get_imports)(swiftscan_dependency_graph_t); swiftscan_module_details_t (*swiftscan_module_info_get_details)(swiftscan_dependency_info_t); @@ -115,6 +133,14 @@ typedef struct { bool (*swiftscan_link_library_info_get_should_force_load)(swiftscan_link_library_info_t); + //=== Import Details Functions -------------------------------------------===// + swiftscan_source_location_set_t * + (*swiftscan_import_info_get_source_locations)(swiftscan_import_info_t info); + swiftscan_string_ref_t + (*swiftscan_import_info_get_identifier)(swiftscan_import_info_t info); + swiftscan_access_level_t + (*swiftscan_import_info_get_access_level)(swiftscan_import_info_t info); + //=== Dependency Module Info Details Functions ----------------------------===// swiftscan_dependency_info_kind_t (*swiftscan_module_detail_get_kind)(swiftscan_module_details_t); diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index e3c0da09f..5e64bde27 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -90,6 +90,29 @@ public struct LinkLibraryInfo: Codable, Hashable { public var shouldForceLoad: Bool } +/// Source 'import' +public struct ImportInfo : Codable, Hashable { + public enum ImportAccessLevel : Codable, Hashable { + case Private + case FilePrivate + case Internal + case Package + case Public + } + + public var importIdentifier: String + public var accessLevel: ImportAccessLevel + public var sourceLocations: [ScannerDiagnosticSourceLocation] + + @_spi(Testing) public init(importIdentifier: String, + accessLevel: ImportAccessLevel, + sourceLocations: [ScannerDiagnosticSourceLocation]) { + self.importIdentifier = importIdentifier + self.accessLevel = accessLevel + self.sourceLocations = sourceLocations + } +} + /// Details specific to Swift modules. public struct SwiftModuleDetails: Codable, Hashable { /// The module interface from which this module was built, if any. @@ -195,6 +218,9 @@ public struct ModuleInfo: Codable, Hashable { /// The set of libraries that need to be linked public var linkLibraries: [LinkLibraryInfo]? + /// The set of import details of this module + public var importInfos: [ImportInfo]? + /// Specific details of a particular kind of module. public var details: Details @@ -215,11 +241,13 @@ public struct ModuleInfo: Codable, Hashable { sourceFiles: [String]?, directDependencies: [ModuleDependencyId]?, linkLibraries: [LinkLibraryInfo]?, + importInfos: [ImportInfo]?, details: Details) { self.modulePath = modulePath self.sourceFiles = sourceFiles self.directDependencies = directDependencies self.linkLibraries = linkLibraries + self.importInfos = importInfos self.details = details } } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index e27efd600..9477cd524 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -137,6 +137,13 @@ public class InterModuleDependencyOracle { return swiftScan.supportsLinkLibraries } + @_spi(Testing) public func supportsImportInfos() throws -> Bool { + guard let swiftScan = swiftScanLibInstance else { + fatalError("Attempting to query supported scanner API with no scanner instance.") + } + return swiftScan.supportsImportInfos + } + @_spi(Testing) public func supportsSeparateImportOnlyDependencise() throws -> Bool { guard let swiftScan = swiftScanLibInstance else { fatalError("Attempting to query supported scanner API with no scanner instance.") diff --git a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift index 347b1ed8f..73d52e90c 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -104,6 +104,22 @@ private extension SwiftScan { } } + var importInfos: [ImportInfo] = [] + if supportsImportInfos { + let importInfoSetRefOrNull = api.swiftscan_module_info_get_imports(moduleInfoRef) + guard let importInfoSetRef = importInfoSetRefOrNull else { + throw DependencyScanningError.missingField("dependency_graph.imports") + } + let importInfoRefArray = Array(UnsafeBufferPointer(start: importInfoSetRef.pointee.imports, + count: Int(importInfoSetRef.pointee.count))) + for importInfoRefOrNull in importInfoRefArray { + guard let importInfoRef = importInfoRefOrNull else { + throw DependencyScanningError.missingField("dependency_set_t.imports[_]") + } + importInfos.append(try constructImportInfo(from: importInfoRef)) + } + } + guard let moduleDetailsRef = api.swiftscan_module_info_get_details(moduleInfoRef) else { throw DependencyScanningError.missingField("modules[\(moduleId)].details") } @@ -113,6 +129,7 @@ private extension SwiftScan { return (moduleId, ModuleInfo(modulePath: modulePath, sourceFiles: sourceFiles, directDependencies: directDependencies, linkLibraries: linkLibraries, + importInfos: importInfos, details: details)) } @@ -122,6 +139,41 @@ private extension SwiftScan { shouldForceLoad: api.swiftscan_link_library_info_get_should_force_load(linkLibraryInfoRef)) } + func constructImportInfo(from importInfoRef: swiftscan_import_info_t) throws -> ImportInfo { + var sourceLocations : [ScannerDiagnosticSourceLocation] = [] + + let sourceLocationsRefOrNull = api.swiftscan_import_info_get_source_locations(importInfoRef) + guard let sourceLocationsRef = sourceLocationsRefOrNull else { + throw DependencyScanningError.missingField("import_info.source_locations") + } + let sourceLocationsRefArray = Array(UnsafeBufferPointer(start: sourceLocationsRef.pointee.source_locations, + count: Int(sourceLocationsRef.pointee.count))) + for sourceLocationRefOrNull in sourceLocationsRefArray { + guard let sourceLocationRef = sourceLocationRefOrNull else { + throw DependencyScanningError.missingField("import_info.source_locations[_]") + } + sourceLocations.append(try constructSourceLocation(from: sourceLocationRef)) + } + + let accessLevel = switch api.swiftscan_import_info_get_access_level(importInfoRef) { + case SWIFTSCAN_ACCESS_LEVEL_PRIVATE: ImportInfo.ImportAccessLevel.Private + case SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE: ImportInfo.ImportAccessLevel.FilePrivate + case SWIFTSCAN_ACCESS_LEVEL_INTERNAL: ImportInfo.ImportAccessLevel.Internal + case SWIFTSCAN_ACCESS_LEVEL_PACKAGE: ImportInfo.ImportAccessLevel.Package + case SWIFTSCAN_ACCESS_LEVEL_PUBLIC: ImportInfo.ImportAccessLevel.Public + default: ImportInfo.ImportAccessLevel.Public + } + + return ImportInfo(importIdentifier: try toSwiftString(api.swiftscan_import_info_get_identifier(importInfoRef)), + accessLevel: accessLevel, sourceLocations: sourceLocations) + } + + func constructSourceLocation(from sourceLocationRef: swiftscan_source_location_t) throws -> ScannerDiagnosticSourceLocation { + return ScannerDiagnosticSourceLocation(bufferIdentifier: try toSwiftString(api.swiftscan_source_location_get_buffer_identifier(sourceLocationRef)), + lineNumber: Int(api.swiftscan_source_location_get_line_number(sourceLocationRef)), + columnNumber: Int(api.swiftscan_source_location_get_column_number(sourceLocationRef))) + } + /// From a reference to a binary-format module info details object info returned by libSwiftScan, /// construct an instance of an `ModuleInfo`.Details as used by the driver. /// The object returned by libSwiftScan is a union so ensure to execute dependency-specific queries. diff --git a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift index c09aa50a2..609210ad4 100644 --- a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift +++ b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift @@ -72,13 +72,21 @@ public enum DependencyScanningError: LocalizedError, DiagnosticData, Equatable { } } -public struct ScannerDiagnosticSourceLocation : DiagnosticLocation { +public struct ScannerDiagnosticSourceLocation : DiagnosticLocation, Codable, Hashable { public var description: String { return "\(bufferIdentifier):\(lineNumber):\(columnNumber)" } public let bufferIdentifier: String public let lineNumber: Int public let columnNumber: Int + + @_spi(Testing) public init(bufferIdentifier: String, + lineNumber: Int, + columnNumber: Int) { + self.bufferIdentifier = bufferIdentifier + self.lineNumber = lineNumber + self.columnNumber = columnNumber + } } public struct ScannerDiagnosticPayload { @@ -318,6 +326,13 @@ private extension String { api.swiftscan_link_library_info_get_should_force_load != nil } + @_spi(Testing) public var supportsImportInfos : Bool { + return api.swiftscan_module_info_get_imports != nil && + api.swiftscan_import_info_get_source_locations != nil && + api.swiftscan_import_info_get_identifier != nil && + api.swiftscan_import_info_get_access_level != nil + } + internal func mapToDriverDiagnosticPayload(_ diagnosticSetRef: UnsafeMutablePointer) throws -> [ScannerDiagnosticPayload] { var result: [ScannerDiagnosticPayload] = [] let diagnosticRefArray = Array(UnsafeBufferPointer(start: diagnosticSetRef.pointee.diagnostics, @@ -573,6 +588,11 @@ private extension swiftscan_functions_t { self.swiftscan_link_library_info_get_is_framework = loadOptional("swiftscan_link_library_info_get_is_framework") self.swiftscan_link_library_info_get_should_force_load = loadOptional("swiftscan_link_library_info_get_should_force_load") + self.swiftscan_module_info_get_imports = loadOptional("swiftscan_module_info_get_imports") + self.swiftscan_import_info_get_source_locations = loadOptional("swiftscan_import_info_get_source_locations") + self.swiftscan_import_info_get_identifier = loadOptional("swiftscan_import_info_get_identifier") + self.swiftscan_import_info_get_access_level = loadOptional("swiftscan_import_info_get_access_level") + // Swift Overlay Dependencies self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies = loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies") diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 9057810e7..5550c94c2 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -500,6 +500,74 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + func testExplicitImportDetails() throws { + try withTemporaryDirectory { path in + let (_, _, toolchain, _) = try getDriverArtifactsForScanning() + + let main = path.appending(component: "testExplicitLinkLibraries.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + public import C; + internal import E; + private import G; + internal import C; + """ + ) + + let cHeadersPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "CHeaders") + let swiftModuleInterfacesPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "Swift") + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] + + let dependencyOracle = InterModuleDependencyOracle() + let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) + guard try dependencyOracle.supportsImportInfos() else { + throw XCTSkip("libSwiftScan does not support import details reporting.") + } + + let args = ["swiftc", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-explicit-module-build", + "-disable-implicit-concurrency-module-import", + "-disable-implicit-string-processing-module-import", + main.nativePathString(escaped: true)] + sdkArgumentsForTesting + var driver = try Driver(args: args) + let _ = try driver.planBuild() + let dependencyGraph = try XCTUnwrap(driver.explicitDependencyBuildPlanner?.dependencyGraph) + let mainModuleImports = try XCTUnwrap(dependencyGraph.mainModule.importInfos) + XCTAssertEqual(mainModuleImports.count, 5) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "Swift", + accessLevel: ImportInfo.ImportAccessLevel.Public, + sourceLocations: []))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "SwiftOnoneSupport", + accessLevel: ImportInfo.ImportAccessLevel.Public, + sourceLocations: []))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "C", + accessLevel: ImportInfo.ImportAccessLevel.Public, + sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 1, + columnNumber: 8), + ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 4, + columnNumber: 8)]))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "E", + accessLevel: ImportInfo.ImportAccessLevel.Internal, + sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 2, + columnNumber: 8)]))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "G", + accessLevel: ImportInfo.ImportAccessLevel.Private, + sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 3, + columnNumber: 8)]))) + } + } + func testExplicitLinkLibraries() throws { try withTemporaryDirectory { path in let (_, _, toolchain, _) = try getDriverArtifactsForScanning() @@ -521,7 +589,6 @@ final class ExplicitModuleBuildTests: XCTestCase { .appending(component: "Swift") let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] - // 2. Run a dependency scan to find the just-built module let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath)