@@ -1961,18 +1961,19 @@ extension SourceKitServer {
1961
1961
position: req. position
1962
1962
)
1963
1963
)
1964
- guard let symbol = symbols. first,
1965
- let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index
1966
- else {
1964
+ guard let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index else {
1967
1965
return nil
1968
1966
}
1969
- guard let usr = symbol. usr else { return nil }
1970
- var occurrences = index. occurrences ( ofUSR: usr, roles: . baseOf)
1971
- if occurrences. isEmpty {
1972
- occurrences = index. occurrences ( relatedToUSR: usr, roles: . overrideOf)
1973
- }
1967
+ let locations = symbols. flatMap { ( symbol) -> [ Location ] in
1968
+ guard let usr = symbol. usr else { return [ ] }
1969
+ var occurrences = index. occurrences ( ofUSR: usr, roles: . baseOf)
1970
+ if occurrences. isEmpty {
1971
+ occurrences = index. occurrences ( relatedToUSR: usr, roles: . overrideOf)
1972
+ }
1974
1973
1975
- return . locations( occurrences. compactMap { indexToLSPLocation ( $0. location) } . sorted ( ) )
1974
+ return occurrences. compactMap { indexToLSPLocation ( $0. location) }
1975
+ }
1976
+ return . locations( locations. sorted ( ) )
1976
1977
}
1977
1978
1978
1979
func references(
@@ -1986,17 +1987,19 @@ extension SourceKitServer {
1986
1987
position: req. position
1987
1988
)
1988
1989
)
1989
- guard let symbol = symbols. first, let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index
1990
- else {
1990
+ guard let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index else {
1991
1991
return [ ]
1992
1992
}
1993
- guard let usr = symbol. usr else { return [ ] }
1994
- logger. info ( " performing indexed jump-to-def with usr \( usr) " )
1995
- var roles : SymbolRole = [ . reference]
1996
- if req. context. includeDeclaration {
1997
- roles. formUnion ( [ . declaration, . definition] )
1993
+ let locations = symbols. flatMap { ( symbol) -> [ Location ] in
1994
+ guard let usr = symbol. usr else { return [ ] }
1995
+ logger. info ( " Finding references for USR \( usr) " )
1996
+ var roles : SymbolRole = [ . reference]
1997
+ if req. context. includeDeclaration {
1998
+ roles. formUnion ( [ . declaration, . definition] )
1999
+ }
2000
+ return index. occurrences ( ofUSR: usr, roles: roles) . compactMap { indexToLSPLocation ( $0. location) }
1998
2001
}
1999
- return index . occurrences ( ofUSR : usr , roles : roles ) . compactMap { indexToLSPLocation ( $0 . location ) } . sorted ( )
2002
+ return locations . sorted ( )
2000
2003
}
2001
2004
2002
2005
private func indexToLSPCallHierarchyItem(
@@ -2031,29 +2034,31 @@ extension SourceKitServer {
2031
2034
position: req. position
2032
2035
)
2033
2036
)
2034
- guard let symbol = symbols. first, let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index
2035
- else {
2037
+ guard let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index else {
2036
2038
return nil
2037
2039
}
2038
2040
// For call hierarchy preparation we only locate the definition
2039
- guard let usr = symbol . usr else { return nil }
2041
+ let usrs = symbols . compactMap ( \ . usr)
2040
2042
2041
2043
// Only return a single call hierarchy item. Returning multiple doesn't make sense because they will all have the
2042
2044
// same USR (because we query them by USR) and will thus expand to the exact same call hierarchy.
2043
- // Also, VS Code doesn't seem to like multiple call hierarchy items being returned and fails to display any results
2044
- // if they are, failing with `Cannot read properties of undefined (reading 'map')`.
2045
- guard let definition = index. definitionOrDeclarationOccurrences ( ofUSR: usr) . first else {
2046
- return nil
2047
- }
2048
- guard let location = indexToLSPLocation ( definition. location) else {
2049
- return nil
2050
- }
2051
- let callHierarchyItem = self . indexToLSPCallHierarchyItem (
2052
- symbol: definition. symbol,
2053
- moduleName: definition. location. moduleName,
2054
- location: location
2055
- )
2056
- return [ callHierarchyItem]
2045
+ var callHierarchyItems = usrs. compactMap { ( usr) -> CallHierarchyItem ? in
2046
+ guard let definition = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: usr) else {
2047
+ return nil
2048
+ }
2049
+ guard let location = indexToLSPLocation ( definition. location) else {
2050
+ return nil
2051
+ }
2052
+ return self . indexToLSPCallHierarchyItem (
2053
+ symbol: definition. symbol,
2054
+ moduleName: definition. location. moduleName,
2055
+ location: location
2056
+ )
2057
+ } . sorted ( by: { Location ( uri: $0. uri, range: $0. range) < Location ( uri: $1. uri, range: $1. range) } )
2058
+
2059
+ // Ideally, we should show multiple symbols. But VS Code fails to display call hierarchies with multiple root items,
2060
+ // failing with `Cannot read properties of undefined (reading 'map')`. Pick the first one.
2061
+ return Array ( callHierarchyItems. prefix ( 1 ) )
2057
2062
}
2058
2063
2059
2064
/// Extracts our implementation-specific data about a call hierarchy
@@ -2089,7 +2094,7 @@ extension SourceKitServer {
2089
2094
return occurrence. relations. filter { $0. symbol. kind. isCallable }
2090
2095
. map { related in
2091
2096
// Resolve the caller's definition to find its location
2092
- let definition = index. occurrences ( ofUSR: related. symbol. usr, roles : [ . definition , . declaration ] ) . first
2097
+ let definition = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: related. symbol. usr)
2093
2098
let definitionSymbolLocation = definition? . location
2094
2099
let definitionLocation = definitionSymbolLocation. flatMap ( indexToLSPLocation)
2095
2100
@@ -2120,7 +2125,7 @@ extension SourceKitServer {
2120
2125
}
2121
2126
2122
2127
// Resolve the callee's definition to find its location
2123
- let definition = index. occurrences ( ofUSR: occurrence. symbol. usr, roles : [ . definition , . declaration ] ) . first
2128
+ let definition = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: occurrence. symbol. usr)
2124
2129
let definitionSymbolLocation = definition? . location
2125
2130
let definitionLocation = definitionSymbolLocation. flatMap ( indexToLSPLocation)
2126
2131
@@ -2196,26 +2201,42 @@ extension SourceKitServer {
2196
2201
position: req. position
2197
2202
)
2198
2203
)
2199
- guard let symbol = symbols. first else {
2204
+ guard ! symbols. isEmpty else {
2200
2205
return nil
2201
2206
}
2202
2207
guard let index = await self . workspaceForDocument ( uri: req. textDocument. uri) ? . index else {
2203
2208
return nil
2204
2209
}
2205
- guard let usr = symbol. usr else { return nil }
2206
- return index. occurrences ( ofUSR: usr, roles: [ . definition, . declaration] )
2207
- . compactMap { info -> TypeHierarchyItem ? in
2208
- guard let location = indexToLSPLocation ( info. location) else {
2209
- return nil
2210
+ let usrs =
2211
+ symbols
2212
+ . filter {
2213
+ // Only include references to type. For example, we don't want to find the type hierarchy of a constructor when
2214
+ // starting the type hierarchy on `Foo()``.
2215
+ switch $0. kind {
2216
+ case . class, . enum, . interface, . struct: return true
2217
+ default : return false
2210
2218
}
2211
- return self . indexToLSPTypeHierarchyItem (
2212
- symbol: info. symbol,
2213
- moduleName: info. location. moduleName,
2214
- location: location,
2215
- index: index
2216
- )
2217
2219
}
2218
- . sorted ( by: { $0. name < $1. name } )
2220
+ . compactMap ( \. usr)
2221
+ let typeHierarchyItems = usrs. compactMap { ( usr) -> TypeHierarchyItem ? in
2222
+ guard
2223
+ let info = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: usr) ,
2224
+ let location = indexToLSPLocation ( info. location)
2225
+ else {
2226
+ return nil
2227
+ }
2228
+ return self . indexToLSPTypeHierarchyItem (
2229
+ symbol: info. symbol,
2230
+ moduleName: info. location. moduleName,
2231
+ location: location,
2232
+ index: index
2233
+ )
2234
+ }
2235
+ . sorted ( by: { $0. name < $1. name } )
2236
+
2237
+ // Ideally, we should show multiple symbols. But VS Code fails to display type hierarchies with multiple root items,
2238
+ // failing with `Cannot read properties of undefined (reading 'map')`. Pick the first one.
2239
+ return Array ( typeHierarchyItems. prefix ( 1 ) )
2219
2240
}
2220
2241
2221
2242
/// Extracts our implementation-specific data about a type hierarchy
@@ -2249,7 +2270,12 @@ extension SourceKitServer {
2249
2270
// Resolve retroactive conformances via the extensions
2250
2271
let extensions = index. occurrences ( ofUSR: data. usr, roles: . extendedBy)
2251
2272
let retroactiveConformanceOccurs = extensions. flatMap { occurrence -> [ SymbolOccurrence ] in
2252
- guard let related = occurrence. relations. first else {
2273
+ if occurrence. relations. count > 1 {
2274
+ // When the occurrence has an `extendedBy` relation, it's an extension declaration. An extension can only extend
2275
+ // a single type, so there can only be a single relation here.
2276
+ logger. fault ( " Expected at most extendedBy relation but got \( occurrence. relations. count) " )
2277
+ }
2278
+ guard let related = occurrence. relations. sorted ( ) . first else {
2253
2279
return [ ]
2254
2280
}
2255
2281
return index. occurrences ( relatedToUSR: related. symbol. usr, roles: . baseOf)
@@ -2263,7 +2289,7 @@ extension SourceKitServer {
2263
2289
}
2264
2290
2265
2291
// Resolve the supertype's definition to find its location
2266
- let definition = index. occurrences ( ofUSR: occurrence. symbol. usr, roles : [ . definition , . declaration ] ) . first
2292
+ let definition = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: occurrence. symbol. usr)
2267
2293
let definitionSymbolLocation = definition? . location
2268
2294
let definitionLocation = definitionSymbolLocation. flatMap ( indexToLSPLocation)
2269
2295
@@ -2289,14 +2315,19 @@ extension SourceKitServer {
2289
2315
2290
2316
// Convert occurrences to type hierarchy items
2291
2317
let types = occurs. compactMap { occurrence -> TypeHierarchyItem ? in
2292
- guard let location = indexToLSPLocation ( occurrence. location) ,
2293
- let related = occurrence. relations. first
2318
+ if occurrence. relations. count > 1 {
2319
+ // An occurrence with a `baseOf` or `extendedBy` relation is an occurrence inside an inheritance clause.
2320
+ // Such an occurrence can only be the source of a single type, namely the one that the inheritance clause belongs
2321
+ // to.
2322
+ logger. fault ( " Expected at most extendedBy or baseOf relation but got \( occurrence. relations. count) " )
2323
+ }
2324
+ guard let related = occurrence. relations. sorted ( ) . first, let location = indexToLSPLocation ( occurrence. location)
2294
2325
else {
2295
2326
return nil
2296
2327
}
2297
2328
2298
2329
// Resolve the subtype's definition to find its location
2299
- let definition = index. occurrences ( ofUSR: related. symbol. usr, roles : [ . definition , . declaration ] ) . first
2330
+ let definition = index. primaryDefinitionOrDeclarationOccurrence ( ofUSR: related. symbol. usr)
2300
2331
let definitionSymbolLocation = definition. map ( \. location)
2301
2332
let definitionLocation = definitionSymbolLocation. flatMap ( indexToLSPLocation)
2302
2333
@@ -2348,6 +2379,14 @@ fileprivate extension IndexStoreDB {
2348
2379
}
2349
2380
return occurrences ( ofUSR: usr, roles: [ . declaration] )
2350
2381
}
2382
+
2383
+ /// Find a `SymbolOccurrence` that is considered the primary definition of the symbol with the given USR.
2384
+ ///
2385
+ /// If the USR has an ambiguous definition, the most important role of this function is to deterministically return
2386
+ /// the same result every time.
2387
+ func primaryDefinitionOrDeclarationOccurrence( ofUSR usr: String ) -> SymbolOccurrence ? {
2388
+ return definitionOrDeclarationOccurrences ( ofUSR: usr) . sorted ( ) . first
2389
+ }
2351
2390
}
2352
2391
2353
2392
extension IndexSymbolKind {
@@ -2394,7 +2433,11 @@ extension IndexSymbolKind {
2394
2433
extension SymbolOccurrence {
2395
2434
/// Get the name of the symbol that is a parent of this symbol, if one exists
2396
2435
func getContainerName( ) -> String ? {
2397
- return relations. first ( where: { $0. roles. contains ( . childOf) } ) ? . symbol. name
2436
+ let containers = relations. filter { $0. roles. contains ( . childOf) }
2437
+ if containers. count > 1 {
2438
+ logger. fault ( " Expected an occurrence to a child of at most one symbol, not multiple " )
2439
+ }
2440
+ return containers. sorted ( ) . first? . symbol. name
2398
2441
}
2399
2442
}
2400
2443
0 commit comments