Skip to content

Commit 79d268e

Browse files
authored
Prefer resolving link to inner symbol when container has same name (#502)
rdar://106509292
1 parent 24ba4b7 commit 79d268e

File tree

3 files changed

+319
-0
lines changed

3 files changed

+319
-0
lines changed

Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy.swift

+5
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ struct PathHierarchy {
508508
// If a parent ID was provided, start at that node and continue up the hierarchy until that node has a child that matches the first path components name.
509509
var parentNode = lookup[parentID]!
510510
let firstComponent = remaining.first!
511+
// Check if the start node has a child that matches the first path components name.
512+
if parentNode.children.keys.contains(firstComponent.name) || parentNode.children.keys.contains(firstComponent.full) {
513+
return parentNode
514+
}
515+
// Check if the start node itself matches the first path components name.
511516
if matches(node: parentNode, component: firstComponent) {
512517
remaining = remaining.dropFirst()
513518
return parentNode

Tests/SwiftDocCTests/Infrastructure/PathHierarchyTests.swift

+35
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,41 @@ class PathHierarchyTests: XCTestCase {
11001100
XCTAssertEqual("/Operators/MyNumber/_=(_:_:)-70j0d", /* >=(_:_:) */ paths["s:SLsE2geoiySbx_xtFZ::SYNTHESIZED::s:9Operators8MyNumberV"])
11011101
}
11021102

1103+
func testSameNameForSymbolAndContainer() throws {
1104+
try XCTSkipUnless(LinkResolutionMigrationConfiguration.shouldUseHierarchyBasedLinkResolver)
1105+
let (_, context) = try testBundleAndContext(named: "BundleWithSameNameForSymbolAndContainer")
1106+
let tree = try XCTUnwrap(context.hierarchyBasedLinkResolver?.pathHierarchy)
1107+
1108+
// public struct Something {
1109+
// public struct Something {
1110+
// public enum SomethingElse {}
1111+
// }
1112+
// public enum SomethingElse {}
1113+
// }
1114+
let moduleID = try tree.find(path: "/SameNames", onlyFindSymbols: true)
1115+
let outerStructID = try tree.find(path: "Something", parent: moduleID, onlyFindSymbols: true)
1116+
1117+
XCTAssertEqual(try tree.findSymbol(path: "Something", parent: moduleID).identifier.precise, "s:9SameNames9SomethingV") // the outer Something struct
1118+
XCTAssertEqual(try tree.findSymbol(path: "Something", parent: moduleID).absolutePath, "Something")
1119+
XCTAssertEqual(try tree.findSymbol(path: "Something", parent: outerStructID).identifier.precise, "s:9SameNames9SomethingVABV") // the inner Something struct
1120+
XCTAssertEqual(try tree.findSymbol(path: "Something", parent: outerStructID).absolutePath, "Something/Something")
1121+
1122+
let innerStructID = try tree.find(path: "Something", parent: outerStructID, onlyFindSymbols: true)
1123+
1124+
XCTAssertEqual(try tree.findSymbol(path: "SomethingElse", parent: outerStructID).identifier.precise, "s:9SameNames9SomethingV0C4ElseO") // the enum within the outer Something struct
1125+
XCTAssertEqual(try tree.findSymbol(path: "SomethingElse", parent: outerStructID).absolutePath, "Something/SomethingElse")
1126+
XCTAssertEqual(try tree.findSymbol(path: "SomethingElse", parent: innerStructID).identifier.precise, "s:9SameNames9SomethingVABV0C4ElseO") // the enum within the inner Something struct
1127+
XCTAssertEqual(try tree.findSymbol(path: "SomethingElse", parent: innerStructID).absolutePath, "Something/Something/SomethingElse")
1128+
1129+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: outerStructID).identifier.precise, "s:9SameNames9SomethingVABV0C4ElseO") // the enum within the inner Something struct
1130+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: outerStructID).absolutePath, "Something/Something/SomethingElse")
1131+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: innerStructID).identifier.precise, "s:9SameNames9SomethingVABV0C4ElseO") // the enum within the inner Something struct
1132+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: innerStructID).absolutePath, "Something/Something/SomethingElse")
1133+
1134+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: moduleID).identifier.precise, "s:9SameNames9SomethingV0C4ElseO") // the enum within the outer Something struct
1135+
XCTAssertEqual(try tree.findSymbol(path: "Something/SomethingElse", parent: moduleID).absolutePath, "Something/SomethingElse")
1136+
}
1137+
11031138
func testOneSymbolPathsWithKnownDisambiguation() throws {
11041139
try XCTSkipUnless(LinkResolutionMigrationConfiguration.shouldUseHierarchyBasedLinkResolver)
11051140
let exampleDocumentation = Folder(name: "MyKit.docc", content: [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
{
2+
"metadata": {
3+
"formatVersion": {
4+
"major": 0,
5+
"minor": 6,
6+
"patch": 0
7+
},
8+
"generator": "Apple Swift version 5.9 (swiftlang-5.9.0.100.22 clang-1500.0.7.24.100)"
9+
},
10+
"module": {
11+
"name": "SameNames",
12+
"platform": {
13+
"architecture": "x86_64",
14+
"vendor": "apple",
15+
"operatingSystem": {
16+
"name": "macosx",
17+
"minimumVersion": {
18+
"major": 13,
19+
"minor": 3
20+
}
21+
}
22+
}
23+
},
24+
"symbols": [
25+
{
26+
"kind": {
27+
"identifier": "swift.enum",
28+
"displayName": "Enumeration"
29+
},
30+
"identifier": {
31+
"precise": "s:9SameNames9SomethingVABV0C4ElseO",
32+
"interfaceLanguage": "swift"
33+
},
34+
"pathComponents": [
35+
"Something",
36+
"Something",
37+
"SomethingElse"
38+
],
39+
"names": {
40+
"title": "Something.Something.SomethingElse",
41+
"navigator": [
42+
{
43+
"kind": "identifier",
44+
"spelling": "SomethingElse"
45+
}
46+
],
47+
"subHeading": [
48+
{
49+
"kind": "keyword",
50+
"spelling": "enum"
51+
},
52+
{
53+
"kind": "text",
54+
"spelling": " "
55+
},
56+
{
57+
"kind": "identifier",
58+
"spelling": "SomethingElse"
59+
}
60+
]
61+
},
62+
"declarationFragments": [
63+
{
64+
"kind": "keyword",
65+
"spelling": "enum"
66+
},
67+
{
68+
"kind": "text",
69+
"spelling": " "
70+
},
71+
{
72+
"kind": "identifier",
73+
"spelling": "SomethingElse"
74+
}
75+
],
76+
"accessLevel": "public",
77+
"location": {
78+
"uri": "file:///Users/username/path/to/SameNames/SameNames.swift",
79+
"position": {
80+
"line": 5,
81+
"character": 20
82+
}
83+
}
84+
},
85+
{
86+
"kind": {
87+
"identifier": "swift.enum",
88+
"displayName": "Enumeration"
89+
},
90+
"identifier": {
91+
"precise": "s:9SameNames9SomethingV0C4ElseO",
92+
"interfaceLanguage": "swift"
93+
},
94+
"pathComponents": [
95+
"Something",
96+
"SomethingElse"
97+
],
98+
"names": {
99+
"title": "Something.SomethingElse",
100+
"navigator": [
101+
{
102+
"kind": "identifier",
103+
"spelling": "SomethingElse"
104+
}
105+
],
106+
"subHeading": [
107+
{
108+
"kind": "keyword",
109+
"spelling": "enum"
110+
},
111+
{
112+
"kind": "text",
113+
"spelling": " "
114+
},
115+
{
116+
"kind": "identifier",
117+
"spelling": "SomethingElse"
118+
}
119+
]
120+
},
121+
"declarationFragments": [
122+
{
123+
"kind": "keyword",
124+
"spelling": "enum"
125+
},
126+
{
127+
"kind": "text",
128+
"spelling": " "
129+
},
130+
{
131+
"kind": "identifier",
132+
"spelling": "SomethingElse"
133+
}
134+
],
135+
"accessLevel": "public",
136+
"location": {
137+
"uri": "file:///Users/username/path/to/SameNames/SameNames.swift",
138+
"position": {
139+
"line": 7,
140+
"character": 16
141+
}
142+
}
143+
},
144+
{
145+
"kind": {
146+
"identifier": "swift.struct",
147+
"displayName": "Structure"
148+
},
149+
"identifier": {
150+
"precise": "s:9SameNames9SomethingVABV",
151+
"interfaceLanguage": "swift"
152+
},
153+
"pathComponents": [
154+
"Something",
155+
"Something"
156+
],
157+
"names": {
158+
"title": "Something.Something",
159+
"navigator": [
160+
{
161+
"kind": "identifier",
162+
"spelling": "Something"
163+
}
164+
],
165+
"subHeading": [
166+
{
167+
"kind": "keyword",
168+
"spelling": "struct"
169+
},
170+
{
171+
"kind": "text",
172+
"spelling": " "
173+
},
174+
{
175+
"kind": "identifier",
176+
"spelling": "Something"
177+
}
178+
]
179+
},
180+
"declarationFragments": [
181+
{
182+
"kind": "keyword",
183+
"spelling": "struct"
184+
},
185+
{
186+
"kind": "text",
187+
"spelling": " "
188+
},
189+
{
190+
"kind": "identifier",
191+
"spelling": "Something"
192+
}
193+
],
194+
"accessLevel": "public",
195+
"location": {
196+
"uri": "file:///Users/username/path/to/SameNames/SameNames.swift",
197+
"position": {
198+
"line": 4,
199+
"character": 18
200+
}
201+
}
202+
},
203+
{
204+
"kind": {
205+
"identifier": "swift.struct",
206+
"displayName": "Structure"
207+
},
208+
"identifier": {
209+
"precise": "s:9SameNames9SomethingV",
210+
"interfaceLanguage": "swift"
211+
},
212+
"pathComponents": [
213+
"Something"
214+
],
215+
"names": {
216+
"title": "Something",
217+
"navigator": [
218+
{
219+
"kind": "identifier",
220+
"spelling": "Something"
221+
}
222+
],
223+
"subHeading": [
224+
{
225+
"kind": "keyword",
226+
"spelling": "struct"
227+
},
228+
{
229+
"kind": "text",
230+
"spelling": " "
231+
},
232+
{
233+
"kind": "identifier",
234+
"spelling": "Something"
235+
}
236+
]
237+
},
238+
"declarationFragments": [
239+
{
240+
"kind": "keyword",
241+
"spelling": "struct"
242+
},
243+
{
244+
"kind": "text",
245+
"spelling": " "
246+
},
247+
{
248+
"kind": "identifier",
249+
"spelling": "Something"
250+
}
251+
],
252+
"accessLevel": "public",
253+
"location": {
254+
"uri": "file:///Users/username/path/to/SameNames/SameNames.swift",
255+
"position": {
256+
"line": 3,
257+
"character": 14
258+
}
259+
}
260+
}
261+
],
262+
"relationships": [
263+
{
264+
"kind": "memberOf",
265+
"source": "s:9SameNames9SomethingV0C4ElseO",
266+
"target": "s:9SameNames9SomethingV"
267+
},
268+
{
269+
"kind": "memberOf",
270+
"source": "s:9SameNames9SomethingVABV0C4ElseO",
271+
"target": "s:9SameNames9SomethingVABV"
272+
},
273+
{
274+
"kind": "memberOf",
275+
"source": "s:9SameNames9SomethingVABV",
276+
"target": "s:9SameNames9SomethingV"
277+
}
278+
]
279+
}

0 commit comments

Comments
 (0)