Skip to content

Commit 687bc8f

Browse files
authored
Support Any Swift keyword in type signature disambiguation (#1201) (#1205)
rdar://148522712
1 parent 8791584 commit 687bc8f

File tree

2 files changed

+127
-5
lines changed

2 files changed

+127
-5
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ extension PathHierarchy {
129129
// Accumulate all of the identifier tokens' spelling.
130130
accumulated.append(contentsOf: fragment.spelling.utf8)
131131

132+
case .keyword where fragment.spelling == "Any":
133+
accumulated.append(contentsOf: fragment.spelling.utf8)
134+
132135
case .text: // In Swift, we're only want some `text` tokens characters in the type disambiguation.
133136
// For example: "[", "?", "<", "...", ",", "(", "->" etc. contribute to the type spellings like
134137
// `[Name]`, `Name?`, "Name<T>", "Name...", "()", "(Name, Name)", "(Name)->Name" and more.

Tests/SwiftDocCTests/Infrastructure/PathHierarchyTests.swift

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,19 @@ class PathHierarchyTests: XCTestCase {
14081408
.init(kind: .text, spelling: ">", preciseIdentifier: nil),
14091409
]))
14101410

1411+
// Any
1412+
XCTAssertEqual("Any", functionSignatureParameterTypeName([
1413+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
1414+
]))
1415+
1416+
// Array<Any>
1417+
XCTAssertEqual("[Any]", functionSignatureParameterTypeName([
1418+
.init(kind: .typeIdentifier, spelling: "Array", preciseIdentifier: "s:Sa"),
1419+
.init(kind: .text, spelling: "<", preciseIdentifier: nil),
1420+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
1421+
.init(kind: .text, spelling: ">", preciseIdentifier: nil),
1422+
]))
1423+
14111424
// some Sequence<Int>
14121425
XCTAssertEqual("Sequence<Int>", functionSignatureParameterTypeName([
14131426
.init(kind: .keyword, spelling: "some", preciseIdentifier: nil),
@@ -1773,7 +1786,7 @@ class PathHierarchyTests: XCTestCase {
17731786
.init(name: "someName", externalName: nil, declarationFragments: [
17741787
.init(kind: .identifier, spelling: "someName", preciseIdentifier: nil),
17751788
.init(kind: .text, spelling: ": ((", preciseIdentifier: nil),
1776-
.init(kind: .typeIdentifier, spelling: "Int", preciseIdentifier: "s:Si"),
1789+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
17771790
.init(kind: .text, spelling: ", ", preciseIdentifier: nil),
17781791
.init(kind: .typeIdentifier, spelling: "String", preciseIdentifier: "s:SS"),
17791792
.init(kind: .text, spelling: "), ", preciseIdentifier: nil),
@@ -1789,10 +1802,10 @@ class PathHierarchyTests: XCTestCase {
17891802
.init(kind: .text, spelling: "?)", preciseIdentifier: nil),
17901803
])
17911804
)
1792-
XCTAssertEqual(tupleArgument?.parameterTypeNames, ["((Int,String),Date)"])
1805+
XCTAssertEqual(tupleArgument?.parameterTypeNames, ["((Any,String),Date)"])
17931806
XCTAssertEqual(tupleArgument?.returnTypeNames, ["[Int]", "String?"])
17941807

1795-
// func doSomething() -> ((Double, Double) -> Double, [Int: (Int, Int)], (Bool, Bool), String?)
1808+
// func doSomething() -> ((Double, Double) -> Double, [Int: (Int, Int)], (Bool, Any), String?)
17961809
let bigTupleReturnType = functionSignatureTypeNames(.init(
17971810
parameters: [],
17981811
returns: [
@@ -1811,7 +1824,7 @@ class PathHierarchyTests: XCTestCase {
18111824
.init(kind: .text, spelling: ")], (", preciseIdentifier: nil),
18121825
.init(kind: .typeIdentifier, spelling: "Bool", preciseIdentifier: "s:Si"),
18131826
.init(kind: .text, spelling: ", ", preciseIdentifier: nil),
1814-
.init(kind: .typeIdentifier, spelling: "Bool", preciseIdentifier: "s:Si"),
1827+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
18151828
.init(kind: .text, spelling: "), ", preciseIdentifier: nil),
18161829
.init(kind: .typeIdentifier, spelling: "Optional", preciseIdentifier: "s:Sq"),
18171830
.init(kind: .text, spelling: "<", preciseIdentifier: nil),
@@ -1820,7 +1833,7 @@ class PathHierarchyTests: XCTestCase {
18201833
])
18211834
)
18221835
XCTAssertEqual(bigTupleReturnType?.parameterTypeNames, [])
1823-
XCTAssertEqual(bigTupleReturnType?.returnTypeNames, ["(Double,Double)->Double", "[Int:(Int,Int)]", "(Bool,Bool)", "String?"])
1836+
XCTAssertEqual(bigTupleReturnType?.returnTypeNames, ["(Double,Double)->Double", "[Int:(Int,Int)]", "(Bool,Any)", "String?"])
18241837

18251838
// func doSomething(with someName: [Int?: String??])
18261839
let dictionaryWithOptionalsArgument = functionSignatureTypeNames(.init(
@@ -1918,6 +1931,112 @@ class PathHierarchyTests: XCTestCase {
19181931
}
19191932
}
19201933

1934+
func testParameterDisambiguationWithAnyType() throws {
1935+
// Create two overloads with different parameter types
1936+
let parameterTypes: [SymbolGraph.Symbol.DeclarationFragments.Fragment] = [
1937+
// Any (swift)
1938+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
1939+
// AnyObject (swift)
1940+
.init(kind: .typeIdentifier, spelling: "AnyObject", preciseIdentifier: "s:s9AnyObjecta"),
1941+
]
1942+
1943+
let catalog = Folder(name: "CatalogName.docc", content: [
1944+
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(moduleName: "ModuleName", symbols: parameterTypes.map { parameterTypeFragment in
1945+
makeSymbol(id: "some-function-id-\(parameterTypeFragment.spelling)", kind: .func, pathComponents: ["doSomething(with:)"], signature: .init(
1946+
parameters: [
1947+
.init(name: "something", externalName: "with", declarationFragments: [
1948+
.init(kind: .identifier, spelling: "something", preciseIdentifier: nil),
1949+
.init(kind: .text, spelling: ": ", preciseIdentifier: nil),
1950+
parameterTypeFragment
1951+
], children: [])
1952+
],
1953+
returns: [
1954+
.init(kind: .text, spelling: "()", preciseIdentifier: nil) // 'Void' in text representation
1955+
]
1956+
))
1957+
})),
1958+
])
1959+
let (_, context) = try loadBundle(catalog: catalog)
1960+
let tree = context.linkResolver.localResolver.pathHierarchy
1961+
1962+
XCTAssert(context.problems.isEmpty, "Unexpected problems \(context.problems.map(\.diagnostic.summary))")
1963+
1964+
let paths = tree.caseInsensitiveDisambiguatedPaths()
1965+
1966+
XCTAssertEqual(paths["some-function-id-Any"], "/ModuleName/doSomething(with:)-(Any)")
1967+
XCTAssertEqual(paths["some-function-id-AnyObject"], "/ModuleName/doSomething(with:)-(AnyObject)")
1968+
1969+
try assertPathCollision("doSomething(with:)", in: tree, collisions: [
1970+
("some-function-id-Any", "-(Any)"),
1971+
("some-function-id-AnyObject", "-(AnyObject)"),
1972+
])
1973+
1974+
try assertPathRaisesErrorMessage("doSomething(with:)", in: tree, context: context, expectedErrorMessage: "'doSomething(with:)' is ambiguous at '/ModuleName'") { error in
1975+
XCTAssertEqual(error.solutions.count, 2)
1976+
1977+
// These test symbols don't have full declarations. A real solution would display enough information to distinguish these.
1978+
XCTAssertEqual(error.solutions.dropFirst(0).first, .init(summary: "Insert '-(Any)' for \n'doSomething(with:)'" , replacements: [("-(Any)", 18, 18)]))
1979+
XCTAssertEqual(error.solutions.dropFirst(1).first, .init(summary: "Insert '-(AnyObject)' for \n'doSomething(with:)'" /* the test symbols don't have full declarations */, replacements: [("-(AnyObject)", 18, 18)]))
1980+
}
1981+
1982+
try assertFindsPath("doSomething(with:)-(Any)", in: tree, asSymbolID: "some-function-id-Any")
1983+
try assertFindsPath("doSomething(with:)-(Any)->()", in: tree, asSymbolID: "some-function-id-Any")
1984+
try assertFindsPath("doSomething(with:)-5gdco", in: tree, asSymbolID: "some-function-id-Any")
1985+
1986+
try assertFindsPath("doSomething(with:)-(AnyObject)", in: tree, asSymbolID: "some-function-id-AnyObject")
1987+
try assertFindsPath("doSomething(with:)-(AnyObject)->()", in: tree, asSymbolID: "some-function-id-AnyObject")
1988+
try assertFindsPath("doSomething(with:)-9kd0v", in: tree, asSymbolID: "some-function-id-AnyObject")
1989+
}
1990+
1991+
func testReturnDisambiguationWithAnyType() throws {
1992+
// Create two overloads with different return types
1993+
let returnTypes: [SymbolGraph.Symbol.DeclarationFragments.Fragment] = [
1994+
// Any (swift)
1995+
.init(kind: .keyword, spelling: "Any", preciseIdentifier: nil),
1996+
// AnyObject (swift)
1997+
.init(kind: .typeIdentifier, spelling: "AnyObject", preciseIdentifier: "s:s9AnyObjecta"),
1998+
]
1999+
2000+
let catalog = Folder(name: "CatalogName.docc", content: [
2001+
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(moduleName: "ModuleName", symbols: returnTypes.map { parameterTypeFragment in
2002+
makeSymbol(id: "some-function-id-\(parameterTypeFragment.spelling)", kind: .func, pathComponents: ["doSomething()"], signature: .init(
2003+
parameters: [],
2004+
returns: [parameterTypeFragment]
2005+
))
2006+
})),
2007+
])
2008+
let (_, context) = try loadBundle(catalog: catalog)
2009+
let tree = context.linkResolver.localResolver.pathHierarchy
2010+
2011+
XCTAssert(context.problems.isEmpty, "Unexpected problems \(context.problems.map(\.diagnostic.summary))")
2012+
2013+
let paths = tree.caseInsensitiveDisambiguatedPaths()
2014+
2015+
XCTAssertEqual(paths["some-function-id-Any"], "/ModuleName/doSomething()->Any")
2016+
XCTAssertEqual(paths["some-function-id-AnyObject"], "/ModuleName/doSomething()->AnyObject")
2017+
2018+
try assertPathCollision("doSomething()", in: tree, collisions: [
2019+
("some-function-id-Any", "->Any"),
2020+
("some-function-id-AnyObject", "->AnyObject"),
2021+
])
2022+
2023+
try assertPathRaisesErrorMessage("doSomething()", in: tree, context: context, expectedErrorMessage: "'doSomething()' is ambiguous at '/ModuleName'") { error in
2024+
XCTAssertEqual(error.solutions.count, 2)
2025+
2026+
// These test symbols don't have full declarations. A real solution would display enough information to distinguish these.
2027+
XCTAssertEqual(error.solutions.dropFirst(0).first, .init(summary: "Insert '->Any' for \n'doSomething()'" , replacements: [("->Any", 13, 13)]))
2028+
XCTAssertEqual(error.solutions.dropFirst(1).first, .init(summary: "Insert '->AnyObject' for \n'doSomething()'" /* the test symbols don't have full declarations */, replacements: [("->AnyObject", 13, 13)]))
2029+
}
2030+
2031+
try assertFindsPath("doSomething()->Any", in: tree, asSymbolID: "some-function-id-Any")
2032+
try assertFindsPath("doSomething()-()->Any", in: tree, asSymbolID: "some-function-id-Any")
2033+
try assertFindsPath("doSomething()-5gdco", in: tree, asSymbolID: "some-function-id-Any")
2034+
2035+
try assertFindsPath("doSomething()->AnyObject", in: tree, asSymbolID: "some-function-id-AnyObject")
2036+
try assertFindsPath("doSomething()-()->AnyObject", in: tree, asSymbolID: "some-function-id-AnyObject")
2037+
try assertFindsPath("doSomething()-9kd0v", in: tree, asSymbolID: "some-function-id-AnyObject")
2038+
}
2039+
19212040
func testOverloadGroupSymbolsResolveLinksWithoutHash() throws {
19222041
enableFeatureFlag(\.isExperimentalOverloadedSymbolPresentationEnabled)
19232042

0 commit comments

Comments
 (0)