Skip to content

Commit 0204f00

Browse files
committed
Improve nested names handling even in #if scopes
This also resolves #822 while at it
1 parent c235a0f commit 0204f00

3 files changed

Lines changed: 209 additions & 5 deletions

File tree

Sources/SwiftExtract/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,37 @@ extension SwiftParsedModuleSymbolTableBuilder {
174174
parent: SwiftNominalTypeDeclaration
175175
) {
176176
for member in node.members {
177-
// Find any nested types within this nominal type and add them.
178-
if let nominalMember = member.decl.asNominal {
179-
self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalMember, parent: parent)
180-
} else if let typeAliasMember = member.decl.as(TypeAliasDeclSyntax.self) {
181-
self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasMember, parent: parent)
177+
self.handle(sourceFilePath: sourceFilePath, memberDecl: member.decl, parent: parent)
178+
}
179+
}
180+
181+
/// Add a nested type declaration to the symbol table.
182+
package mutating func handle(
183+
sourceFilePath: String,
184+
memberDecl decl: DeclSyntax,
185+
parent: SwiftNominalTypeDeclaration
186+
) {
187+
if let nominalMember = decl.asNominal {
188+
self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalMember, parent: parent)
189+
} else if let typeAliasMember = decl.as(TypeAliasDeclSyntax.self) {
190+
self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasMember, parent: parent)
191+
} else if let ifConfigNode = decl.as(IfConfigDeclSyntax.self) {
192+
let (clause, _) = ifConfigNode.activeClause(in: buildConfig)
193+
if let clause, let elements = clause.elements {
194+
switch elements {
195+
case .decls(let memberBlock):
196+
for memberItem in memberBlock {
197+
self.handle(sourceFilePath: sourceFilePath, memberDecl: memberItem.decl, parent: parent)
198+
}
199+
case .statements(let codeBlock):
200+
for codeItem in codeBlock {
201+
if let declNode = codeItem.item.as(DeclSyntax.self) {
202+
self.handle(sourceFilePath: sourceFilePath, memberDecl: declNode, parent: parent)
203+
}
204+
}
205+
default:
206+
break
207+
}
182208
}
183209
}
184210
}

Sources/SwiftExtract/SwiftTypes/SwiftTypeLookupContext.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ public class SwiftTypeLookupContext {
212212
return
213213
(try typeDeclaration(for: parentDecl, sourceFilePath: "FIXME_NO_SOURCE_FILE.swift")
214214
as! SwiftNominalTypeDeclaration) // FIXME: need to get the source file of the parent
215+
case .extensionDecl(let extensionNode):
216+
if let extendedNominal = extendedNominal(of: extensionNode) {
217+
return extendedNominal
218+
}
219+
node = parentDecl
220+
continue
215221
default:
216222
node = parentDecl
217223
continue

Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,178 @@ struct JNINestedTypesTests {
102102
)
103103
}
104104

