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 @@ -114,6 +114,8 @@ Callback>); void onGoToDefinition(const TextDocumentPositionParams &, Callback>); + void onImplementation(const ImplementationParams &, + 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 @@ -612,6 +612,7 @@ {"selectionRangeProvider", true}, {"documentSymbolProvider", true}, {"workspaceSymbolProvider", true}, + {"implementationProvider", true}, {"referencesProvider", true}, {"executeCommandProvider", llvm::json::Object{ @@ -1288,6 +1289,21 @@ }); } +void ClangdLSPServer::onImplementation(const ImplementationParams &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, @@ -1424,6 +1440,7 @@ MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition); MsgHandler->bind("textDocument/declaration", &ClangdLSPServer::onGoToDeclaration); MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference); + MsgHandler->bind("textDocument/implementation", &ClangdLSPServer::onImplementation); MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader); MsgHandler->bind("textDocument/prepareRename", &ClangdLSPServer::onPrepareRename); MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename); 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 @@ -717,6 +717,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/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1479,6 +1479,10 @@ }; bool fromJSON(const llvm::json::Value &, ReferenceParams &, llvm::json::Path); +struct ImplementationParams : public TextDocumentPositionParams {}; +bool fromJSON(const llvm::json::Value &, ImplementationParams &, + llvm::json::Path); + /// Clangd extension: indicates the current state of the file in clangd, /// sent from server via the `textDocument/clangd.fileStatus` notification. struct FileStatus { diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1261,6 +1261,12 @@ return llvm::json::Object{{"to", C.to}, {"fromRanges", C.fromRanges}}; } +bool fromJSON(const llvm::json::Value &Params, ImplementationParams &R, + llvm::json::Path P) { + TextDocumentPositionParams &Base = R; + return fromJSON(Params, Base, P); +} + static const char *toString(OffsetEncoding OE) { switch (OE) { case OffsetEncoding::UTF8: 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 @@ -66,7 +66,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,