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 @@ -100,6 +100,8 @@ Callback>); void onTypeHierarchy(const TypeHierarchyParams &, Callback>); + void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, + Callback>); void onChangeConfiguration(const DidChangeConfigurationParams &); void onSymbolInfo(const TextDocumentPositionParams &, 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 @@ -924,6 +924,13 @@ Params.resolve, Params.direction, std::move(Reply)); } +void ClangdLSPServer::onResolveTypeHierarchy( + const ResolveTypeHierarchyItemParams &Params, + Callback> Reply) { + Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, + std::move(Reply)); +} + void ClangdLSPServer::applyConfiguration( const ConfigurationSettings &Settings) { // Per-file update to the compilation database. @@ -1019,6 +1026,7 @@ MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration); MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo); MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy); + MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy); // clang-format on } 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 @@ -142,7 +142,8 @@ bool SemanticHighlighting = false; /// Returns true if the StringRef is a tweak that should be enabled - std::function TweakFilter = [](llvm::StringRef TweakToSearch) {return true;}; + std::function TweakFilter = + [](llvm::StringRef TweakToSearch) { return true; }; }; // Sensible default options for use in tests. // Features like indexing must be enabled if desired. @@ -216,6 +217,11 @@ TypeHierarchyDirection Direction, Callback> CB); + /// Resolve type hierarchy item in the given direction. + void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve, + TypeHierarchyDirection Direction, + Callback> CB); + /// Retrieve the top symbols from the workspace matching a query. void workspaceSymbols(StringRef Query, int Limit, Callback> CB); @@ -316,7 +322,7 @@ // can be caused by missing includes (e.g. member access in incomplete type). bool SuggestMissingIncludes = false; bool EnableHiddenFeatures = false; - + std::function TweakFilter; // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) 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 @@ -101,8 +101,7 @@ : nullptr), GetClangTidyOptions(Opts.GetClangTidyOptions), SuggestMissingIncludes(Opts.SuggestMissingIncludes), - EnableHiddenFeatures(Opts.HiddenFeatures), - TweakFilter(Opts.TweakFilter), + EnableHiddenFeatures(Opts.HiddenFeatures), TweakFilter(Opts.TweakFilter), WorkspaceRoot(Opts.WorkspaceRoot), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST @@ -527,6 +526,12 @@ WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB))); } +void ClangdServer::resolveTypeHierarchy( + TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, + Callback> CB) { + CB(clangd::resolveTypeHierarchy(std::move(Item), Resolve, Direction, Index)); +} + void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { // FIXME: Do nothing for now. This will be used for indexing and potentially // invalidating other caches. 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 @@ -1119,7 +1119,7 @@ SymbolKind kind; /// `true` if the hierarchy item is deprecated. Otherwise, `false`. - bool deprecated; + bool deprecated = false; /// The URI of the text document where this type hierarchy item belongs to. URIForFile uri; @@ -1145,13 +1145,26 @@ /// descendants. If not defined, the children have not been resolved. llvm::Optional> children; - /// The protocol has a slot here for an optional 'data' filed, which can - /// be used to identify a type hierarchy item in a resolve request. We don't - /// need this (the item itself is sufficient to identify what to resolve) - /// so don't declare it. + /// An optional 'data' filed, which can be used to identify a type hierarchy + /// item in a resolve request. + llvm::Optional data; }; llvm::json::Value toJSON(const TypeHierarchyItem &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &); +bool fromJSON(const llvm::json::Value &, TypeHierarchyItem &); + +/// Parameters for the `typeHierarchy/resolve` request. +struct ResolveTypeHierarchyItemParams { + /// The item to resolve. + TypeHierarchyItem item; + + /// The hierarchy levels to resolve. `0` indicates no level. + int resolve; + + /// The direction of the hierarchy levels to resolve. + TypeHierarchyDirection direction; +}; +bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &); struct ReferenceParams : public TextDocumentPositionParams { // For now, no options like context.includeDeclaration are supported. 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 @@ -421,8 +421,7 @@ bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R) { llvm::json::ObjectMapper O(Params); - return O && O.map("textDocument", R.textDocument) && - O.map("range", R.range); + return O && O.map("textDocument", R.textDocument) && O.map("range", R.range); } bool fromJSON(const llvm::json::Value &Params, @@ -444,8 +443,8 @@ llvm::json::Value toJSON(const DiagnosticRelatedInformation &DRI) { return llvm::json::Object{ - {"location", DRI.location}, - {"message", DRI.message}, + {"location", DRI.location}, + {"message", DRI.message}, }; } @@ -977,6 +976,8 @@ Result["parents"] = I.parents; if (I.children) Result["children"] = I.children; + if (I.data) + Result["data"] = I.data; return std::move(Result); } @@ -995,10 +996,18 @@ O.map("deprecated", I.deprecated); O.map("parents", I.parents); O.map("children", I.children); + O.map("data", I.data); return true; } +bool fromJSON(const llvm::json::Value &Params, + ResolveTypeHierarchyItemParams &P) { + llvm::json::ObjectMapper O(Params); + return O && O.map("item", P.item) && O.map("resolve", P.resolve) && + O.map("direction", P.direction); +} + bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R) { TextDocumentPositionParams &Base = R; return fromJSON(Params, Base); diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -140,6 +140,11 @@ ParsedAST &AST, Position Pos, int Resolve, TypeHierarchyDirection Direction, const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{}); +llvm::Optional +resolveTypeHierarchy(TypeHierarchyItem Item, int ResolveLevels, + TypeHierarchyDirection Direction, + const SymbolIndex *Index); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1065,6 +1065,10 @@ // (https://github.com/clangd/clangd/issues/59). THI.range = THI.selectionRange; THI.uri = Loc->uri; + // Store the SymbolID in the 'data' field. The client will + // send this back in typeHierarchy/resolve, allowing us to + // continue resolving additional levels of the type hierarchy. + THI.data = S.ID.str(); return std::move(THI); } @@ -1208,6 +1212,28 @@ return Result; } +llvm::Optional +resolveTypeHierarchy(TypeHierarchyItem Item, int ResolveLevels, + TypeHierarchyDirection Direction, + const SymbolIndex *Index) { + // We only support typeHierarchy/resolve for children, because for parents + // we ignore ResolveLevels and return all levels of parents eagerly. + if (Direction == TypeHierarchyDirection::Parents || ResolveLevels == 0) + return llvm::None; + + Item.children.emplace(); + + if (Index && Item.data) { + // We store the item's SymbolID in the 'data' field, and the client + // passes it back to us in typeHierarchy/resolve. + if (Expected ID = SymbolID::fromStr(*Item.data)) { + fillSubTypes(*ID, *Item.children, Index, ResolveLevels, Item.uri.file()); + } + } + + return Item; +} + FormattedString HoverInfo::present() const { FormattedString Output; if (NamespaceScope) { diff --git a/clang-tools-extra/clangd/test/type-hierarchy.test b/clang-tools-extra/clangd/test/type-hierarchy.test --- a/clang-tools-extra/clangd/test/type-hierarchy.test +++ b/clang-tools-extra/clangd/test/type-hierarchy.test @@ -1,7 +1,7 @@ # 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 {};\nstruct Child1 : Parent {};\nstruct Child2 : Child1 {};\nstruct Child3 : Child2 {};"}}} +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Parent {};\nstruct Child1 : Parent {};\nstruct Child2 : Child1 {};\nstruct Child3 : Child2 {};\nstruct Child4 : Child3 {};"}}} --- {"jsonrpc":"2.0","id":1,"method":"textDocument/typeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}} # CHECK: "id": 1 @@ -9,6 +9,7 @@ # CHECK-NEXT: "result": { # CHECK-NEXT: "children": [ # CHECK-NEXT: { +# CHECK-NEXT: "data": "A6576FE083F2949A", # CHECK-NEXT: "kind": 23, # CHECK-NEXT: "name": "Child3", # CHECK-NEXT: "range": { @@ -114,6 +115,64 @@ # CHECK-NEXT: "uri": "file:///clangd-test/main.cpp" # CHECK-NEXT: } --- -{"jsonrpc":"2.0","id":2,"method":"shutdown"} +{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":"A6576FE083F2949A","name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}} +# CHECK: "id": 2 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": { +# CHECK-NEXT: "children": [ +# CHECK-NEXT: { +# CHECK-NEXT: "data": "5705B382DFC77CBC", +# CHECK-NEXT: "kind": 23, +# CHECK-NEXT: "name": "Child4", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "line": 4 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 4 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "selectionRange": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "line": 4 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 4 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "file:///clangd-test/main.cpp" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "data": "A6576FE083F2949A", +# CHECK-NEXT: "kind": 23, +# CHECK-NEXT: "name": "Child3", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "line": 3 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 3 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "selectionRange": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "line": 3 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 3 +# 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"}