Skip to content

Commit 0c42c01

Browse files
committed
Show protocol requirements satisfied in extensions in jump-to-definition
The key issue here was that we were looking at the symbol occurrence that had the `overrideOf` role and were using this symbol occurrence to also check that it’s a child of one of the receiver types. But if the protocol requirement is satisfied in an extension, we have an implicit symbol occurrence at the location where the protocol requirement is stated but the real declaration’s occurrence is inside the extension. The fix here is to just extract the USR from the `overrideOf` relation and then do another index lookup to find the symbol’s definition. rdar://129412428
1 parent 518aac4 commit 0c42c01

File tree

2 files changed

+33
-6
lines changed

2 files changed

+33
-6
lines changed

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,9 +1717,12 @@ extension SourceKitLSPServer {
17171717
}
17181718
}()
17191719
occurrences += occurrences.flatMap {
1720-
let overrides = index.occurrences(relatedToUSR: $0.symbol.usr, roles: .overrideOf)
1721-
// Only contain overrides that are children of one of the receiver types or their subtypes.
1722-
return overrides.filter { override in
1720+
let overriddenUsrs = index.occurrences(relatedToUSR: $0.symbol.usr, roles: .overrideOf).map(\.symbol.usr)
1721+
let overriddenSymbolDefinitions = overriddenUsrs.compactMap {
1722+
index.primaryDefinitionOrDeclarationOccurrence(ofUSR: $0)
1723+
}
1724+
// Only contain overrides that are children of one of the receiver types or their subtypes or extensions.
1725+
return overriddenSymbolDefinitions.filter { override in
17231726
override.relations.contains(where: {
17241727
guard $0.roles.contains(.childOf) else {
17251728
return false
@@ -2473,13 +2476,13 @@ fileprivate struct DocumentNotificationRequestQueue {
24732476
}
24742477
}
24752478

2476-
/// Returns the USRs of the subtypes of `usrs` as well as their subtypes, transitively.
2479+
/// Returns the USRs of the subtypes of `usrs` as well as their subtypes and extensions, transitively.
24772480
fileprivate func transitiveSubtypeClosure(ofUsrs usrs: [String], index: CheckedIndex) -> [String] {
24782481
var result: [String] = []
24792482
for usr in usrs {
24802483
result.append(usr)
2481-
let directSubtypes = index.occurrences(ofUSR: usr, roles: .baseOf).flatMap { occurrence in
2482-
occurrence.relations.filter { $0.roles.contains(.baseOf) }.map(\.symbol.usr)
2484+
let directSubtypes = index.occurrences(ofUSR: usr, roles: [.baseOf, .extendedBy]).flatMap { occurrence in
2485+
occurrence.relations.filter { $0.roles.contains(.baseOf) || $0.roles.contains(.extendedBy) }.map(\.symbol.usr)
24832486
}
24842487
let transitiveSubtypes = transitiveSubtypeClosure(ofUsrs: directSubtypes, index: index)
24852488
result += transitiveSubtypes

Tests/SourceKitLSPTests/DefinitionTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,28 @@ class DefinitionTests: XCTestCase {
601601
])
602602
)
603603
}
604+
605+
func testJumpToSatisfiedProtocolRequirementInExtension() async throws {
606+
let project = try await IndexedSingleSwiftFileTestProject(
607+
"""
608+
protocol TestProtocol {
609+
func 1️⃣doThing()
610+
}
611+
612+
struct TestImpl: TestProtocol {}
613+
extension TestImpl {
614+
func 2️⃣doThing() { }
615+
}
616+
"""
617+
)
618+
619+
let response = try await project.testClient.send(
620+
DefinitionRequest(textDocument: TextDocumentIdentifier(project.fileURI), position: project.positions["1️⃣"])
621+
)
622+
guard case .locations(let locations) = response else {
623+
XCTFail("Expected locations response")
624+
return
625+
}
626+
XCTAssertEqual(locations, [Location(uri: project.fileURI, range: Range(project.positions["2️⃣"]))])
627+
}
604628
}

0 commit comments

Comments
 (0)