From c27e99055c4678f303b3375cb84d4e3c1f7cf425 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 14 Apr 2026 21:31:09 +0900 Subject: [PATCH 1/3] Avoid logging "adding..." on info --- Sources/JExtractSwiftLib/Swift2JavaTranslator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index 106247a4..7455bab6 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -103,7 +103,7 @@ extension Swift2JavaTranslator { } package func add(filePath: String, text: String) { - log.info("Adding: \(filePath)") + log.debug("Adding: \(filePath)") let sourceFileSyntax = Parser.parse(source: text) self.inputs.append(SwiftJavaInputFile(syntax: sourceFileSyntax, path: filePath)) } From 67b7db610570415bee4928ee7d73e7297c62da3d Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 14 Apr 2026 21:49:45 +0900 Subject: [PATCH 2/3] Some initial module qualified lookups Pretty simple approach, if we recognize the name as a module, we try to make the lookup inside it. Part of https://github.com/swiftlang/swift-java/issues/339 --- .../SwiftTypes/SwiftSymbolTable.swift | 10 +- .../SwiftTypes/SwiftType.swift | 18 ++-- .../SwiftTypes/SwiftTypeLookupContext.swift | 13 ++- .../SwiftSymbolTableTests.swift | 92 ++++++++++++++++++- 4 files changed, 122 insertions(+), 11 deletions(-) diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift index 5ba824c4..34e8c6a4 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift @@ -153,11 +153,17 @@ extension SwiftSymbolTable: SwiftSymbolTableProtocol { } } - // FIXME: Implement module qualified name lookups. E.g. 'Swift.String' - return nil } + /// Look for a top-level nominal type in a specific module by name + package func lookupTopLevelNominalType(_ name: String, inModule moduleName: String) -> SwiftNominalTypeDeclaration? { + if moduleName == self.moduleName { + return parsedModule.lookupTopLevelNominalType(name) + } + return importedModules[moduleName]?.lookupTopLevelNominalType(name) + } + // Look for a nested type with the given name. package func lookupNestedType(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftNominalTypeDeclaration? { if let parsedResult = parsedModule.lookupNestedType(name, parent: parent) { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index 0de2ca19..67516473 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -353,19 +353,21 @@ extension SwiftType { self = knownTypes.optionalSugar(try SwiftType(optionalType.wrappedType, lookupContext: lookupContext)) case .memberType(let memberType): - // If the parent type isn't a known module, translate it. - // FIXME: Need a more reasonable notion of which names are module names - // for this to work. What can we query for this information? + // If the parent type is a known module name, perform a module-qualified + // lookup instead of treating the module as a parent type let parentType: SwiftType? + let moduleName: String? if let base = memberType.baseType.as(IdentifierTypeSyntax.self), lookupContext.symbolTable.isModuleName(base.name.trimmedDescription) { parentType = nil + moduleName = base.name.trimmedDescription } else { parentType = try SwiftType(memberType.baseType, lookupContext: lookupContext) + moduleName = nil } - // Translate the generic arguments. + // Translate the generic arguments let genericArgs = try memberType.genericArgumentClause.map { genericArgumentClause in try genericArgumentClause.arguments.map { argument in switch argument.argument { @@ -382,7 +384,8 @@ extension SwiftType { parent: parentType, name: memberType.name, genericArguments: genericArgs, - lookupContext: lookupContext + lookupContext: lookupContext, + module: moduleName ) case .metatypeType(let metatypeType): @@ -431,7 +434,8 @@ extension SwiftType { parent: SwiftType?, name: TokenSyntax, genericArguments: [SwiftType]?, - lookupContext: SwiftTypeLookupContext + lookupContext: SwiftTypeLookupContext, + module: String? = nil ) throws { // Look up the imported types by name to resolve it to a nominal type. let typeDecl: SwiftTypeDeclaration? @@ -440,6 +444,8 @@ extension SwiftType { throw TypeTranslationError.unknown(originalType) } typeDecl = lookupContext.symbolTable.lookupNestedType(name.text, parent: parentDecl) + } else if let module { + typeDecl = lookupContext.qualifiedLookup(name: name.text, inModule: module) } else { guard let ident = Identifier(name) else { throw TypeTranslationError.unknown(originalType) diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift index 28b0e4d0..b441a3d8 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift @@ -15,8 +15,8 @@ @_spi(Experimental) import SwiftLexicalLookup import SwiftSyntax -/// Unqualified type lookup manager. -/// All unqualified lookup should be done via this instance. This caches the +/// Type lookup manager. +/// All type lookups should be done via this instance. This caches the /// association of `Syntax.ID` to `SwiftTypeDeclaration`, and guarantees that /// there's only one `SwiftTypeDeclaration` per declaration `Syntax`. class SwiftTypeLookupContext { @@ -28,6 +28,15 @@ class SwiftTypeLookupContext { self.symbolTable = symbolTable } + /// Perform qualified type lookup in a specific module + /// + /// - Parameters: + /// - name: name to lookup + /// - moduleName: the module to look in + func qualifiedLookup(name: String, inModule moduleName: String) -> SwiftTypeDeclaration? { + symbolTable.lookupTopLevelNominalType(name, inModule: moduleName) + } + /// Perform unqualified type lookup. /// /// - Parameters: diff --git a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift index bdf00de1..66a725f1 100644 --- a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift +++ b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift @@ -81,7 +81,7 @@ struct SwiftSymbolTableSuite { public struct MyValue {} } - public func fullyQualifiedType() -> MyModule.MyModule.MyValue + public func fullyQualifiedType() -> MyModule.MyModule.MyValue """, mode, .java, @@ -92,4 +92,94 @@ struct SwiftSymbolTableSuite { ], ) } + + @Test func moduleScopedLookup() throws { + let sourceFile: SourceFileSyntax = """ + public struct MyClass {} + """ + let symbolTable = SwiftSymbolTable.setup( + moduleName: "MyModule", + [ + .init(syntax: sourceFile, path: "Fake.swift") + ], + config: nil, + log: Logger(label: "swift-java", logLevel: .critical), + ) + + // Lookup in self-module by qualified name + let myClass = symbolTable.lookupTopLevelNominalType("MyClass", inModule: "MyModule") + #expect(myClass != nil) + #expect(myClass?.qualifiedName == "MyClass") + + // Lookup in imported module (Swift) + let swiftInt = symbolTable.lookupTopLevelNominalType("Int", inModule: "Swift") + #expect(swiftInt != nil) + #expect(swiftInt?.qualifiedName == "Int") + + // Lookup in unknown module returns nil + let unknown = symbolTable.lookupTopLevelNominalType("Foo", inModule: "NoSuchModule") + #expect(unknown == nil) + } + + @Test(arguments: [JExtractGenerationMode.jni, .ffm]) + func resolveQualifiedTypesInFunctionSignatures(mode: JExtractGenerationMode) throws { + try assertOutput( + input: """ + public struct MySwiftClass { + public init() {} + } + + public func factory(len: Swift.Int, cap: Swift.Int) -> MyModule.MySwiftClass + """, + mode, + .java, + swiftModuleName: "MyModule", + detectChunkByInitialLines: 1, + expectedChunks: [ + "public static MySwiftClass factory(" + ], + ) + } + + @Test(arguments: [JExtractGenerationMode.jni, .ffm]) + func resolveQualifiedNestedTypesInFunctionSignatures(mode: JExtractGenerationMode) throws { + try assertOutput( + input: """ + public struct MySwiftClass { + public struct Nested { + public init() {} + } + } + + public func factory(len: Swift.Int, cap: Swift.Int) -> MyModule.MySwiftClass.Nested + """, + mode, + .java, + swiftModuleName: "MyModule", + detectChunkByInitialLines: 1, + expectedChunks: [ + "public static MySwiftClass.Nested factory(" + ], + ) + } + + @Test(arguments: [JExtractGenerationMode.jni, .ffm]) + func resolveQualifiedTypesShadowingModule(mode: JExtractGenerationMode) throws { + try assertOutput( + input: """ + public struct MyModule { // shadowing module MyModule + public init() {} + } + + public func factory(len: Swift.Int, cap: Swift.Int) -> MyModule + """, + mode, + .java, + swiftModuleName: "MyModule", + detectChunkByInitialLines: 1, + expectedChunks: [ + "public static MyModule factory(" + ], + ) + } } From f642ff12c0090a39a278838ea1d3164228b68eee Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Wed, 15 Apr 2026 09:41:29 +0900 Subject: [PATCH 3/3] review follow up --- Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift | 4 ++-- .../JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index 67516473..e0752ea6 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -367,7 +367,7 @@ extension SwiftType { moduleName = nil } - // Translate the generic arguments + // Translate the generic arguments. let genericArgs = try memberType.genericArgumentClause.map { genericArgumentClause in try genericArgumentClause.arguments.map { argument in switch argument.argument { @@ -445,7 +445,7 @@ extension SwiftType { } typeDecl = lookupContext.symbolTable.lookupNestedType(name.text, parent: parentDecl) } else if let module { - typeDecl = lookupContext.qualifiedLookup(name: name.text, inModule: module) + typeDecl = lookupContext.moduleQualifiedLookup(name: name.text, in: module) } else { guard let ident = Identifier(name) else { throw TypeTranslationError.unknown(originalType) diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift index b441a3d8..71aec972 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift @@ -28,12 +28,12 @@ class SwiftTypeLookupContext { self.symbolTable = symbolTable } - /// Perform qualified type lookup in a specific module + /// Perform module-qualified type lookup in a specific module /// /// - Parameters: /// - name: name to lookup /// - moduleName: the module to look in - func qualifiedLookup(name: String, inModule moduleName: String) -> SwiftTypeDeclaration? { + func moduleQualifiedLookup(name: String, in moduleName: String) -> SwiftTypeDeclaration? { symbolTable.lookupTopLevelNominalType(name, inModule: moduleName) }