Skip to content

Commit 2840c7d

Browse files
authored
Forward definition requests when index lookup fails (#294)
* Forward definition requests when index lookup fails If we're unable to look up a definition or declaration via the index, forward the request to the language service. This allows clangd to support go to definition or declaration for symbols defined/declared in the AST.
1 parent 8449b1c commit 2840c7d

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

Sources/SourceKitLSP/SourceKitServer.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -801,22 +801,32 @@ extension SourceKitServer {
801801
) {
802802
let symbolInfo = SymbolInfoRequest(textDocument: req.params.textDocument, position: req.params.position)
803803
let index = self.workspace?.index
804+
// If we're unable to handle the definition request using our index, see if the
805+
// language service can handle it (e.g. clangd can provide AST based definitions).
806+
let resultHandler: ([Location], ResponseError?) -> Void = { (locs, error) in
807+
guard locs.isEmpty else {
808+
req.reply(.locations(locs))
809+
return
810+
}
811+
let handled = languageService.definition(req)
812+
guard !handled else { return }
813+
if let error = error {
814+
req.reply(.failure(error))
815+
} else {
816+
req.reply(.locations([]))
817+
}
818+
}
804819
let callback = callbackOnQueue(self.queue) { (result: Result<SymbolInfoRequest.Response, ResponseError>) in
805820
guard let symbols: [SymbolDetails] = result.success ?? nil, let symbol = symbols.first else {
806-
let handled = languageService.definition(req)
807-
guard !handled else { return }
808-
if let error = result.failure {
809-
req.reply(.failure(error))
810-
} else {
811-
req.reply(.locations([]))
812-
}
821+
resultHandler([], result.failure)
813822
return
814823
}
815824

816825
let fallbackLocation = [symbol.bestLocalDeclaration].compactMap { $0 }
817826

818827
guard let usr = symbol.usr, let index = index else {
819-
return req.reply(.locations(fallbackLocation))
828+
resultHandler(fallbackLocation, nil)
829+
return
820830
}
821831

822832
log("performing indexed jump-to-def with usr \(usr)")
@@ -842,7 +852,7 @@ extension SourceKitServer {
842852
)
843853
}
844854

845-
req.reply(.locations(locations.isEmpty ? fallbackLocation : locations))
855+
resultHandler(locations.isEmpty ? fallbackLocation : locations, nil)
846856
}
847857
let request = Request(symbolInfo, id: req.id, clientID: ObjectIdentifier(self),
848858
cancellation: req.cancellationToken, reply: callback)

Tests/INPUTS/BasicCXX/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include /*Object:include:main*/"Object.h"
22

33
int main(int argc, const char *argv[]) {
4-
struct Object *obj = newObject();
4+
struct /*Object:ref:main*/Object *obj = newObject();
55
return obj->field;
66
}

Tests/SourceKitLSPTests/SourceKitTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,36 @@ final class SKTests: XCTestCase {
322322
}
323323
}
324324
}
325+
326+
func testClangdGoToDefinitionWithoutIndex() throws {
327+
guard let ws = try staticSourceKitTibsWorkspace(name: "BasicCXX") else { return }
328+
guard ToolchainRegistry.shared.default?.clangd != nil else { return }
329+
330+
let refLoc = ws.testLoc("Object:ref:main")
331+
let expectedDoc = ws.testLoc("Object").docIdentifier.uri
332+
let refPos = Position(line: refLoc.position.line, utf16index: refLoc.utf16Column + 2)
333+
334+
try ws.openDocument(refLoc.url, language: .c)
335+
336+
let goToDefinition = DefinitionRequest(
337+
textDocument: refLoc.docIdentifier, position: refPos)
338+
let resp = try! ws.sk.sendSync(goToDefinition)
339+
340+
guard let locationsOrLinks = resp else {
341+
XCTFail("No response for go-to-definition")
342+
return
343+
}
344+
switch locationsOrLinks {
345+
case .locations(let locations):
346+
XCTAssert(!locations.isEmpty, "Found no locations for go-to-definition")
347+
if let loc = locations.first {
348+
XCTAssertEqual(loc.uri, expectedDoc)
349+
}
350+
case .locationLinks(let locationLinks):
351+
XCTAssert(!locationLinks.isEmpty, "Found no location links for go-to-definition")
352+
if let link = locationLinks.first {
353+
XCTAssertEqual(link.targetUri, expectedDoc)
354+
}
355+
}
356+
}
325357
}

Tests/SourceKitLSPTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ extension SKTests {
143143
// `swift test --generate-linuxmain`
144144
// to regenerate.
145145
static let __allTests__SKTests = [
146+
("testClangdGoToDefinitionWithoutIndex", testClangdGoToDefinitionWithoutIndex),
146147
("testClangdGoToInclude", testClangdGoToInclude),
147148
("testCodeCompleteSwiftTibs", testCodeCompleteSwiftTibs),
148149
("testDependenciesUpdatedCXXTibs", testDependenciesUpdatedCXXTibs),

0 commit comments

Comments
 (0)