diff --git a/Sources/CSwiftScan/include/swiftscan_header.h b/Sources/CSwiftScan/include/swiftscan_header.h index f6e8e00cd..905d924cd 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 -------------------------------------------===// @@ -44,6 +44,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; @@ -53,6 +54,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; @@ -65,6 +73,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 ------------------------------------===// @@ -105,6 +121,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); @@ -116,6 +134,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/CommonDependencyOperations.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift index ec5c5a4eb..1648ed201 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -35,6 +35,7 @@ import protocol TSCBasic.FileSystem sourceFiles: [], directDependencies: currentInfo.directDependencies, linkLibraries: currentInfo.linkLibraries, + importInfos: nil, details: .swiftPrebuiltExternal(newExternalModuleDetails)) Self.replaceModule(originalId: swiftModuleId, replacementId: prebuiltModuleId, replacementInfo: newInfo, in: &modules) @@ -48,6 +49,7 @@ import protocol TSCBasic.FileSystem sourceFiles: [], directDependencies: currentPrebuiltInfo.directDependencies, linkLibraries: currentPrebuiltInfo.linkLibraries, + importInfos: nil, details: .swiftPrebuiltExternal(newExternalModuleDetails)) Self.replaceModule(originalId: prebuiltModuleId, replacementId: prebuiltModuleId, replacementInfo: newInfo, in: &modules) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index 40282c160..378951ddc 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -101,6 +101,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. @@ -212,6 +235,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 @@ -236,11 +262,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 06e42cabf..5929b5313 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -147,6 +147,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 f55fef5be..9c4d167f1 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -104,6 +104,22 @@ private extension SwiftScan { } } + var importInfos: [ImportInfo]? = nil + 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))) + importInfos = try importInfoRefArray.map { importInfoRefOrNull in + guard let importInfoRef = importInfoRefOrNull else { + throw DependencyScanningError.missingField("dependency_set_t.imports[_]") + } + return 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 804a1e8d6..6210c86b3 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 53b74cbc7..f837f4187 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", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-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: 15), + ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 4, + columnNumber: 17)]))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "E", + accessLevel: ImportInfo.ImportAccessLevel.Internal, + sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 2, + columnNumber: 17)]))) + XCTAssertTrue(mainModuleImports.contains(ImportInfo(importIdentifier: "G", + accessLevel: ImportInfo.ImportAccessLevel.Private, + sourceLocations: [ScannerDiagnosticSourceLocation(bufferIdentifier: main.nativePathString(escaped: true), + lineNumber: 3, + columnNumber: 16)]))) + } + } + 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)