Skip to content

Commit 755868a

Browse files
authored
Merge pull request #1074 from ahoppen/ahoppen/dont-report-definition-and-declaration
Only report a single item in prepareCallHierarchy
2 parents 4269e90 + 82936eb commit 755868a

File tree

2 files changed

+78
-22
lines changed

2 files changed

+78
-22
lines changed

Sources/SourceKitLSP/SourceKitServer.swift

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,10 +1752,7 @@ extension SourceKitServer {
17521752
}
17531753
guard let usr = symbol.usr else { return [] }
17541754
logger.info("performing indexed jump-to-def with usr \(usr)")
1755-
var occurrences = index.occurrences(ofUSR: usr, roles: [.definition])
1756-
if occurrences.isEmpty {
1757-
occurrences = index.occurrences(ofUSR: usr, roles: [.declaration])
1758-
}
1755+
var occurrences = index.definitionOrDeclarationOccurrences(ofUSR: usr)
17591756
if symbol.isDynamic ?? true {
17601757
lazy var transitiveReceiverUsrs: [String]? = {
17611758
if let receiverUsrs = symbol.receiverUsrs {
@@ -1861,7 +1858,7 @@ extension SourceKitServer {
18611858
/// to get to the definition of `symbolUSR`.
18621859
///
18631860
/// `originatorUri` is the URI of the file from which the definition request is performed. It is used to determine the
1864-
/// compiler arguments to generate the generated inteface.
1861+
/// compiler arguments to generate the generated interface.
18651862
func definitionInInterface(
18661863
moduleName: String,
18671864
symbolUSR: String?,
@@ -1898,12 +1895,12 @@ extension SourceKitServer {
18981895
return nil
18991896
}
19001897
guard let usr = symbol.usr else { return nil }
1901-
var occurrances = index.occurrences(ofUSR: usr, roles: .baseOf)
1902-
if occurrances.isEmpty {
1903-
occurrances = index.occurrences(relatedToUSR: usr, roles: .overrideOf)
1898+
var occurrences = index.occurrences(ofUSR: usr, roles: .baseOf)
1899+
if occurrences.isEmpty {
1900+
occurrences = index.occurrences(relatedToUSR: usr, roles: .overrideOf)
19041901
}
19051902

1906-
return .locations(occurrances.compactMap { indexToLSPLocation($0.location) })
1903+
return .locations(occurrences.compactMap { indexToLSPLocation($0.location) })
19071904
}
19081905

19091906
func references(
@@ -1968,17 +1965,23 @@ extension SourceKitServer {
19681965
}
19691966
// For call hierarchy preparation we only locate the definition
19701967
guard let usr = symbol.usr else { return nil }
1971-
return index.occurrences(ofUSR: usr, roles: [.definition, .declaration])
1972-
.compactMap { info -> CallHierarchyItem? in
1973-
guard let location = indexToLSPLocation(info.location) else {
1974-
return nil
1975-
}
1976-
return self.indexToLSPCallHierarchyItem(
1977-
symbol: info.symbol,
1978-
moduleName: info.location.moduleName,
1979-
location: location
1980-
)
1981-
}
1968+
1969+
// Only return a single call hierarchy item. Returning multiple doesn't make sense because they will all have the
1970+
// same USR (because we query them by USR) and will thus expand to the exact same call hierarchy.
1971+
// Also, VS Code doesn't seem to like multiple call hierarchy items being returned and fails to display any results
1972+
// if they are, failing with `Cannot read properties of undefined (reading 'map')`.
1973+
guard let definition = index.definitionOrDeclarationOccurrences(ofUSR: usr).first else {
1974+
return nil
1975+
}
1976+
guard let location = indexToLSPLocation(definition.location) else {
1977+
return nil
1978+
}
1979+
let callHierarchyItem = self.indexToLSPCallHierarchyItem(
1980+
symbol: definition.symbol,
1981+
moduleName: definition.location.moduleName,
1982+
location: location
1983+
)
1984+
return [callHierarchyItem]
19821985
}
19831986

19841987
/// Extracts our implementation-specific data about a call hierarchy
@@ -2260,6 +2263,18 @@ private let maxWorkspaceSymbolResults = 4096
22602263

22612264
public typealias Diagnostic = LanguageServerProtocol.Diagnostic
22622265

2266+
fileprivate extension IndexStoreDB {
2267+
/// If there are any definition occurrences of the given USR, return these.
2268+
/// Otherwise return declaration occurrences.
2269+
func definitionOrDeclarationOccurrences(ofUSR usr: String) -> [SymbolOccurrence] {
2270+
let definitions = occurrences(ofUSR: usr, roles: [.definition])
2271+
if !definitions.isEmpty {
2272+
return definitions
2273+
}
2274+
return occurrences(ofUSR: usr, roles: [.declaration])
2275+
}
2276+
}
2277+
22632278
extension IndexSymbolKind {
22642279
func asLspSymbolKind() -> SymbolKind {
22652280
switch self {
@@ -2333,8 +2348,8 @@ fileprivate func transitiveSubtypeClosure(ofUsrs usrs: [String], index: IndexSto
23332348
var result: [String] = []
23342349
for usr in usrs {
23352350
result.append(usr)
2336-
let directSubtypes = index.occurrences(ofUSR: usr, roles: .baseOf).flatMap { occurance in
2337-
occurance.relations.filter { $0.roles.contains(.baseOf) }.map(\.symbol.usr)
2351+
let directSubtypes = index.occurrences(ofUSR: usr, roles: .baseOf).flatMap { occurrence in
2352+
occurrence.relations.filter { $0.roles.contains(.baseOf) }.map(\.symbol.usr)
23382353
}
23392354
let transitiveSubtypes = transitiveSubtypeClosure(ofUsrs: directSubtypes, index: index)
23402355
result += transitiveSubtypes

Tests/SourceKitLSPTests/CallHierarchyTests.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,45 @@ final class CallHierarchyTests: XCTestCase {
183183
]
184184
)
185185
}
186+
187+
func testReportSingleItemInPrepareCallHierarchy() async throws {
188+
let ws = try await SwiftPMTestWorkspace(
189+
files: [
190+
"MyLibrary/include/lib.h": """
191+
struct FilePathIndex {
192+
void 1️⃣foo();
193+
};
194+
""",
195+
"MyLibrary/lib.cpp": """
196+
#include "lib.h"
197+
void FilePathIndex::2️⃣foo() {}
198+
""",
199+
],
200+
build: true
201+
)
202+
let (uri, positions) = try ws.openDocument("lib.h", language: .cpp)
203+
let result = try await ws.testClient.send(
204+
CallHierarchyPrepareRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
205+
)
206+
207+
// Test that we don't provide both the definition in .cpp and the declaration on .h
208+
XCTAssertEqual(
209+
result,
210+
[
211+
CallHierarchyItem(
212+
name: "foo",
213+
kind: .method,
214+
tags: nil,
215+
detail: "",
216+
uri: try ws.uri(for: "lib.cpp"),
217+
range: try Range(ws.position(of: "2️⃣", in: "lib.cpp")),
218+
selectionRange: try Range(ws.position(of: "2️⃣", in: "lib.cpp")),
219+
data: LSPAny.dictionary([
220+
"usr": .string("c:@S@FilePathIndex@F@foo#"),
221+
"uri": .string(try ws.uri(for: "lib.cpp").stringValue),
222+
])
223+
)
224+
]
225+
)
226+
}
186227
}

0 commit comments

Comments
 (0)