Skip to content

Commit 2bf4776

Browse files
authored
Merge pull request #1868 from ahoppen/only-calls-in-call-hierarchy
Only show call-like occurrences in call hierarchy.
2 parents 29619a6 + 751291e commit 2bf4776

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,7 @@ extension SourceKitLSPServer {
20312031
// callOccurrences are all the places that any of the USRs in callableUsrs is called.
20322032
// We also load the `calledBy` roles to get the method that contains the reference to this call.
20332033
let callOccurrences = callableUsrs.flatMap { index.occurrences(ofUSR: $0, roles: .containedBy) }
2034+
.filter(\.shouldShowInCallHierarchy)
20342035

20352036
// Maps functions that call a USR in `callableUSRs` to all the called occurrences of `callableUSRs` within the
20362037
// function. If a function `foo` calls `bar` multiple times, `callersToCalls[foo]` will contain two call
@@ -2101,6 +2102,7 @@ extension SourceKitLSPServer {
21012102

21022103
let callableUsrs = [data.usr] + index.occurrences(relatedToUSR: data.usr, roles: .accessorOf).map(\.symbol.usr)
21032104
let callOccurrences = callableUsrs.flatMap { index.occurrences(relatedToUSR: $0, roles: .containedBy) }
2105+
.filter(\.shouldShowInCallHierarchy)
21042106
let calls = callOccurrences.compactMap { occurrence -> CallHierarchyOutgoingCall? in
21052107
guard occurrence.symbol.kind.isCallable else {
21062108
return nil
@@ -2504,6 +2506,13 @@ extension IndexSymbolKind {
25042506
}
25052507
}
25062508

2509+
fileprivate extension SymbolOccurrence {
2510+
/// Whether this is a call-like occurrence that should be shown in the call hierarchy.
2511+
var shouldShowInCallHierarchy: Bool {
2512+
!roles.intersection([.addressOf, .call, .read, .reference, .write]).isEmpty
2513+
}
2514+
}
2515+
25072516
/// Simple struct for pending notifications/requests, including a cancellation handler.
25082517
/// For convenience the notifications/request handlers are type erased via wrapping.
25092518
fileprivate struct NotificationRequestOperation {

Tests/SourceKitLSPTests/CallHierarchyTests.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,4 +895,56 @@ final class CallHierarchyTests: XCTestCase {
895895
]
896896
)
897897
}
898+
899+
func testOnlyConsiderCallsAsIncomingCallOccurrences() async throws {
900+
try await SkipUnless.indexOnlyHasContainedByRelationsToIndexedDecls()
901+
902+
// extension MyTask: AnyTask {} includes an occurrence of `MyTask.cancel` to mark it as an override of
903+
// `AnyTask.cancel` but we shouldn't show the extension in the call hierarchy.
904+
let project = try await IndexedSingleSwiftFileTestProject(
905+
"""
906+
struct MyTask {
907+
func cancel() {}
908+
}
909+
910+
protocol AnyTask {
911+
func cancel()
912+
}
913+
914+
extension MyTask: AnyTask {}
915+
916+
func 2️⃣foo(task: MyTask)3️⃣ {
917+
task.1️⃣cancel()
918+
}
919+
"""
920+
)
921+
let prepare = try await project.testClient.send(
922+
CallHierarchyPrepareRequest(
923+
textDocument: TextDocumentIdentifier(project.fileURI),
924+
position: project.positions["1️⃣"]
925+
)
926+
)
927+
let initialItem = try XCTUnwrap(prepare?.only)
928+
let calls = try await project.testClient.send(CallHierarchyIncomingCallsRequest(item: initialItem))
929+
XCTAssertEqual(
930+
calls,
931+
[
932+
CallHierarchyIncomingCall(
933+
from: CallHierarchyItem(
934+
name: "foo(task:)",
935+
kind: .function,
936+
tags: nil,
937+
uri: project.fileURI,
938+
range: Range(project.positions["2️⃣"]),
939+
selectionRange: Range(project.positions["2️⃣"]),
940+
data: .dictionary([
941+
"usr": .string("s:4test3foo4taskyAA6MyTaskV_tF"),
942+
"uri": .string(project.fileURI.stringValue),
943+
])
944+
),
945+
fromRanges: [Range(project.positions["1️⃣"])]
946+
)
947+
]
948+
)
949+
}
898950
}

0 commit comments

Comments
 (0)