Author: jkorous Date: Tue Nov 27 08:40:46 2018 New Revision: 347675 URL: http://llvm.org/viewvc/llvm-project?rev=347675&view=rev Log: [clangd] textDocument/SymbolInfo extension
New method returning symbol info for given source position. Differential Revision: https://reviews.llvm.org/D54799 rdar://problem/46050281 Added: clang-tools-extra/trunk/test/clangd/symbol-info.test clang-tools-extra/trunk/unittests/clangd/SymbolInfoTests.cpp Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp clang-tools-extra/trunk/clangd/ClangdLSPServer.h clang-tools-extra/trunk/clangd/ClangdServer.cpp clang-tools-extra/trunk/clangd/ClangdServer.h clang-tools-extra/trunk/clangd/Protocol.cpp clang-tools-extra/trunk/clangd/Protocol.h clang-tools-extra/trunk/clangd/XRefs.cpp clang-tools-extra/trunk/clangd/XRefs.h clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Tue Nov 27 08:40:46 2018 @@ -698,6 +698,12 @@ void ClangdLSPServer::onReference(const std::move(Reply)); } +void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, + Callback<std::vector<SymbolDetails>> Reply) { + Server->symbolInfo(Params.textDocument.uri.file(), Params.position, + std::move(Reply)); +} + ClangdLSPServer::ClangdLSPServer(class Transport &Transp, const clangd::CodeCompleteOptions &CCOpts, Optional<Path> CompileCommandsDir, @@ -733,6 +739,7 @@ ClangdLSPServer::ClangdLSPServer(class T MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange); MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent); MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration); + MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo); // clang-format on } Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original) +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Tue Nov 27 08:40:46 2018 @@ -92,6 +92,8 @@ private: void onHover(const TextDocumentPositionParams &, Callback<llvm::Optional<Hover>>); void onChangeConfiguration(const DidChangeConfigurationParams &); + void onSymbolInfo(const TextDocumentPositionParams &, + Callback<std::vector<SymbolDetails>>); std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D); Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Tue Nov 27 08:40:46 2018 @@ -503,6 +503,18 @@ void ClangdServer::findReferences(PathRe WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB))); } +void ClangdServer::symbolInfo(PathRef File, Position Pos, + Callback<std::vector<SymbolDetails>> CB) { + auto Action = [Pos](Callback<std::vector<SymbolDetails>> CB, + Expected<InputsAndAST> InpAST) { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::getSymbolInfo(InpAST->AST, Pos)); + }; + + WorkScheduler.runWithAST("SymbolInfo", File, Bind(Action, std::move(CB))); +} + std::vector<std::pair<Path, std::size_t>> ClangdServer::getUsedBytesPerFile() const { return WorkScheduler.getUsedBytesPerFile(); Modified: clang-tools-extra/trunk/clangd/ClangdServer.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.h (original) +++ clang-tools-extra/trunk/clangd/ClangdServer.h Tue Nov 27 08:40:46 2018 @@ -202,6 +202,11 @@ public: /// Called when an event occurs for a watched file in the workspace. void onFileEvent(const DidChangeWatchedFilesParams &Params); + /// Get symbol info for given position. + /// Clangd extension - not part of official LSP. + void symbolInfo(PathRef File, Position Pos, + Callback<std::vector<SymbolDetails>> CB); + /// Returns estimated memory usage for each of the currently open files. /// The order of results is unspecified. /// Overall memory usage of clangd may be significantly more than reported Modified: clang-tools-extra/trunk/clangd/Protocol.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.cpp (original) +++ clang-tools-extra/trunk/clangd/Protocol.cpp Tue Nov 27 08:40:46 2018 @@ -14,7 +14,9 @@ #include "Protocol.h" #include "Logger.h" #include "URI.h" +#include "index/Index.h" #include "clang/Basic/LLVM.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" @@ -428,6 +430,44 @@ raw_ostream &operator<<(raw_ostream &O, return O; } +bool operator==(const SymbolDetails &LHS, const SymbolDetails &RHS) { + return LHS.name == RHS.name && LHS.containerName == RHS.containerName && + LHS.USR == RHS.USR && LHS.ID == RHS.ID; +} + +llvm::json::Value toJSON(const SymbolDetails &P) { + json::Object result{{"name", llvm::json::Value(nullptr)}, + {"containerName", llvm::json::Value(nullptr)}, + {"usr", llvm::json::Value(nullptr)}, + {"id", llvm::json::Value(nullptr)}}; + + if (!P.name.empty()) + result["name"] = P.name; + + if (!P.containerName.empty()) + result["containerName"] = P.containerName; + + if (!P.USR.empty()) + result["usr"] = P.USR; + + if (P.ID.hasValue()) + result["id"] = P.ID.getValue().str(); + + return result; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const SymbolDetails &S) { + if (!S.containerName.empty()) { + O << S.containerName; + StringRef ContNameRef; + if (!ContNameRef.endswith("::")) { + O << " "; + } + } + O << S.name << " - " << toJSON(S); + return O; +} + bool fromJSON(const json::Value &Params, WorkspaceSymbolParams &R) { json::ObjectMapper O(Params); return O && O.map("query", R.query); Modified: clang-tools-extra/trunk/clangd/Protocol.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.h (original) +++ clang-tools-extra/trunk/clangd/Protocol.h Tue Nov 27 08:40:46 2018 @@ -713,6 +713,26 @@ struct SymbolInformation { llvm::json::Value toJSON(const SymbolInformation &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolInformation &); +/// Represents information about identifier. +/// This is returned from textDocument/symbolInfo, which is a clangd extension. +struct SymbolDetails { + std::string name; + + std::string containerName; + + /// Unified Symbol Resolution identifier + /// This is an opaque string uniquely identifying a symbol. + /// Unlike SymbolID, it is variable-length and somewhat human-readable. + /// It is a common representation across several clang tools. + /// (See USRGeneration.h) + std::string USR; + + llvm::Optional<SymbolID> ID; +}; +llvm::json::Value toJSON(const SymbolDetails &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolDetails &); +bool operator==(const SymbolDetails &, const SymbolDetails &); + /// The parameters of a Workspace Symbol Request. struct WorkspaceSymbolParams { /// A non-empty query string Modified: clang-tools-extra/trunk/clangd/XRefs.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.cpp?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/XRefs.cpp (original) +++ clang-tools-extra/trunk/clangd/XRefs.cpp Tue Nov 27 08:40:46 2018 @@ -750,5 +750,48 @@ std::vector<Location> findReferences(Par return Results; } +std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) { + const SourceManager &SM = AST.getASTContext().getSourceManager(); + + auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()); + auto Symbols = getSymbolAtPosition(AST, Loc); + + std::vector<SymbolDetails> Results; + + for (const auto &Sym : Symbols.Decls) { + SymbolDetails NewSymbol; + if (const NamedDecl *ND = dyn_cast<NamedDecl>(Sym.D)) { + std::string QName = printQualifiedName(*ND); + std::tie(NewSymbol.containerName, NewSymbol.name) = + splitQualifiedName(QName); + + if (NewSymbol.containerName.empty()) { + if (const auto *ParentND = + dyn_cast_or_null<NamedDecl>(ND->getDeclContext())) + NewSymbol.containerName = printQualifiedName(*ParentND); + } + } + llvm::SmallString<32> USR; + if (!index::generateUSRForDecl(Sym.D, USR)) { + NewSymbol.USR = USR.str(); + NewSymbol.ID = SymbolID(NewSymbol.USR); + } + Results.push_back(std::move(NewSymbol)); + } + + for (const auto &Macro : Symbols.Macros) { + SymbolDetails NewMacro; + NewMacro.name = Macro.Name; + llvm::SmallString<32> USR; + if (!index::generateUSRForMacro(NewMacro.name, Loc, SM, USR)) { + NewMacro.USR = USR.str(); + NewMacro.ID = SymbolID(NewMacro.USR); + } + Results.push_back(std::move(NewMacro)); + } + + return Results; +} + } // namespace clangd } // namespace clang Modified: clang-tools-extra/trunk/clangd/XRefs.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.h?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/XRefs.h (original) +++ clang-tools-extra/trunk/clangd/XRefs.h Tue Nov 27 08:40:46 2018 @@ -38,6 +38,9 @@ llvm::Optional<Hover> getHover(ParsedAST std::vector<Location> findReferences(ParsedAST &AST, Position Pos, const SymbolIndex *Index = nullptr); +/// Get info about symbols at \p Pos. +std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos); + } // namespace clangd } // namespace clang Added: clang-tools-extra/trunk/test/clangd/symbol-info.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/symbol-info.test?rev=347675&view=auto ============================================================================== --- clang-tools-extra/trunk/test/clangd/symbol-info.test (added) +++ clang-tools-extra/trunk/test/clangd/symbol-info.test Tue Nov 27 08:40:46 2018 @@ -0,0 +1,14 @@ +# RUN: clangd -lit-test < %s | FileCheck %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:///simple.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///simple.cpp"},"position":{"line":0,"character":27}}} +# CHECK: "containerName": null, +# CHECK-NEXT: "id": "CA2EBE44A1D76D2A", +# CHECK-NEXT: "name": "foo", +# CHECK-NEXT: "usr": "c:@F@foo#" +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=347675&r1=347674&r2=347675&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original) +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Tue Nov 27 08:40:46 2018 @@ -35,6 +35,7 @@ add_extra_unittest(ClangdTests SerializationTests.cpp SourceCodeTests.cpp SymbolCollectorTests.cpp + SymbolInfoTests.cpp SyncAPI.cpp TUSchedulerTests.cpp TestFS.cpp Added: clang-tools-extra/trunk/unittests/clangd/SymbolInfoTests.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SymbolInfoTests.cpp?rev=347675&view=auto ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/SymbolInfoTests.cpp (added) +++ clang-tools-extra/trunk/unittests/clangd/SymbolInfoTests.cpp Tue Nov 27 08:40:46 2018 @@ -0,0 +1,357 @@ +//===-- SymbolInfoTests.cpp -----------------------*- C++ -*--------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Annotations.h" +#include "ClangdUnit.h" +#include "Compiler.h" +#include "Matchers.h" +#include "SyncAPI.h" +#include "TestFS.h" +#include "TestTU.h" +#include "XRefs.h" +#include "index/FileIndex.h" +#include "index/SymbolCollector.h" +#include "clang/Index/IndexingAction.h" +#include "llvm/Support/Path.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace llvm; +namespace clang { +namespace clangd { +namespace { + +using testing::ElementsAreArray; + +auto CreateExpectedSymbolDetails = [](const std::string &name, + const std::string &container, + const std::string &USR) { + return SymbolDetails{name, container, USR, SymbolID(USR)}; +}; + +TEST(SymbolInfoTests, All) { + std::pair<const char *, std::vector<SymbolDetails>> + TestInputExpectedOutput[] = { + { + R"cpp( // Simple function reference - declaration + void foo(); + int bar() { + fo^o(); + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")} + }, + { + R"cpp( // Simple function reference - definition + void foo() {} + int bar() { + fo^o(); + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")} + }, + { + R"cpp( // Function in namespace reference + namespace bar { + void foo(); + int baz() { + fo^o(); + } + } + )cpp", + {CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@F@foo#")} + }, + { + R"cpp( // Function in different namespace reference + namespace bar { + void foo(); + } + namespace barbar { + int baz() { + bar::fo^o(); + } + } + )cpp", + {CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@F@foo#")} + }, + { + R"cpp( // Function in global namespace reference + void foo(); + namespace Nbar { + namespace Nbaz { + int baz() { + ::fo^o(); + } + } + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")} + }, + { + R"cpp( // Function in anonymous namespace reference + namespace { + void foo(); + } + namespace barbar { + int baz() { + fo^o(); + } + } + )cpp", + {CreateExpectedSymbolDetails("foo", "(anonymous)", "c:TestTU.cpp@aN@F@foo#")} + }, + { + R"cpp( // Function reference - ADL + namespace bar { + struct BarType {}; + void foo(const BarType&); + } + namespace barbar { + int baz() { + bar::BarType b; + fo^o(b); + } + } + )cpp", + {CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@F@foo#&1$@N@bar@S@BarType#")} + }, + { + R"cpp( // Global value reference + int value; + void foo(int) { } + void bar() { + foo(val^ue); + } + )cpp", + {CreateExpectedSymbolDetails("value", "", "c:@value")} + }, + { + R"cpp( // Local value reference + void foo() { int aaa; int bbb = aa^a; } + )cpp", + {CreateExpectedSymbolDetails("aaa", "foo", "c:TestTU.cpp@49@F@foo#@aaa")} + }, + { + R"cpp( // Function param + void bar(int aaa) { + int bbb = a^aa; + } + )cpp", + {CreateExpectedSymbolDetails("aaa", "bar", "c:TestTU.cpp@38@F@bar#I#@aaa")} + }, + { + R"cpp( // Lambda capture + int ii; + auto lam = [ii]() { + return i^i; + }; + )cpp", + {CreateExpectedSymbolDetails("ii", "", "c:@ii")} + }, + { + R"cpp( // Macro reference + #define MACRO 5\nint i = MAC^RO; + )cpp", + {CreateExpectedSymbolDetails("MACRO", "", "c:TestTU.cpp@55@macro@MACRO")} + }, + { + R"cpp( // Multiple symbols returned - using overloaded function name + void foo() {} + void foo(bool) {} + void foo(int) {} + namespace bar { + using ::fo^o; + } + )cpp", + { + CreateExpectedSymbolDetails("foo", "", "c:@F@foo#"), + CreateExpectedSymbolDetails("foo", "", "c:@F@foo#b#"), + CreateExpectedSymbolDetails("foo", "", "c:@F@foo#I#") + } + }, + { + R"cpp( // Multiple symbols returned - implicit conversion + struct foo {}; + struct bar { + bar(const foo&) {} + }; + void func_baz1(bar) {} + void func_baz2() { + foo ff; + func_baz1(f^f); + } + )cpp", + { + CreateExpectedSymbolDetails("ff", "func_baz2", "c:TestTU.cpp@218@F@func_baz2#@ff"), + CreateExpectedSymbolDetails("bar", "bar::", "c:@S@bar@F@bar#&1$@S@foo#"), + } + }, + { + R"cpp( // Type reference - declaration + struct foo; + void bar(fo^o*); + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")} + }, + { + R"cpp( // Type reference - definition + struct foo {}; + void bar(fo^o*); + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")} + }, + { + R"cpp( // Type Reference - template argumen + struct foo {}; + template<class T> struct bar {}; + void baz() { + bar<fo^o> b; + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")} + }, + { + R"cpp( // Template parameter reference - type param + template<class TT> struct bar { + T^T t; + }; + )cpp", + { /* not implemented */ } + }, + { + R"cpp( // Template parameter reference - type param + template<int NN> struct bar { + int a = N^N; + }; + )cpp", + { /* not implemented */ } + }, + { + R"cpp( // Class member reference - objec + struct foo { + int aa; + }; + void bar() { + foo f; + f.a^a; + } + )cpp", + {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@FI@aa")} + }, + { + R"cpp( // Class member reference - pointer + struct foo { + int aa; + }; + void bar() { + &foo::a^a; + } + )cpp", + {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@FI@aa")} + }, + { + R"cpp( // Class method reference - objec + struct foo { + void aa() {} + }; + void bar() { + foo f; + f.a^a(); + } + )cpp", + {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@F@aa#")} + }, + { + R"cpp( // Class method reference - pointer + struct foo { + void aa() {} + }; + void bar() { + &foo::a^a; + } + )cpp", + {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@F@aa#")} + }, + { + R"cpp( // Typedef + typedef int foo; + void bar() { + fo^o a; + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:TestTU.cpp@T@foo")} + }, + { + R"cpp( // Type alias + using foo = int; + void bar() { + fo^o a; + } + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@foo")} + }, + { + R"cpp( // Namespace reference + namespace foo {} + using namespace fo^o; + )cpp", + {CreateExpectedSymbolDetails("foo", "", "c:@N@foo")} + }, + { + R"cpp( // Enum value reference + enum foo { bar, baz }; + void f() { + foo fff = ba^r; + } + )cpp", + {CreateExpectedSymbolDetails("bar", "foo", "c:@E@foo@bar")} + }, + { + R"cpp( // Enum class value reference + enum class foo { bar, baz }; + void f() { + foo fff = foo::ba^r; + } + )cpp", + {CreateExpectedSymbolDetails("bar", "foo::", "c:@E@foo@bar")} + }, + { + R"cpp( // Type inferrence with auto keyword + struct foo {}; + foo getfoo() { return foo{}; } + void f() { + au^to a = getfoo(); + } + )cpp", + {/* not implemented */} + }, + { + R"cpp( // decltype + struct foo {}; + void f() { + foo f; + declt^ype(f); + } + )cpp", + {/* not implemented */} + }, + }; + + for (const auto &T : TestInputExpectedOutput) { + Annotations TestInput(T.first); + auto AST = TestTU::withCode(TestInput.code()).build(); + + EXPECT_THAT(getSymbolInfo(AST, TestInput.point()), + ElementsAreArray(T.second)) + << T.first; + } +} + +} // namespace +} // namespace clangd +} // namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits