Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,37 @@ extension SwiftParsedModuleSymbolTableBuilder {
parent: SwiftNominalTypeDeclaration
) {
for member in node.members {
// Find any nested types within this nominal type and add them.
if let nominalMember = member.decl.asNominal {
self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalMember, parent: parent)
} else if let typeAliasMember = member.decl.as(TypeAliasDeclSyntax.self) {
self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasMember, parent: parent)
self.handle(sourceFilePath: sourceFilePath, memberDecl: member.decl, parent: parent)
}
}

/// Add a nested type declaration to the symbol table.
package mutating func handle(
sourceFilePath: String,
memberDecl decl: DeclSyntax,
parent: SwiftNominalTypeDeclaration
) {
if let nominalMember = decl.asNominal {
self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalMember, parent: parent)
} else if let typeAliasMember = decl.as(TypeAliasDeclSyntax.self) {
self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasMember, parent: parent)
} else if let ifConfigNode = decl.as(IfConfigDeclSyntax.self) {
let (clause, _) = ifConfigNode.activeClause(in: buildConfig)
if let clause, let elements = clause.elements {
switch elements {
case .decls(let memberBlock):
for memberItem in memberBlock {
self.handle(sourceFilePath: sourceFilePath, memberDecl: memberItem.decl, parent: parent)
}
case .statements(let codeBlock):
for codeItem in codeBlock {
if let declNode = codeItem.item.as(DeclSyntax.self) {
self.handle(sourceFilePath: sourceFilePath, memberDecl: declNode, parent: parent)
}
}
default:
break
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ public class SwiftTypeLookupContext {
return
(try typeDeclaration(for: parentDecl, sourceFilePath: "FIXME_NO_SOURCE_FILE.swift")
as! SwiftNominalTypeDeclaration) // FIXME: need to get the source file of the parent
case .extensionDecl(let extensionNode):
if let extendedNominal = extendedNominal(of: extensionNode) {
return extendedNominal
}
node = parentDecl
continue
default:
node = parentDecl
continue
Expand Down
172 changes: 172 additions & 0 deletions Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,178 @@ struct JNINestedTypesTests {
)
}

@Test("Import: shadowed nested type name")
func shadowedNestedTypeName_java() throws {
// A nested type whose name shadows a type of the same name in an outer
// scope must resolve to the innermost declaration.
try assertOutput(
input: """
public struct Outer {
public enum Kind {
case alpha
case beta
}

public struct Inner {
public enum Kind {
case one
case two
}

public var kind: Kind // Outer.Inner.Kind
}

public var kind: Kind // Outer.Kind
public var inner: Inner
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
// The `Inner.kind` property must reference `Outer.Inner.Kind`, not `Outer.Kind`.
"""
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
""",
"""
public void setKind(Outer.Inner.Kind newValue) {
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
""",
// The outer `kind` must reference `Outer.Kind`.
"""
public Outer.Kind getKind(SwiftArena swiftArena) {
return Outer.Kind.wrapMemoryAddressUnsafe(Outer.$getKind(this.$memoryAddress()), swiftArena);
""",
]
)
}

@Test("Import: shadowed nested type name inside #if")
func shadowedNestedTypeName_ifConfig_java() throws {
try assertOutput(
input: """
public struct Outer {
#if SOME_FLAG
public enum Kind {
case alphaX
}
#else
public enum Kind {
case alpha
case beta
}
#endif

public struct Inner {
#if SOME_FLAG
public enum Kind {
case oneX
}
#else
public enum Kind {
case one
case two
}
#endif

public var kind: Kind // Outer.Inner.Kind
}

public var kind: Kind // Outer.Kind
public var inner: Inner
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
"""
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
""",
"""
public void setKind(Outer.Inner.Kind newValue) {
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
""",
]
)
}

@Test("Import: shadowed nested type name in deeper nesting")
func shadowedNestedTypeName_deep_java() throws {
try assertOutput(
input: """
public struct DiscordChannel {
public struct Kind {
public init() {}
}

public struct Message {
public struct MessageReference {
public struct Kind {
public init() {}
}
public var kind: Kind
}
}
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
// The `MessageReference.kind` property must reference the deeply nested
// `DiscordChannel.Message.MessageReference.Kind`, not `DiscordChannel.Kind`.
"""
public DiscordChannel.Message.MessageReference.Kind getKind(SwiftArena swiftArena) {
""",
"""
public void setKind(DiscordChannel.Message.MessageReference.Kind newValue) {
""",
]
)
}

@Test("Import: shadowed nested type name declared via extension")
func shadowedNestedTypeName_extension_java() throws {
try assertOutput(
input: """
public struct Outer {
public enum Kind {
case alpha
case beta
}
}

extension Outer {
public struct Inner {
public enum Kind {
case one
case two
}

public var kind: Kind // Outer.Inner.Kind
}
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
// The `Inner.kind` property must reference `Outer.Inner.Kind`, not `Outer.Kind`.
"""
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
""",
"""
public void setKind(Outer.Inner.Kind newValue) {
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
""",
]
)
}

@Test("Import: nested in enum")
func nestedEnums_java() throws {
try assertOutput(
Expand Down
Loading