From 3b2c72d32ae1e3c26215c5e6ed1d2fd067a1b5f2 Mon Sep 17 00:00:00 2001 From: caggiulio Date: Thu, 24 Sep 2026 16:36:57 +0200 Subject: [PATCH 1/3] Fixes on missing filepath for extension in finalize --- Sources/JExtractSwiftLib/Swift2Java.swift | 6 +++++- .../SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift | 9 +++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 6952f3b1e..e07338101 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -201,18 +201,22 @@ public func collectAllFiles(suffix: String, in inputPaths: [URL], log: Logger) - ] for path in inputPaths { + var filesInPath: OrderedSet = [] do { try collectFilesFromPath( path, suffix: suffix, fileManager: fileManager, resourceKeys: resourceKeys, - into: &allFiles, + into: &filesInPath, log: log ) } catch { log.trace("Failed to collect paths in: \(path), skipping.") } + // Directory enumeration order is filesystem-dependent (e.g. hash-ordered on APFS); + // sort so that extraction output is deterministic across machines. + allFiles.append(contentsOf: filesInPath.sorted { $0.path < $1.path }) } return allFiles diff --git a/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 08bc4e146..62805fa41 100644 --- a/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -27,8 +27,9 @@ package struct SwiftParsedModuleSymbolTableBuilder { /// The build configuration used to resolve #if conditional compilation blocks. package let buildConfig: any BuildConfiguration - /// Extension decls their extended type hasn't been resolved. - package var unresolvedExtensions: [ExtensionDeclSyntax] + /// Extension decls their extended type hasn't been resolved, paired with + /// the path of the source file they were declared in. + package var unresolvedExtensions: [(node: ExtensionDeclSyntax, sourceFilePath: String)] package init( moduleName: String, @@ -213,7 +214,7 @@ extension SwiftParsedModuleSymbolTableBuilder { sourceFilePath: String ) { if !self.tryHandle(extension: node, sourceFilePath: sourceFilePath) { - self.unresolvedExtensions.append(node) + self.unresolvedExtensions.append((node: node, sourceFilePath: sourceFilePath)) } } @@ -273,7 +274,7 @@ extension SwiftParsedModuleSymbolTableBuilder { while !unresolvedExtensions.isEmpty { var extensions = self.unresolvedExtensions extensions.removeAll(where: { - self.tryHandle(extension: $0, sourceFilePath: "FIXME_MISSING_FILEPATH.swift") // FIXME: missing filepath here in finalize + self.tryHandle(extension: $0.node, sourceFilePath: $0.sourceFilePath) }) // If we didn't resolve anything, we're done. From cc5119df113cacc1e8ac3a70f15a9683cc8ef5ad Mon Sep 17 00:00:00 2001 From: caggiulio Date: Thu, 24 Sep 2026 20:45:44 +0200 Subject: [PATCH 2/3] Adds test --- .../SwiftSymbolTableTests.swift | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Tests/SwiftExtractTests/SwiftSymbolTableTests.swift b/Tests/SwiftExtractTests/SwiftSymbolTableTests.swift index 4b8e44dbd..8648a95df 100644 --- a/Tests/SwiftExtractTests/SwiftSymbolTableTests.swift +++ b/Tests/SwiftExtractTests/SwiftSymbolTableTests.swift @@ -54,6 +54,37 @@ struct SwiftSymbolTableSuite { #expect(symbolTable.lookupType("Z", parent: nil) == nil) } + @Test func deferredExtensionKeepsSourceFilePath() throws { + // The extension is visited before the file declaring the extended type, + // so it is resolved late (in `finalize()`); nested types must still be + // attributed to the file they were declared in. + let extensionFile: SourceFileSyntax = """ + extension Outer.Inner { + public struct Deferred {} + } + """ + let declarationFile: SourceFileSyntax = """ + public enum Outer { + public enum Inner {} + } + """ + let symbolTable = SwiftSymbolTable.setup( + moduleName: "MyModule", + [ + .init(syntax: extensionFile, path: "Feature/Feature+Model.swift"), + .init(syntax: declarationFile, path: "Core/Outer.swift"), + ], + config: nil, + sourceDependencies: SourceDependencies(), + ) + + let outer = try #require(symbolTable.lookupType("Outer", parent: nil)) + let inner = try #require(symbolTable.lookupType("Inner", parent: outer)) + let deferred = try #require(symbolTable.lookupType("Deferred", parent: inner)) + #expect(deferred.sourceFilePath == "Feature/Feature+Model.swift") + #expect(inner.sourceFilePath == "Core/Outer.swift") + } + @Test func moduleScopedLookup() throws { let symbolTable = makeSymbolTable( moduleName: "MyModule", From b0bb56c7d78ff29be83641c5f6e7dd042bdcfaca Mon Sep 17 00:00:00 2001 From: caggiulio Date: Fri, 25 Sep 2026 09:19:41 +0200 Subject: [PATCH 3/3] Replace unresolvedExtensions tuple with named UnresolvedExtension struct --- .../SwiftParsedModuleSymbolTableBuilder.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 62805fa41..773b7b00f 100644 --- a/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -27,9 +27,16 @@ package struct SwiftParsedModuleSymbolTableBuilder { /// The build configuration used to resolve #if conditional compilation blocks. package let buildConfig: any BuildConfiguration + /// An extension decl whose extended type hasn't been resolved yet, paired with + /// the path of the source file it was declared in. + package struct UnresolvedExtension { + package var node: ExtensionDeclSyntax + package var sourceFilePath: String + } + /// Extension decls their extended type hasn't been resolved, paired with /// the path of the source file they were declared in. - package var unresolvedExtensions: [(node: ExtensionDeclSyntax, sourceFilePath: String)] + package var unresolvedExtensions: [UnresolvedExtension] package init( moduleName: String, @@ -214,7 +221,7 @@ extension SwiftParsedModuleSymbolTableBuilder { sourceFilePath: String ) { if !self.tryHandle(extension: node, sourceFilePath: sourceFilePath) { - self.unresolvedExtensions.append((node: node, sourceFilePath: sourceFilePath)) + self.unresolvedExtensions.append(UnresolvedExtension(node: node, sourceFilePath: sourceFilePath)) } }