diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -115,6 +115,8 @@ Callback>); void onGoToDefinition(const TextDocumentPositionParams &, Callback>); + void onGoToImplementation(const TextDocumentPositionParams &, + Callback>); void onReference(const ReferenceParams &, Callback>); void onSwitchSourceHeader(const TextDocumentIdentifier &, Callback>); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -604,6 +604,7 @@ }}, {"declarationProvider", true}, {"definitionProvider", true}, + {"implementationProvider", true}, {"documentHighlightProvider", true}, {"documentLinkProvider", llvm::json::Object{ @@ -1291,6 +1292,22 @@ }); } +void ClangdLSPServer::onGoToImplementation( + const TextDocumentPositionParams &Params, + Callback> Reply) { + Server->findImplementations( + Params.textDocument.uri.file(), Params.position, + [Reply = std::move(Reply)]( + llvm::Expected> Overrides) mutable { + if (!Overrides) + return Reply(Overrides.takeError()); + std::vector Impls; + for (const LocatedSymbol &Sym : *Overrides) + Impls.push_back(Sym.PreferredDeclaration); + return Reply(std::move(Impls)); + }); +} + void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, Callback> Reply) { Server->symbolInfo(Params.textDocument.uri.file(), Params.position, @@ -1431,6 +1448,7 @@ MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp); MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition); MsgHandler->bind("textDocument/declaration", &ClangdLSPServer::onGoToDeclaration); + MsgHandler->bind("textDocument/implementation", &ClangdLSPServer::onGoToImplementation); MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference); MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader); MsgHandler->bind("textDocument/prepareRename", &ClangdLSPServer::onPrepareRename); diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -253,6 +253,10 @@ /// Retrieve ranges that can be used to fold code within the specified file. void foldingRanges(StringRef File, Callback> CB); + /// Retrieve implementations for virtual method. + void findImplementations(PathRef File, Position Pos, + Callback> CB); + /// Retrieve locations for symbol references. void findReferences(PathRef File, Position Pos, uint32_t Limit, Callback CB); diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -718,6 +718,18 @@ TUScheduler::InvalidateOnUpdate); } +void ClangdServer::findImplementations( + PathRef File, Position Pos, Callback> CB) { + auto Action = [Pos, CB = std::move(CB), + this](llvm::Expected InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::findImplementations(InpAST->AST, Pos, Index)); + }; + + WorkScheduler.runWithAST("Implementations", File, std::move(Action)); +} + void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, Callback CB) { auto Action = [Pos, Limit, CB = std::move(CB), diff --git a/clang-tools-extra/clangd/test/implementations.test b/clang-tools-extra/clangd/test/implementations.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/implementations.test @@ -0,0 +1,39 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Parent { virtual void Foo(); };\nstruct Child1 : Parent { void Foo() override(); };\nstruct Child2 : Parent { void Foo() override(); };"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/implementation","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":32}}} +# CHECK: "id": 1 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 33, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 30, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 33, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 30, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/clang-tools-extra/clangd/test/initialize-params.test @@ -67,7 +67,8 @@ # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "hoverProvider": true, -# CHECK-NEXT: "memoryUsageProvider": true +# CHECK-NEXT: "implementationProvider": true, +# CHECK-NEXT: "memoryUsageProvider": true, # CHECK-NEXT: "referencesProvider": true, # CHECK-NEXT: "renameProvider": true, # CHECK-NEXT: "selectionRangeProvider": true,