jkorous updated this revision to Diff 175229. jkorous marked an inline comment as done. jkorous added a comment.
Addressed comments from the reivew. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D54799/new/ https://reviews.llvm.org/D54799 Files: clangd/CMakeLists.txt clangd/ClangdLSPServer.cpp clangd/ClangdLSPServer.h clangd/ClangdServer.cpp clangd/ClangdServer.h clangd/Protocol.cpp clangd/Protocol.h clangd/XRefs.cpp clangd/XRefs.h clangd/index/Index.cpp clangd/index/Index.h clangd/index/SymbolID.cpp clangd/index/SymbolID.h test/clangd/symbol-info.test
Index: test/clangd/symbol-info.test =================================================================== --- /dev/null +++ test/clangd/symbol-info.test @@ -0,0 +1,46 @@ +# 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": "CA2EBE44A1D76D2A1547D47BC6D51EBF", +# CHECK-NEXT: "name": "foo", +# CHECK-NEXT: "usr": "c:@F@foo#" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///nested-decl.cpp","languageId":"cpp","version":1,"text":"namespace nnn { struct aaa {}; void foo() { aaa a; }; }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///nested-decl.cpp"},"position":{"line":0,"character":46}}} +# CHECK: "containerName": "nnn::", +# CHECK-NEXT: "id": "20237FF18EB405D842456DC5D578426D", +# CHECK-NEXT: "name": "aaa", +# CHECK-NEXT: "usr": "c:@N@nnn@S@aaa" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///reference.cpp","languageId":"cpp","version":1,"text":"int value; void foo(int) {} void bar() { foo(value); }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///reference.cpp"},"position":{"line":0,"character":48}}} +# CHECK: "containerName": null, +# CHECK-NEXT: "id": "844613FB2393C9D40A2AFF25D5D316A1", +# CHECK-NEXT: "name": "value", +# CHECK-NEXT: "usr": "c:@value" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///local.cpp","languageId":"cpp","version":1,"text":"void foo() { int aaa; int bbb = aaa; }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///local.cpp"},"position":{"line":0,"character":33}}} +# CHECK: "containerName": "foo", +# CHECK-NEXT: "id": "C05589F2664B06F392C2C438568E55E0", +# CHECK-NEXT: "name": "aaa", +# CHECK-NEXT: "usr": "c:local.cpp@13@F@foo#@aaa" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///macro.cpp","languageId":"cpp","version":1,"text":"#define MACRO 5\nint i = MACRO;"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///macro.cpp"},"position":{"line":1,"character":11}}} +# CHECK: "containerName": null, +# CHECK-NEXT: "id": "29EB506CBDF1BA6D1B6EC203FF03B384", +# CHECK-NEXT: "name": "MACRO", +# CHECK-NEXT: "usr": "c:macro.cpp@24@macro@MACRO" +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} Index: clangd/index/SymbolID.h =================================================================== --- /dev/null +++ clangd/index/SymbolID.h @@ -0,0 +1,67 @@ +//===--- SymbolID.h ----------------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLID_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLID_H + +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include <array> +#include <string> + +namespace clang { +namespace clangd { + +// The class identifies a particular C++ symbol (class, function, method, etc). +// +// As USRs (Unified Symbol Resolution) could be large, especially for functions +// with long type arguments, SymbolID is using truncated SHA1(USR) values to +// guarantee the uniqueness of symbols while using a relatively small amount of +// memory (vs storing USRs directly). +// +// SymbolID can be used as key in the symbol indexes to lookup the symbol. +class SymbolID { +public: + SymbolID() = default; + explicit SymbolID(llvm::StringRef USR); + + bool operator==(const SymbolID &Sym) const { + return HashValue == Sym.HashValue; + } + bool operator<(const SymbolID &Sym) const { + return HashValue < Sym.HashValue; + } + + // The stored hash is truncated to RawSize bytes. + // This trades off memory against the number of symbols we can handle. + // FIXME: can we reduce this further to 8 bytes? + constexpr static size_t RawSize = 16; + llvm::StringRef raw() const; + + static SymbolID fromRaw(llvm::StringRef); + + // Returns a hex encoded string. + std::string str() const; + static llvm::Expected<SymbolID> fromStr(llvm::StringRef); + +private: + std::array<uint8_t, RawSize> HashValue; +}; + +llvm::hash_code hash_value(const SymbolID &ID); + +// Write SymbolID into the given stream. SymbolID is encoded as ID.str(). +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolID &ID); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLID_H Index: clangd/index/SymbolID.cpp =================================================================== --- /dev/null +++ clangd/index/SymbolID.cpp @@ -0,0 +1,58 @@ +//===--- SymbolID.cpp --------------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolID.h" +#include "llvm/Support/SHA1.h" + +using namespace llvm; +namespace clang { +namespace clangd { + +SymbolID::SymbolID(StringRef USR) { + auto Hash = llvm::SHA1::hash(arrayRefFromStringRef(USR)); + static_assert(sizeof(Hash) >= RawSize, "RawSize larger than SHA1"); + memcpy(HashValue.data(), Hash.data(), RawSize); +} + +llvm::StringRef SymbolID::raw() const { + return StringRef(reinterpret_cast<const char *>(HashValue.data()), RawSize); +} + +SymbolID SymbolID::fromRaw(StringRef Raw) { + SymbolID ID; + assert(Raw.size() == RawSize); + memcpy(ID.HashValue.data(), Raw.data(), RawSize); + return ID; +} + +std::string SymbolID::str() const { return toHex(raw()); } + +Expected<SymbolID> SymbolID::fromStr(StringRef Str) { + if (Str.size() != RawSize * 2) + return createStringError(inconvertibleErrorCode(), "Bad ID length"); + for (char C : Str) + if (!isHexDigit(C)) + return createStringError(inconvertibleErrorCode(), "Bad hex ID"); + return fromRaw(fromHex(Str)); +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolID &ID) { + return OS << toHex(ID.raw()); +} + +llvm::hash_code hash_value(const SymbolID &ID) { + // We already have a good hash, just return the first bytes. + assert(sizeof(size_t) <= SymbolID::RawSize && "size_t longer than SHA1!"); + size_t Result; + memcpy(&Result, ID.raw().data(), sizeof(size_t)); + return llvm::hash_code(Result); +} + +} // namespace clangd +} // namespace clang Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -10,11 +10,11 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H +#include "SymbolID.h" #include "clang/Index/IndexSymbol.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -94,54 +94,6 @@ } llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolLocation &); -// The class identifies a particular C++ symbol (class, function, method, etc). -// -// As USRs (Unified Symbol Resolution) could be large, especially for functions -// with long type arguments, SymbolID is using truncated SHA1(USR) values to -// guarantee the uniqueness of symbols while using a relatively small amount of -// memory (vs storing USRs directly). -// -// SymbolID can be used as key in the symbol indexes to lookup the symbol. -class SymbolID { -public: - SymbolID() = default; - explicit SymbolID(llvm::StringRef USR); - - bool operator==(const SymbolID &Sym) const { - return HashValue == Sym.HashValue; - } - bool operator<(const SymbolID &Sym) const { - return HashValue < Sym.HashValue; - } - - // The stored hash is truncated to RawSize bytes. - // This trades off memory against the number of symbols we can handle. - // FIXME: can we reduce this further to 8 bytes? - constexpr static size_t RawSize = 16; - llvm::StringRef raw() const { - return StringRef(reinterpret_cast<const char *>(HashValue.data()), RawSize); - } - static SymbolID fromRaw(llvm::StringRef); - - // Returns a hex encoded string. - std::string str() const; - static llvm::Expected<SymbolID> fromStr(llvm::StringRef); - -private: - std::array<uint8_t, RawSize> HashValue; -}; - -inline llvm::hash_code hash_value(const SymbolID &ID) { - // We already have a good hash, just return the first bytes. - assert(sizeof(size_t) <= SymbolID::RawSize && "size_t longer than SHA1!"); - size_t Result; - memcpy(&Result, ID.raw().data(), sizeof(size_t)); - return llvm::hash_code(Result); -} - -// Write SymbolID into the given stream. SymbolID is encoded as ID.str(). -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolID &ID); - } // namespace clangd } // namespace clang namespace llvm { Index: clangd/index/Index.cpp =================================================================== --- clangd/index/Index.cpp +++ clangd/index/Index.cpp @@ -12,7 +12,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" -#include "llvm/Support/SHA1.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -43,34 +42,6 @@ << "-" << L.End.line() << ":" << L.End.column() << ")"; } -SymbolID::SymbolID(StringRef USR) { - auto Hash = SHA1::hash(arrayRefFromStringRef(USR)); - static_assert(sizeof(Hash) >= RawSize, "RawSize larger than SHA1"); - memcpy(HashValue.data(), Hash.data(), RawSize); -} - -raw_ostream &operator<<(raw_ostream &OS, const SymbolID &ID) { - return OS << toHex(ID.raw()); -} - -SymbolID SymbolID::fromRaw(StringRef Raw) { - SymbolID ID; - assert(Raw.size() == RawSize); - memcpy(ID.HashValue.data(), Raw.data(), RawSize); - return ID; -} - -std::string SymbolID::str() const { return toHex(raw()); } - -Expected<SymbolID> SymbolID::fromStr(StringRef Str) { - if (Str.size() != RawSize * 2) - return createStringError(inconvertibleErrorCode(), "Bad ID length"); - for (char C : Str) - if (!isHexDigit(C)) - return createStringError(inconvertibleErrorCode(), "Bad hex ID"); - return fromRaw(fromHex(Str)); -} - raw_ostream &operator<<(raw_ostream &OS, SymbolOrigin O) { if (O == SymbolOrigin::Unknown) return OS << "unknown"; Index: clangd/XRefs.h =================================================================== --- clangd/XRefs.h +++ clangd/XRefs.h @@ -38,6 +38,9 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos, const SymbolIndex *Index = nullptr); +/// Get info about symbols at \p Cursor. +std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos); + } // namespace clangd } // namespace clang Index: clangd/XRefs.cpp =================================================================== --- clangd/XRefs.cpp +++ clangd/XRefs.cpp @@ -153,6 +153,7 @@ }; bool IsExplicit = !hasImplicitExpr(ASTNode.OrigE); + // Find and add definition declarations (for GoToDefinition). // We don't use parameter `D`, as Parameter `D` is the canonical // declaration, which is the first declaration of a redeclarable @@ -749,5 +750,50 @@ 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()) { + StringRef ContainerNameRef = NewSymbol.containerName; + ContainerNameRef.consume_back("::"); + } else 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 Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -25,6 +25,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H #include "URI.h" +#include "index/SymbolID.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/JSON.h" #include <bitset> @@ -673,6 +674,25 @@ 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 &); + /// The parameters of a Workspace Symbol Request. struct WorkspaceSymbolParams { /// A non-empty query string Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -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" @@ -422,6 +424,27 @@ return O; } +llvm::json::Value toJSON(const SymbolDetails &P) { + return json::Object{ + {"name", !P.name.empty() ? llvm::json::Value(P.name) + : llvm::json::Value(nullptr)}, + {"containerName", !P.containerName.empty() + ? llvm::json::Value(P.containerName) + : llvm::json::Value(nullptr)}, + {"usr", + !P.USR.empty() ? llvm::json::Value(P.USR) : llvm::json::Value(nullptr)}, + {"id", P.ID.hasValue() ? llvm::json::Value(P.ID.getValue().str()) + : llvm::json::Value(nullptr)}, + }; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const SymbolDetails &S) { + if (!S.containerName.empty()) + O << S.containerName << "::"; + 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); Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -201,6 +201,10 @@ /// Called when an event occurs for a watched file in the workspace. void onFileEvent(const DidChangeWatchedFilesParams &Params); + /// Get symbol info for given position. + 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 Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -517,6 +517,18 @@ 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(); Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -88,6 +88,8 @@ 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); Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -685,6 +685,12 @@ 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, @@ -719,6 +725,7 @@ 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 } Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -44,6 +44,7 @@ index/IndexAction.cpp index/MemIndex.cpp index/Merge.cpp + index/SymbolID.cpp index/Serialization.cpp index/SymbolCollector.cpp index/YAMLSerialization.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits