Skip to content

Commit 9efa7e8

Browse files
authored
Merge pull request #1844 from ahoppen/fully-qualified-names
Fully qualify type names in call hierarchy, type hierarchy and workspace symbols
2 parents c85dd7c + eb72e10 commit 9efa7e8

File tree

3 files changed

+156
-114
lines changed

3 files changed

+156
-114
lines changed

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 113 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,13 +1454,24 @@ extension SourceKitLSPServer {
14541454
range: Range(symbolPosition)
14551455
)
14561456

1457+
let containerNames = index.containerNames(of: symbolOccurrence)
1458+
let containerName: String?
1459+
if containerNames.isEmpty {
1460+
containerName = nil
1461+
} else {
1462+
switch symbolOccurrence.symbol.language {
1463+
case .cxx, .c, .objc: containerName = containerNames.joined(separator: "::")
1464+
case .swift: containerName = containerNames.joined(separator: ".")
1465+
}
1466+
}
1467+
14571468
return WorkspaceSymbolItem.symbolInformation(
14581469
SymbolInformation(
14591470
name: symbolOccurrence.symbol.name,
14601471
kind: symbolOccurrence.symbol.kind.asLspSymbolKind(),
14611472
deprecated: nil,
14621473
location: symbolLocation,
1463-
containerName: index.containerName(of: symbolOccurrence)
1474+
containerName: containerName
14641475
)
14651476
)
14661477
}
@@ -1934,27 +1945,14 @@ extension SourceKitLSPServer {
19341945
}
19351946

19361947
private func indexToLSPCallHierarchyItem(
1937-
symbol: Symbol,
1938-
containerName: String?,
1939-
location: Location
1940-
) -> CallHierarchyItem {
1941-
let name: String
1942-
if let containerName {
1943-
switch symbol.language {
1944-
case .objc where symbol.kind == .instanceMethod || symbol.kind == .instanceProperty:
1945-
name = "-[\(containerName) \(symbol.name)]"
1946-
case .objc where symbol.kind == .classMethod || symbol.kind == .classProperty:
1947-
name = "+[\(containerName) \(symbol.name)]"
1948-
case .cxx, .c, .objc:
1949-
// C shouldn't have container names for call hierarchy and Objective-C should be covered above.
1950-
// Fall back to using the C++ notation using `::`.
1951-
name = "\(containerName)::\(symbol.name)"
1952-
case .swift:
1953-
name = "\(containerName).\(symbol.name)"
1954-
}
1955-
} else {
1956-
name = symbol.name
1948+
definition: SymbolOccurrence,
1949+
index: CheckedIndex
1950+
) -> CallHierarchyItem? {
1951+
guard let location = indexToLSPLocation(definition.location) else {
1952+
return nil
19571953
}
1954+
let name = index.fullyQualifiedName(of: definition)
1955+
let symbol = definition.symbol
19581956
return CallHierarchyItem(
19591957
name: name,
19601958
kind: symbol.kind.asLspSymbolKind(),
@@ -1994,14 +1992,7 @@ extension SourceKitLSPServer {
19941992
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
19951993
return nil
19961994
}
1997-
guard let location = indexToLSPLocation(definition.location) else {
1998-
return nil
1999-
}
2000-
return self.indexToLSPCallHierarchyItem(
2001-
symbol: definition.symbol,
2002-
containerName: index.containerName(of: definition),
2003-
location: location
2004-
)
1995+
return self.indexToLSPCallHierarchyItem(definition: definition, index: index)
20051996
}.sorted(by: { Location(uri: $0.uri, range: $0.range) < Location(uri: $1.uri, range: $1.range) })
20061997

20071998
// Ideally, we should show multiple symbols. But VS Code fails to display call hierarchies with multiple root items,
@@ -2063,38 +2054,27 @@ extension SourceKitLSPServer {
20632054

20642055
// TODO: Remove this workaround once https://github.com/swiftlang/swift/issues/75600 is fixed
20652056
func indexToLSPCallHierarchyItem2(
2066-
symbol: Symbol,
2067-
containerName: String?,
2068-
location: Location
2069-
) -> CallHierarchyItem {
2070-
return self.indexToLSPCallHierarchyItem(symbol: symbol, containerName: containerName, location: location)
2057+
definition: SymbolOccurrence,
2058+
index: CheckedIndex
2059+
) -> CallHierarchyItem? {
2060+
return self.indexToLSPCallHierarchyItem(definition: definition, index: index)
20712061
}
20722062

20732063
let calls = callersToCalls.compactMap { (caller: Symbol, calls: [SymbolOccurrence]) -> CallHierarchyIncomingCall? in
20742064
// Resolve the caller's definition to find its location
2075-
let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: caller.usr)
2076-
let definitionSymbolLocation = definition?.location
2077-
let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2)
2078-
let containerName: String? =
2079-
if let definition {
2080-
index.containerName(of: definition)
2081-
} else {
2082-
nil
2083-
}
2065+
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: caller.usr) else {
2066+
return nil
2067+
}
20842068

20852069
let locations = calls.compactMap { indexToLSPLocation2($0.location) }.sorted()
20862070
guard !locations.isEmpty else {
20872071
return nil
20882072
}
2073+
guard let item = indexToLSPCallHierarchyItem2(definition: definition, index: index) else {
2074+
return nil
2075+
}
20892076

2090-
return CallHierarchyIncomingCall(
2091-
from: indexToLSPCallHierarchyItem2(
2092-
symbol: caller,
2093-
containerName: containerName,
2094-
location: definitionLocation ?? locations.first!
2095-
),
2096-
fromRanges: locations.map(\.range)
2097-
)
2077+
return CallHierarchyIncomingCall(from: item, fromRanges: locations.map(\.range))
20982078
}
20992079
return calls.sorted(by: { $0.from.name < $1.from.name })
21002080
}
@@ -2113,11 +2093,10 @@ extension SourceKitLSPServer {
21132093

21142094
// TODO: Remove this workaround once https://github.com/swiftlang/swift/issues/75600 is fixed
21152095
func indexToLSPCallHierarchyItem2(
2116-
symbol: Symbol,
2117-
containerName: String?,
2118-
location: Location
2119-
) -> CallHierarchyItem {
2120-
return self.indexToLSPCallHierarchyItem(symbol: symbol, containerName: containerName, location: location)
2096+
definition: SymbolOccurrence,
2097+
index: CheckedIndex
2098+
) -> CallHierarchyItem? {
2099+
return self.indexToLSPCallHierarchyItem(definition: definition, index: index)
21212100
}
21222101

21232102
let callableUsrs = [data.usr] + index.occurrences(relatedToUSR: data.usr, roles: .accessorOf).map(\.symbol.usr)
@@ -2131,37 +2110,32 @@ extension SourceKitLSPServer {
21312110
}
21322111

21332112
// Resolve the callee's definition to find its location
2134-
let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr)
2135-
let definitionSymbolLocation = definition?.location
2136-
let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2)
2137-
let containerName: String? =
2138-
if let definition {
2139-
index.containerName(of: definition)
2140-
} else {
2141-
nil
2142-
}
2113+
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr) else {
2114+
return nil
2115+
}
21432116

2144-
return CallHierarchyOutgoingCall(
2145-
to: indexToLSPCallHierarchyItem2(
2146-
symbol: occurrence.symbol,
2147-
containerName: containerName,
2148-
location: definitionLocation ?? location // Use occurrence location as fallback
2149-
),
2150-
fromRanges: [location.range]
2151-
)
2117+
guard let item = indexToLSPCallHierarchyItem2(definition: definition, index: index) else {
2118+
return nil
2119+
}
2120+
2121+
return CallHierarchyOutgoingCall(to: item, fromRanges: [location.range])
21522122
}
21532123
return calls.sorted(by: { $0.to.name < $1.to.name })
21542124
}
21552125

21562126
private func indexToLSPTypeHierarchyItem(
2157-
symbol: Symbol,
2127+
definition: SymbolOccurrence,
21582128
moduleName: String?,
2159-
location: Location,
21602129
index: CheckedIndex
2161-
) -> TypeHierarchyItem {
2130+
) -> TypeHierarchyItem? {
21622131
let name: String
21632132
let detail: String?
21642133

2134+
guard let location = indexToLSPLocation(definition.location) else {
2135+
return nil
2136+
}
2137+
2138+
let symbol = definition.symbol
21652139
switch symbol.kind {
21662140
case .extension:
21672141
// Query the conformance added by this extension
@@ -2182,7 +2156,7 @@ extension SourceKitLSPServer {
21822156
detail = "Extension"
21832157
}
21842158
default:
2185-
name = symbol.name
2159+
name = index.fullyQualifiedName(of: definition)
21862160
detail = moduleName
21872161
}
21882162

@@ -2231,16 +2205,12 @@ extension SourceKitLSPServer {
22312205
}
22322206
.compactMap(\.usr)
22332207
let typeHierarchyItems = usrs.compactMap { (usr) -> TypeHierarchyItem? in
2234-
guard
2235-
let info = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr),
2236-
let location = indexToLSPLocation(info.location)
2237-
else {
2208+
guard let info = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
22382209
return nil
22392210
}
22402211
return self.indexToLSPTypeHierarchyItem(
2241-
symbol: info.symbol,
2212+
definition: info,
22422213
moduleName: info.location.moduleName,
2243-
location: location,
22442214
index: index
22452215
)
22462216
}
@@ -2305,30 +2275,28 @@ extension SourceKitLSPServer {
23052275

23062276
// TODO: Remove this workaround once https://github.com/swiftlang/swift/issues/75600 is fixed
23072277
func indexToLSPTypeHierarchyItem2(
2308-
symbol: Symbol,
2278+
definition: SymbolOccurrence,
23092279
moduleName: String?,
2310-
location: Location,
23112280
index: CheckedIndex
2312-
) -> TypeHierarchyItem {
2313-
return self.indexToLSPTypeHierarchyItem(symbol: symbol, moduleName: moduleName, location: location, index: index)
2281+
) -> TypeHierarchyItem? {
2282+
return self.indexToLSPTypeHierarchyItem(
2283+
definition: definition,
2284+
moduleName: moduleName,
2285+
index: index
2286+
)
23142287
}
23152288

23162289
// Convert occurrences to type hierarchy items
23172290
let occurs = baseOccurs + retroactiveConformanceOccurs
23182291
let types = occurs.compactMap { occurrence -> TypeHierarchyItem? in
2319-
guard let location = indexToLSPLocation2(occurrence.location) else {
2292+
// Resolve the supertype's definition to find its location
2293+
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr) else {
23202294
return nil
23212295
}
23222296

2323-
// Resolve the supertype's definition to find its location
2324-
let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr)
2325-
let definitionSymbolLocation = definition?.location
2326-
let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2)
2327-
23282297
return indexToLSPTypeHierarchyItem2(
2329-
symbol: occurrence.symbol,
2330-
moduleName: definitionSymbolLocation?.moduleName,
2331-
location: definitionLocation ?? location, // Use occurrence location as fallback
2298+
definition: definition,
2299+
moduleName: definition.location.moduleName,
23322300
index: index
23332301
)
23342302
}
@@ -2352,12 +2320,15 @@ extension SourceKitLSPServer {
23522320

23532321
// TODO: Remove this workaround once https://github.com/swiftlang/swift/issues/75600 is fixed
23542322
func indexToLSPTypeHierarchyItem2(
2355-
symbol: Symbol,
2323+
definition: SymbolOccurrence,
23562324
moduleName: String?,
2357-
location: Location,
23582325
index: CheckedIndex
2359-
) -> TypeHierarchyItem {
2360-
return self.indexToLSPTypeHierarchyItem(symbol: symbol, moduleName: moduleName, location: location, index: index)
2326+
) -> TypeHierarchyItem? {
2327+
return self.indexToLSPTypeHierarchyItem(
2328+
definition: definition,
2329+
moduleName: moduleName,
2330+
index: index
2331+
)
23612332
}
23622333

23632334
// Convert occurrences to type hierarchy items
@@ -2368,20 +2339,18 @@ extension SourceKitLSPServer {
23682339
// to.
23692340
logger.fault("Expected at most extendedBy or baseOf relation but got \(occurrence.relations.count)")
23702341
}
2371-
guard let related = occurrence.relations.sorted().first, let location = indexToLSPLocation2(occurrence.location)
2372-
else {
2342+
guard let related = occurrence.relations.sorted().first else {
23732343
return nil
23742344
}
23752345

23762346
// Resolve the subtype's definition to find its location
2377-
let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: related.symbol.usr)
2378-
let definitionSymbolLocation = definition.map(\.location)
2379-
let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2)
2347+
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: related.symbol.usr) else {
2348+
return nil
2349+
}
23802350

23812351
return indexToLSPTypeHierarchyItem2(
2382-
symbol: related.symbol,
2383-
moduleName: definitionSymbolLocation?.moduleName,
2384-
location: definitionLocation ?? location, // Use occurrence location as fallback
2352+
definition: definition,
2353+
moduleName: definition.location.moduleName,
23852354
index: index
23862355
)
23872356
}
@@ -2426,24 +2395,23 @@ private let maxWorkspaceSymbolResults = 4096
24262395
package typealias Diagnostic = LanguageServerProtocol.Diagnostic
24272396

24282397
fileprivate extension CheckedIndex {
2429-
/// Get the name of the symbol that is a parent of this symbol, if one exists
2430-
func containerName(of symbol: SymbolOccurrence) -> String? {
2398+
func containerNames(of symbol: SymbolOccurrence) -> [String] {
24312399
// The container name of accessors is the container of the surrounding variable.
24322400
let accessorOf = symbol.relations.filter { $0.roles.contains(.accessorOf) }
24332401
if let primaryVariable = accessorOf.sorted().first {
24342402
if accessorOf.count > 1 {
24352403
logger.fault("Expected an occurrence to an accessor of at most one symbol, not multiple")
24362404
}
24372405
if let primaryVariable = primaryDefinitionOrDeclarationOccurrence(ofUSR: primaryVariable.symbol.usr) {
2438-
return containerName(of: primaryVariable)
2406+
return containerNames(of: primaryVariable)
24392407
}
24402408
}
24412409

24422410
let containers = symbol.relations.filter { $0.roles.contains(.childOf) }
24432411
if containers.count > 1 {
24442412
logger.fault("Expected an occurrence to a child of at most one symbol, not multiple")
24452413
}
2446-
return containers.filter {
2414+
let container = containers.filter {
24472415
switch $0.symbol.kind {
24482416
case .module, .namespace, .enum, .struct, .class, .protocol, .extension, .union:
24492417
return true
@@ -2452,7 +2420,39 @@ fileprivate extension CheckedIndex {
24522420
.destructor, .conversionFunction, .parameter, .using, .concept, .commentTag:
24532421
return false
24542422
}
2455-
}.sorted().first?.symbol.name
2423+
}.sorted().first
2424+
2425+
if let container {
2426+
if let containerDefinition = primaryDefinitionOrDeclarationOccurrence(ofUSR: container.symbol.usr) {
2427+
return self.containerNames(of: containerDefinition) + [container.symbol.name]
2428+
}
2429+
return [container.symbol.name]
2430+
} else {
2431+
return []
2432+
}
2433+
}
2434+
2435+
/// Take the name of containers into account to form a fully-qualified name for the given symbol.
2436+
/// This means that we will form names of nested types and type-qualify methods.
2437+
func fullyQualifiedName(of symbolOccurrence: SymbolOccurrence) -> String {
2438+
let symbol = symbolOccurrence.symbol
2439+
let containerNames = containerNames(of: symbolOccurrence)
2440+
guard let containerName = containerNames.last else {
2441+
// No containers, so nothing to do.
2442+
return symbol.name
2443+
}
2444+
switch symbol.language {
2445+
case .objc where symbol.kind == .instanceMethod || symbol.kind == .instanceProperty:
2446+
return "-[\(containerName) \(symbol.name)]"
2447+
case .objc where symbol.kind == .classMethod || symbol.kind == .classProperty:
2448+
return "+[\(containerName) \(symbol.name)]"
2449+
case .cxx, .c, .objc:
2450+
// C shouldn't have container names for call hierarchy and Objective-C should be covered above.
2451+
// Fall back to using the C++ notation using `::`.
2452+
return (containerNames + [symbol.name]).joined(separator: "::")
2453+
case .swift:
2454+
return (containerNames + [symbol.name]).joined(separator: ".")
2455+
}
24562456
}
24572457
}
24582458

Tests/SourceKitLSPTests/CallHierarchyTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ final class CallHierarchyTests: XCTestCase {
833833
[
834834
CallHierarchyIncomingCall(
835835
from: CallHierarchyItem(
836-
name: "Bar.init()",
836+
name: "Outer.Bar.init()",
837837
kind: .constructor,
838838
tags: nil,
839839
uri: project.fileURI,

0 commit comments

Comments
 (0)