105+
@Test("Import: shadowed nested type name")
106+
func shadowedNestedTypeName_java() throws {
107+
// A nested type whose name shadows a type of the same name in an outer
108+
// scope must resolve to the innermost declaration.
109+
try assertOutput(
110+
input: """
111+
public struct Outer {
112+
public enum Kind {
113+
case alpha
114+
case beta
115+
}
116+
117+
public struct Inner {
118+
public enum Kind {
119+
case one
120+
case two
121+
}
122+
123+
public var kind: Kind // Outer.Inner.Kind
124+
}
125+
126+
public var kind: Kind // Outer.Kind
127+
public var inner: Inner
128+
}
129+
""",
130+
.jni,
131+
.java,
132+
detectChunkByInitialLines: 1,
133+
expectedChunks: [
134+
// The `Inner.kind` property must reference `Outer.Inner.Kind`, not `Outer.Kind`.
135+
"""
136+
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
137+
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
138+
""",
139+
"""
140+
public void setKind(Outer.Inner.Kind newValue) {
141+
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
142+
""",
143+
// The outer `kind` must reference `Outer.Kind`.
144+
"""
145+
public Outer.Kind getKind(SwiftArena swiftArena) {
146+
return Outer.Kind.wrapMemoryAddressUnsafe(Outer.$getKind(this.$memoryAddress()), swiftArena);
147+
""",
148+
]
149+
)
150+
}
151+
152+
@Test("Import: shadowed nested type name inside #if")
153+
func shadowedNestedTypeName_ifConfig_java() throws {
154+
try assertOutput(
155+
input: """
156+
public struct Outer {
157+
#if SOME_FLAG
158+
public enum Kind {
159+
case alphaX
160+
}
161+
#else
162+
public enum Kind {
163+
case alpha
164+
case beta
165+
}
166+
#endif
167+
168+
public struct Inner {
169+
#if SOME_FLAG
170+
public enum Kind {
171+
case oneX
172+
}
173+
#else
174+
public enum Kind {
175+
case one
176+
case two
177+
}
178+
#endif
179+
180+
public var kind: Kind // Outer.Inner.Kind
181+
}
182+
183+
public var kind: Kind // Outer.Kind
184+
public var inner: Inner
185+
}
186+
""",
187+
.jni,
188+
.java,
189+
detectChunkByInitialLines: 1,
190+
expectedChunks: [
191+
"""
192+
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
193+
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
194+
""",
195+
"""
196+
public void setKind(Outer.Inner.Kind newValue) {
197+
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
198+
""",
199+
]
200+
)
201+
}
202+
203+
@Test("Import: shadowed nested type name in deeper nesting")
204+
func shadowedNestedTypeName_deep_java() throws {
205+
try assertOutput(
206+
input: """
207+
public struct DiscordChannel {
208+
public struct Kind {
209+
public init() {}
210+
}
211+
212+
public struct Message {
213+
public struct MessageReference {
214+
public struct Kind {
215+
public init() {}
216+
}
217+
public var kind: Kind
218+
}
219+
}
220+
}
221+
""",
222+
.jni,
223+
.java,
224+
detectChunkByInitialLines: 1,
225+
expectedChunks: [
226+
// The `MessageReference.kind` property must reference the deeply nested
227+
// `DiscordChannel.Message.MessageReference.Kind`, not `DiscordChannel.Kind`.
228+
"""
229+
public DiscordChannel.Message.MessageReference.Kind getKind(SwiftArena swiftArena) {
230+
""",
231+
"""
232+
public void setKind(DiscordChannel.Message.MessageReference.Kind newValue) {
233+
""",
234+
]
235+
)
236+
}
237+
238+
@Test("Import: shadowed nested type name declared via extension")
239+
func shadowedNestedTypeName_extension_java() throws {
240+
try assertOutput(
241+
input: """
242+
public struct Outer {
243+
public enum Kind {
244+
case alpha
245+
case beta
246+
}
247+
}
248+
249+
extension Outer {
250+
public struct Inner {
251+
public enum Kind {
252+
case one
253+
case two
254+
}
255+
256+
public var kind: Kind // Outer.Inner.Kind
257+
}
258+
}
259+
""",
260+
.jni,
261+
.java,
262+
detectChunkByInitialLines: 1,
263+
expectedChunks: [
264+
// The `Inner.kind` property must reference `Outer.Inner.Kind`, not `Outer.Kind`.
265+
"""
266+
public Outer.Inner.Kind getKind(SwiftArena swiftArena) {
267+
return Outer.Inner.Kind.wrapMemoryAddressUnsafe(Outer.Inner.$getKind(this.$memoryAddress()), swiftArena);
268+
""",
269+
"""
270+
public void setKind(Outer.Inner.Kind newValue) {
271+
Outer.Inner.$setKind(newValue.$memoryAddress(), this.$memoryAddress());
272+
""",
273+
]
274+
)
275+
}
276+
105277
@Test("Import: nested in enum")
106278
func nestedEnums_java() throws {
107279
try assertOutput(

0 commit comments

Comments
 (0)