https://github.com/ratzdi updated https://github.com/llvm/llvm-project/pull/170103
>From 36ca87786fded14d2271cccc732778f249befb63 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Fri, 28 Nov 2025 14:01:25 +0100 Subject: [PATCH 01/10] Fill symbol tags for the response TypeHierarchyItem of prepareTypeHierarchy. --- clang-tools-extra/clangd/FindSymbols.cpp | 14 ++++++++++++++ clang-tools-extra/clangd/FindSymbols.h | 3 +++ clang-tools-extra/clangd/Protocol.cpp | 2 ++ clang-tools-extra/clangd/XRefs.cpp | 1 + clang-tools-extra/clangd/test/type-hierarchy.test | 9 +++++++++ 5 files changed, 29 insertions(+) diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 243746056aed0..3a14fe060fbbb 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -228,6 +228,20 @@ std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND) { return Tags; } +std::vector<SymbolTag> getSymbolTags(const Symbol &S) { + std::vector<SymbolTag> Tags; + + if (S.Flags & Symbol::Deprecated) + Tags.push_back(SymbolTag::Deprecated); + + if (S.Definition) + Tags.push_back(SymbolTag::Definition); + else + Tags.push_back(SymbolTag::Declaration); + + return Tags; +} + namespace { using ScoredSymbolInfo = std::pair<float, SymbolInformation>; struct ScoredSymbolGreater { diff --git a/clang-tools-extra/clangd/FindSymbols.h b/clang-tools-extra/clangd/FindSymbols.h index 97b99af4f35e6..4847ea359e9ef 100644 --- a/clang-tools-extra/clangd/FindSymbols.h +++ b/clang-tools-extra/clangd/FindSymbols.h @@ -69,6 +69,9 @@ SymbolTags computeSymbolTags(const NamedDecl &ND); /// \p ND The declaration to get tags for. std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND); +/// Returns the symbol tags for an index `Symbol`. +std::vector<SymbolTag> getSymbolTags(const Symbol &S); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 9926f2dd63de5..5ff92fca933ff 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1437,6 +1437,8 @@ llvm::json::Value toJSON(const TypeHierarchyItem &I) { if (I.detail) Result["detail"] = I.detail; + if(!I.tags.empty()) + Result["tags"] = I.tags; return std::move(Result); } diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 8a24d19a7d129..6747b165cda5a 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1864,6 +1864,7 @@ static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S, HI.name = std::string(S.Name); HI.detail = (S.Scope + S.Name).str(); HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind); + HI.tags = getSymbolTags(S); HI.selectionRange = Loc->range; // FIXME: Populate 'range' correctly // (https://github.com/clangd/clangd/issues/59). diff --git a/clang-tools-extra/clangd/test/type-hierarchy.test b/clang-tools-extra/clangd/test/type-hierarchy.test index a5f13ab13d0b3..918a37a74098c 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy.test +++ b/clang-tools-extra/clangd/test/type-hierarchy.test @@ -44,6 +44,9 @@ # CHECK-NEXT: "line": 2 # CHECK-NEXT: } # CHECK-NEXT: }, +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 19 +# CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] @@ -85,6 +88,9 @@ # CHECK-NEXT: "line": 1 # CHECK-NEXT: } # CHECK-NEXT: }, +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 19 +# CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] @@ -136,6 +142,9 @@ # CHECK-NEXT: "line": 3 # CHECK-NEXT: } # CHECK-NEXT: }, +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 19 +# CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] >From 4b09729b54a6e0565d4e6e1da1be52e81f824b9d Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Mon, 1 Dec 2025 11:34:51 +0100 Subject: [PATCH 02/10] Fill symbol tags into the object SymbolInformation, and on getting workspace symbols. --- clang-tools-extra/clangd/FindSymbols.cpp | 1 + clang-tools-extra/clangd/Protocol.cpp | 2 ++ clang-tools-extra/clangd/test/symbols.test | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 3a14fe060fbbb..5b0bb4a43f883 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -373,6 +373,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit, Info.score = Relevance.NameMatch > std::numeric_limits<float>::epsilon() ? Score / Relevance.NameMatch : QualScore; + Info.tags = getSymbolTags(Sym); Top.push({Score, std::move(Info)}); }); for (auto &R : std::move(Top).items()) diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 5ff92fca933ff..55c06be0ae21c 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -859,6 +859,8 @@ llvm::json::Value toJSON(const SymbolInformation &P) { }; if (P.score) O["score"] = *P.score; + if(!P.tags.empty()) + O["tags"] = P.tags; return std::move(O); } diff --git a/clang-tools-extra/clangd/test/symbols.test b/clang-tools-extra/clangd/test/symbols.test index a16a226e48c05..4f0d7b8b08569 100644 --- a/clang-tools-extra/clangd/test/symbols.test +++ b/clang-tools-extra/clangd/test/symbols.test @@ -24,7 +24,10 @@ # CHECK-NEXT: "uri": "file://{{.*}}/vector.h" # CHECK-NEXT: }, # CHECK-NEXT: "name": "vector", -# CHECK-NEXT: "score": {{.*}} +# CHECK-NEXT: "score": {{.*}}, +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 18 +# CHECK-NEXT: ] # CHECK-NEXT: } # CHECK-NEXT: ] # CHECK-NEXT:} >From 92c1162ec92f654fd8b0c01ca9e17ea63e8f7b40 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Mon, 1 Dec 2025 12:03:35 +0100 Subject: [PATCH 03/10] Minor improvements. --- clang-tools-extra/clangd/FindSymbols.cpp | 3 +++ clang-tools-extra/clangd/FindSymbols.h | 6 +++++- clang-tools-extra/clangd/XRefs.cpp | 2 -- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 5b0bb4a43f883..7fe91f883c79f 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -13,7 +13,10 @@ #include "Quality.h" #include "SourceCode.h" #include "index/Index.h" +#include "index/Symbol.h" +#include "index/SymbolLocation.h" #include "support/Logger.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/Index/IndexSymbol.h" diff --git a/clang-tools-extra/clangd/FindSymbols.h b/clang-tools-extra/clangd/FindSymbols.h index 4847ea359e9ef..169efa9a84584 100644 --- a/clang-tools-extra/clangd/FindSymbols.h +++ b/clang-tools-extra/clangd/FindSymbols.h @@ -14,13 +14,17 @@ #include "Protocol.h" #include "index/Symbol.h" -#include "clang/AST/Decl.h" #include "llvm/ADT/StringRef.h" +#include "clang/AST/Decl.h" namespace clang { +class NamedDecl; + namespace clangd { class ParsedAST; class SymbolIndex; +struct Symbol; +struct SymbolLocation; /// A bitmask type representing symbol tags supported by LSP. /// \see diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 6747b165cda5a..5a53e387ac545 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1890,8 +1890,6 @@ symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) { if (!Result) return Result; Result->data = S.ID.str(); - if (S.Flags & Symbol::Deprecated) - Result->tags.push_back(SymbolTag::Deprecated); return Result; } >From 9003cca1755f02302b1050495e47a737c8d43a4b Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Wed, 3 Dec 2025 13:09:58 +0100 Subject: [PATCH 04/10] Set the detail field of HierarchyItem. --- clang-tools-extra/clangd/XRefs.cpp | 2 +- clang-tools-extra/clangd/test/call-hierarchy.test | 1 + clang-tools-extra/clangd/test/type-hierarchy-ext.test | 3 +++ clang-tools-extra/clangd/test/type-hierarchy.test | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 5a53e387ac545..7be68c5d1e350 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1810,7 +1810,7 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) { HierarchyItem HI; HI.name = printName(Ctx, ND); - // FIXME: Populate HI.detail the way we do in symbolToHierarchyItem? + HI.detail = printQualifiedName(ND); HI.kind = SK; HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()), sourceLocToPosition(SM, DeclRange->getEnd())}; diff --git a/clang-tools-extra/clangd/test/call-hierarchy.test b/clang-tools-extra/clangd/test/call-hierarchy.test index 6548ea0068a8d..44c89d22d9c85 100644 --- a/clang-tools-extra/clangd/test/call-hierarchy.test +++ b/clang-tools-extra/clangd/test/call-hierarchy.test @@ -9,6 +9,7 @@ # CHECK-NEXT: "result": [ # CHECK-NEXT: { # CHECK-NEXT: "data": "{{.*}}", +# CHECK-NEXT: "detail": "callee", # CHECK-NEXT: "kind": 12, # CHECK-NEXT: "name": "callee", # CHECK-NEXT: "range": { diff --git a/clang-tools-extra/clangd/test/type-hierarchy-ext.test b/clang-tools-extra/clangd/test/type-hierarchy-ext.test index 8d1a5dc31da0f..983c7538088bf 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy-ext.test +++ b/clang-tools-extra/clangd/test/type-hierarchy-ext.test @@ -52,6 +52,7 @@ # CHECK-NEXT: ], # CHECK-NEXT: "symbolID": "8A991335E4E67D08" # CHECK-NEXT: }, +# CHECK-NEXT: "detail": "Child2", # CHECK-NEXT: "kind": 23, # CHECK-NEXT: "name": "Child2", # CHECK-NEXT: "parents": [ @@ -65,6 +66,7 @@ # CHECK-NEXT: ], # CHECK-NEXT: "symbolID": "ECDC0C46D75120F4" # CHECK-NEXT: }, +# CHECK-NEXT: "detail": "Child1", # CHECK-NEXT: "kind": 23, # CHECK-NEXT: "name": "Child1", # CHECK-NEXT: "parents": [ @@ -73,6 +75,7 @@ # CHECK-NEXT: "parents": [], # CHECK-NEXT: "symbolID": "FE546E7B648D69A7" # CHECK-NEXT: }, +# CHECK-NEXT: "detail": "Parent", # CHECK-NEXT: "kind": 23, # CHECK-NEXT: "name": "Parent", # CHECK-NEXT: "parents": [], diff --git a/clang-tools-extra/clangd/test/type-hierarchy.test b/clang-tools-extra/clangd/test/type-hierarchy.test index 918a37a74098c..d1dda4b92c29c 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy.test +++ b/clang-tools-extra/clangd/test/type-hierarchy.test @@ -22,6 +22,7 @@ # CHECK-NEXT: ], # CHECK-NEXT: "symbolID": "8A991335E4E67D08" # CHECK-NEXT: }, +# CHECK-NEXT: "detail": "Child2", # CHECK-NEXT: "kind": 23, # CHECK-NEXT: "name": "Child2", # CHECK-NEXT: "range": { >From 195604ec4220c7cd81ca55a1eff0a62310e84caa Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Fri, 12 Dec 2025 12:17:56 +0100 Subject: [PATCH 05/10] Collect symbols tags from AST in the method incomingCalls. --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +- clang-tools-extra/clangd/ClangdServer.cpp | 12 ++-- clang-tools-extra/clangd/ClangdServer.h | 5 +- clang-tools-extra/clangd/XRefs.cpp | 57 +++++++++++++++++-- clang-tools-extra/clangd/XRefs.h | 3 +- .../clangd/unittests/CallHierarchyTests.cpp | 56 +++++++++--------- 6 files changed, 93 insertions(+), 42 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 761b07eceec83..3fa217ff833bb 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1389,7 +1389,7 @@ void ClangdLSPServer::onPrepareCallHierarchy( void ClangdLSPServer::onCallHierarchyIncomingCalls( const CallHierarchyIncomingCallsParams &Params, Callback<std::vector<CallHierarchyIncomingCall>> Reply) { - Server->incomingCalls(Params.item, std::move(Reply)); + Server->incomingCalls(Params.item.uri.file(), Params.item, std::move(Reply)); } void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams &Params, diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index f1a87dd12d905..21d69b99ad8d4 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -909,12 +909,16 @@ void ClangdServer::prepareCallHierarchy( } void ClangdServer::incomingCalls( + PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyIncomingCall>> CB) { - WorkScheduler->run("Incoming Calls", "", - [CB = std::move(CB), Item, this]() mutable { - CB(clangd::incomingCalls(Item, Index)); - }); + auto Action = [Item, CB = std::move(CB), this]( + llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::incomingCalls(Item, Index, InpAST->AST)); + }; + WorkScheduler->runWithAST("Incoming Calls", File, std::move(Action)); } void ClangdServer::inlayHints(PathRef File, std::optional<Range> RestrictRange, diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 3ffaf67553dce..ae7e25e2b6db6 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -299,8 +299,9 @@ class ClangdServer { Callback<std::vector<CallHierarchyItem>> CB); /// Resolve incoming calls for a given call hierarchy item. - void incomingCalls(const CallHierarchyItem &Item, - Callback<std::vector<CallHierarchyIncomingCall>>); + void incomingCalls(PathRef File, + const CallHierarchyItem &Item, + Callback<std::vector<CallHierarchyIncomingCall>> CB); /// Resolve outgoing calls for a given call hierarchy item. void outgoingCalls(const CallHierarchyItem &Item, diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 7be68c5d1e350..f326c3ecea92d 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -2341,6 +2341,36 @@ void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, Item.uri.file()); } +// Tries to find a NamedDecl in the AST that matches the given Symbol. +// Returns nullptr if the symbol is not found in the current AST. +const NamedDecl *getNamedDeclFromSymbol(const Symbol &Sym, + const ParsedAST &AST) { + // Try to convert the symbol to a location and find the decl at that location + auto SymLoc = symbolToLocation(Sym, AST.tuPath()); + if (!SymLoc) + return nullptr; + + // Check if the symbol location is in the main file + if (SymLoc->uri.file() != AST.tuPath()) + return nullptr; + + // Convert LSP position to source location + const auto &SM = AST.getSourceManager(); + auto CurLoc = sourceLocationInMainFile(SM, SymLoc->range.start); + if (!CurLoc) { + llvm::consumeError(CurLoc.takeError()); + return nullptr; + } + + // Get all decls at this location + auto Decls = getDeclAtPosition(const_cast<ParsedAST &>(AST), *CurLoc, {}); + if (Decls.empty()) + return nullptr; + + // Return the first decl (usually the most specific one) + return Decls[0]; +} + std::vector<CallHierarchyItem> prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) { std::vector<CallHierarchyItem> Result; @@ -2368,8 +2398,10 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) { } std::vector<CallHierarchyIncomingCall> -incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { +incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST) { std::vector<CallHierarchyIncomingCall> Results; + if (!Index || Item.data.empty()) return Results; auto ID = SymbolID::fromStr(Item.data); @@ -2413,14 +2445,27 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { Index->lookup(ContainerLookup, [&](const Symbol &Caller) { auto It = CallsIn.find(Caller.ID); assert(It != CallsIn.end()); - if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) { + if (auto *ND = getNamedDeclFromSymbol(Caller, AST)) { + if (auto CHI = declToCallHierarchyItem(*ND, AST.tuPath())) { + std::vector<Range> FromRanges; + for (const Location &L : It->second) { + if (L.uri != CHI->uri) { + // Call location not in same file as caller. + // This can happen in some edge cases. There's not much we can do, + // since the protocol only allows returning ranges interpreted as + // being in the caller's file. + continue; + } + FromRanges.push_back(L.range); + } + Results.push_back(CallHierarchyIncomingCall{ + std::move(*CHI), std::move(FromRanges), MightNeverCall}); + } + } else if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) { + // Fallback to using symbol if NamedDecl is not available std::vector<Range> FromRanges; for (const Location &L : It->second) { if (L.uri != CHI->uri) { - // Call location not in same file as caller. - // This can happen in some edge cases. There's not much we can do, - // since the protocol only allows returning ranges interpreted as - // being in the caller's file. continue; } FromRanges.push_back(L.range); diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 247e52314c3f9..1019fa189a613 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -148,7 +148,8 @@ std::vector<CallHierarchyItem> prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath); std::vector<CallHierarchyIncomingCall> -incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index); +incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST); std::vector<CallHierarchyOutgoingCall> outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index); diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp index 9859577c7cf7e..f5e9983aa70ec 100644 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -89,12 +89,12 @@ TEST(CallHierarchy, IncomingOneFileCpp) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT( IncomingLevel1, ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))), iFromRanges(Source.range("Callee"))))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT( IncomingLevel2, ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))), @@ -103,13 +103,13 @@ TEST(CallHierarchy, IncomingOneFileCpp) { AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))), iFromRanges(Source.range("Caller1C"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get()); + auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT( IncomingLevel3, ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))), iFromRanges(Source.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get()); + auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); } @@ -137,12 +137,12 @@ TEST(CallHierarchy, IncomingOneFileObjC) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("MyClass::caller1"))), iFromRanges(Source.range("Callee"))))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel2, ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("MyClass::caller2"))), @@ -152,13 +152,13 @@ TEST(CallHierarchy, IncomingOneFileObjC) { withDetail("MyClass::caller3"))), iFromRanges(Source.range("Caller1C"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get()); + auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel3, ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("MyClass::caller3"))), iFromRanges(Source.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get()); + auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); } @@ -184,18 +184,18 @@ TEST(CallHierarchy, IncomingIncludeOverrides) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(AllOf(withName("Func"), withDetail("Implementation::Func"))), iFromRanges(Source.range("Callee"))))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT( IncomingLevel2, ElementsAre(AllOf(from(AllOf(withName("Test"), withDetail("Test"))), iFromRanges(Source.range("FuncCall"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get()); + auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel3, IsEmpty()); } @@ -221,13 +221,13 @@ TEST(CallHierarchy, MainFileOnlyRef) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT( IncomingLevel1, ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))), iFromRanges(Source.range("Callee"))))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); EXPECT_THAT( IncomingLevel2, ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))), @@ -256,7 +256,7 @@ TEST(CallHierarchy, IncomingQualified) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("Waldo::find"))); - auto Incoming = incomingCalls(Items[0], Index.get()); + auto Incoming = incomingCalls(Items[0], Index.get(), AST); EXPECT_THAT( Incoming, ElementsAre( @@ -396,13 +396,13 @@ TEST(CallHierarchy, MultiFileCpp) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Pos, TUPath); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("nsa::caller1"))), iFromRanges(Caller1C.range())))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT( IncomingLevel2, ElementsAre( @@ -411,13 +411,13 @@ TEST(CallHierarchy, MultiFileCpp) { AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))), iFromRanges(Caller3C.range("Caller1"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get()); + auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel3, ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))), iFromRanges(Caller3C.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get()); + auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); }; @@ -553,12 +553,12 @@ TEST(CallHierarchy, IncomingMultiFileObjC) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Pos, TUPath); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(withName("caller1")), iFromRanges(Caller1C.range())))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get()); + auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel2, ElementsAre(AllOf(from(withName("caller2")), iFromRanges(Caller2C.range("A"), @@ -566,12 +566,12 @@ TEST(CallHierarchy, IncomingMultiFileObjC) { AllOf(from(withName("caller3")), iFromRanges(Caller3C.range("Caller1"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get()); + auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel3, ElementsAre(AllOf(from(withName("caller3")), iFromRanges(Caller3C.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get()); + auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); }; @@ -616,7 +616,7 @@ TEST(CallHierarchy, CallInLocalVarDecl) { prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto Incoming = incomingCalls(Items[0], Index.get()); + auto Incoming = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName("caller1")), iFromRanges(Source.range("call1"))), AllOf(from(withName("caller2")), @@ -643,7 +643,7 @@ TEST(CallHierarchy, HierarchyOnField) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("var1"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(withName("caller")), iFromRanges(Source.range("Callee"))))); @@ -664,7 +664,7 @@ TEST(CallHierarchy, HierarchyOnVar) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("var"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(withName("caller")), iFromRanges(Source.range("Callee"))))); @@ -686,14 +686,14 @@ TEST(CallHierarchy, HierarchyOnEnumConstant) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point("Heads"), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("heads"))); - auto IncomingLevel1 = incomingCalls(Items[0], Index.get()); + auto IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(withName("caller")), iFromRanges(Source.range("CallerH"))))); Items = prepareCallHierarchy(AST, Source.point("Tails"), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("tails"))); - IncomingLevel1 = incomingCalls(Items[0], Index.get()); + IncomingLevel1 = incomingCalls(Items[0], Index.get(), AST); ASSERT_THAT(IncomingLevel1, ElementsAre(AllOf(from(withName("caller")), iFromRanges(Source.range("CallerT"))))); @@ -718,7 +718,7 @@ TEST(CallHierarchy, CallInDifferentFileThanCaller) { prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("callee"))); - auto Incoming = incomingCalls(Items[0], Index.get()); + auto Incoming = incomingCalls(Items[0], Index.get(), AST); // The only call site is in the source file, which is a different file from // the declaration of the function containing the call, which is in the >From 0a852bb10ddbf56a6e42cc62aea2818b8c93e973 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Sun, 14 Dec 2025 19:49:49 +0100 Subject: [PATCH 06/10] Collect symbols tags from AST in the method outgoingCalls. --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +- clang-tools-extra/clangd/ClangdServer.cpp | 12 ++++-- clang-tools-extra/clangd/ClangdServer.h | 2 +- clang-tools-extra/clangd/XRefs.cpp | 42 ++++++++++--------- clang-tools-extra/clangd/XRefs.h | 3 +- .../clangd/unittests/CallHierarchyTests.cpp | 20 ++++----- 6 files changed, 45 insertions(+), 36 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 3fa217ff833bb..0eaab92e5109f 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1434,7 +1434,7 @@ void ClangdLSPServer::onInlayHint(const InlayHintsParams &Params, void ClangdLSPServer::onCallHierarchyOutgoingCalls( const CallHierarchyOutgoingCallsParams &Params, Callback<std::vector<CallHierarchyOutgoingCall>> Reply) { - Server->outgoingCalls(Params.item, std::move(Reply)); + Server->outgoingCalls(Params.item.uri.file(), Params.item, std::move(Reply)); } void ClangdLSPServer::applyConfiguration( diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 21d69b99ad8d4..cb26c7a799f5e 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -933,12 +933,16 @@ void ClangdServer::inlayHints(PathRef File, std::optional<Range> RestrictRange, } void ClangdServer::outgoingCalls( + PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyOutgoingCall>> CB) { - WorkScheduler->run("Outgoing Calls", "", - [CB = std::move(CB), Item, this]() mutable { - CB(clangd::outgoingCalls(Item, Index)); - }); + auto Action = [Item, CB = std::move(CB), this]( + llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::outgoingCalls(Item, Index, InpAST->AST)); + }; + WorkScheduler->runWithAST("Outgoing Calls", File, std::move(Action)); } void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index ae7e25e2b6db6..c28224e8bbb6b 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -304,7 +304,7 @@ class ClangdServer { Callback<std::vector<CallHierarchyIncomingCall>> CB); /// Resolve outgoing calls for a given call hierarchy item. - void outgoingCalls(const CallHierarchyItem &Item, + void outgoingCalls(PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyOutgoingCall>>); /// Resolve inlay hints for a given document. diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index f326c3ecea92d..6ef7626cec6ff 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -2445,27 +2445,21 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, Index->lookup(ContainerLookup, [&](const Symbol &Caller) { auto It = CallsIn.find(Caller.ID); assert(It != CallsIn.end()); + + std::optional<CallHierarchyItem> CHI; if (auto *ND = getNamedDeclFromSymbol(Caller, AST)) { - if (auto CHI = declToCallHierarchyItem(*ND, AST.tuPath())) { - std::vector<Range> FromRanges; - for (const Location &L : It->second) { - if (L.uri != CHI->uri) { - // Call location not in same file as caller. - // This can happen in some edge cases. There's not much we can do, - // since the protocol only allows returning ranges interpreted as - // being in the caller's file. - continue; - } - FromRanges.push_back(L.range); - } - Results.push_back(CallHierarchyIncomingCall{ - std::move(*CHI), std::move(FromRanges), MightNeverCall}); - } - } else if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) { - // Fallback to using symbol if NamedDecl is not available + CHI = declToCallHierarchyItem(*ND, AST.tuPath()); + } else { + CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()); + } + if (CHI) { std::vector<Range> FromRanges; for (const Location &L : It->second) { if (L.uri != CHI->uri) { + // Call location not in same file as caller. + // This can happen in some edge cases. There's not much we can do, + // since the protocol only allows returning ranges interpreted as + // being in the caller's file. continue; } FromRanges.push_back(L.range); @@ -2495,7 +2489,8 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, } std::vector<CallHierarchyOutgoingCall> -outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { +outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST) { std::vector<CallHierarchyOutgoingCall> Results; if (!Index || Item.data.empty()) return Results; @@ -2541,7 +2536,16 @@ outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { auto It = CallsOut.find(Callee.ID); assert(It != CallsOut.end()); - if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file())) { + + std::optional<CallHierarchyItem> CHI; + + if (auto *ND = getNamedDeclFromSymbol(Callee, AST)) { + CHI = declToCallHierarchyItem(*ND, AST.tuPath()); + } else { + CHI = symbolToCallHierarchyItem(Callee, Item.uri.file()); + } + + if (CHI) { std::vector<Range> FromRanges; for (const Location &L : It->second) { if (L.uri != Item.uri) { diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 1019fa189a613..6319729ba39e4 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -152,7 +152,8 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, const ParsedAST &AST); std::vector<CallHierarchyOutgoingCall> -outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index); +outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST); /// Returns all decls that are referenced in the \p FD except local symbols. llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST, diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp index f5e9983aa70ec..2891b420427d2 100644 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -296,29 +296,29 @@ TEST(CallHierarchy, OutgoingOneFile) { std::vector<CallHierarchyItem> Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(withName("caller3"))); - auto OugoingLevel1 = outgoingCalls(Items[0], Index.get()); + auto OugoingLevel1 = outgoingCalls(Items[0], Index.get(), AST); ASSERT_THAT( OugoingLevel1, ElementsAre( - AllOf(to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))), + AllOf(to(AllOf(withName("Foo::caller1"), withDetail("ns::Foo::caller1"))), oFromRanges(Source.range("Caller1C"))), AllOf(to(AllOf(withName("caller2"), withDetail("caller2"))), oFromRanges(Source.range("Caller2"))))); - auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get()); + auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get(), AST); ASSERT_THAT( OutgoingLevel2, ElementsAre(AllOf( - to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))), + to(AllOf(withName("Foo::caller1"), withDetail("ns::Foo::caller1"))), oFromRanges(Source.range("Caller1A"), Source.range("Caller1B"))))); - auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get()); + auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get(), AST); ASSERT_THAT( OutgoingLevel3, ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))), oFromRanges(Source.range("Callee"))))); - auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get()); + auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get(), AST); EXPECT_THAT(OutgoingLevel4, IsEmpty()); } @@ -430,7 +430,7 @@ TEST(CallHierarchy, MultiFileCpp) { ElementsAre(AllOf( withName("caller3"), withFile(testPath(IsDeclaration ? "caller3.hh" : "caller3.cc"))))); - auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get()); + auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get(), AST); ASSERT_THAT( OutgoingLevel1, // fromRanges are interpreted in the context of Items[0]'s file. @@ -444,19 +444,19 @@ TEST(CallHierarchy, MultiFileCpp) { IsDeclaration ? oFromRanges() : oFromRanges(Caller3C.range("Caller2"))))); - auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get()); + auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get(), AST); ASSERT_THAT(OutgoingLevel2, ElementsAre(AllOf( to(AllOf(withName("caller1"), withDetail("nsa::caller1"))), oFromRanges(Caller2C.range("A"), Caller2C.range("B"))))); - auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get()); + auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get(), AST); ASSERT_THAT( OutgoingLevel3, ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))), oFromRanges(Caller1C.range())))); - auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get()); + auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get(), AST); EXPECT_THAT(OutgoingLevel4, IsEmpty()); }; >From 57fb1ac6e488a43e575d1c21e894d9985053da0a Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Mon, 15 Dec 2025 11:02:01 +0100 Subject: [PATCH 07/10] Collect symbols tags from AST in the method subtypes. --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 4 +- clang-tools-extra/clangd/ClangdServer.cpp | 36 +++++--- clang-tools-extra/clangd/ClangdServer.h | 4 +- clang-tools-extra/clangd/XRefs.cpp | 89 +++++++++++-------- clang-tools-extra/clangd/XRefs.h | 5 +- .../clangd/test/type-hierarchy-ext.test | 8 +- .../clangd/test/type-hierarchy.test | 4 +- .../clangd/unittests/TypeHierarchyTests.cpp | 4 +- 8 files changed, 91 insertions(+), 63 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 0eaab92e5109f..e77093f31eda6 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1356,7 +1356,7 @@ void ClangdLSPServer::onResolveTypeHierarchy( } Reply(serializeTHIForExtension(std::move(**Resp))); }; - Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, + Server->resolveTypeHierarchy(Params.item.uri.file(), Params.item, Params.resolve, Params.direction, std::move(Serialize)); } @@ -1376,7 +1376,7 @@ void ClangdLSPServer::onSuperTypes( void ClangdLSPServer::onSubTypes( const ResolveTypeHierarchyItemParams &Params, Callback<std::vector<TypeHierarchyItem>> Reply) { - Server->subTypes(Params.item, std::move(Reply)); + Server->subTypes(Params.item.uri.file(), Params.item, std::move(Reply)); } void ClangdLSPServer::onPrepareCallHierarchy( diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index cb26c7a799f5e..c210eda6bfd2e 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -880,21 +880,37 @@ void ClangdServer::superTypes( }); } -void ClangdServer::subTypes(const TypeHierarchyItem &Item, +void ClangdServer::subTypes(PathRef File, const TypeHierarchyItem &Item, Callback<std::vector<TypeHierarchyItem>> CB) { - WorkScheduler->run( - "typeHierarchy/subTypes", /*Path=*/"", - [=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); }); + + auto Action = [File = File.str(), Item, CB = std::move(CB), this]( + llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::subTypes(Item, Index, InpAST->AST)); + }; + WorkScheduler->runWithAST("subTypes Calls", File, std::move(Action)); + // WorkScheduler->run( + // "typeHierarchy/subTypes", /*Path=*/"", + // [=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); }); } -void ClangdServer::resolveTypeHierarchy( +void ClangdServer::resolveTypeHierarchy(PathRef File, TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, Callback<std::optional<TypeHierarchyItem>> CB) { - WorkScheduler->run( - "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { - clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); - CB(Item); - }); + auto Action = [=, CB = std::move(CB), this]( + llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index, InpAST->AST); + CB(Item); + }; + WorkScheduler->runWithAST("resolveTypeHierarchy Calls", File, std::move(Action)); + // WorkScheduler->run( + // "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { + // clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); + // CB(Item); + // }); } void ClangdServer::prepareCallHierarchy( diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index c28224e8bbb6b..431856aa1fadb 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -286,11 +286,11 @@ class ClangdServer { void superTypes(const TypeHierarchyItem &Item, Callback<std::optional<std::vector<TypeHierarchyItem>>> CB); /// Get direct children of a type hierarchy item. - void subTypes(const TypeHierarchyItem &Item, + void subTypes(PathRef File, const TypeHierarchyItem &Item, Callback<std::vector<TypeHierarchyItem>> CB); /// Resolve type hierarchy item in the given direction. - void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve, + void resolveTypeHierarchy(PathRef File, TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, Callback<std::optional<TypeHierarchyItem>> CB); diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 6ef7626cec6ff..b48a834224cd7 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1893,18 +1893,58 @@ symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) { return Result; } +// Tries to find a NamedDecl in the AST that matches the given Symbol. +// Returns nullptr if the symbol is not found in the current AST. +const NamedDecl *getNamedDeclFromSymbol(const Symbol &Sym, + const ParsedAST &AST) { + // Try to convert the symbol to a location and find the decl at that location + auto SymLoc = symbolToLocation(Sym, AST.tuPath()); + if (!SymLoc) + return nullptr; + + // Check if the symbol location is in the main file + if (SymLoc->uri.file() != AST.tuPath()) + return nullptr; + + // Convert LSP position to source location + const auto &SM = AST.getSourceManager(); + auto CurLoc = sourceLocationInMainFile(SM, SymLoc->range.start); + if (!CurLoc) { + llvm::consumeError(CurLoc.takeError()); + return nullptr; + } + + // Get all decls at this location + auto Decls = getDeclAtPosition(const_cast<ParsedAST &>(AST), *CurLoc, {}); + if (Decls.empty()) + return nullptr; + + // Return the first decl (usually the most specific one) + return Decls[0]; +} + static void fillSubTypes(const SymbolID &ID, std::vector<TypeHierarchyItem> &SubTypes, - const SymbolIndex *Index, int Levels, PathRef TUPath) { + const SymbolIndex *Index, int Levels, PathRef TUPath, + const ParsedAST &AST) { RelationsRequest Req; Req.Subjects.insert(ID); Req.Predicate = RelationKind::BaseOf; Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) { - if (std::optional<TypeHierarchyItem> ChildSym = - symbolToTypeHierarchyItem(Object, TUPath)) { + std::optional<TypeHierarchyItem> ChildSym; + + if (auto *ND = getNamedDeclFromSymbol(Object, AST)) { + ChildSym = declToTypeHierarchyItem(*ND, AST.tuPath()); + elog("fillSubTypes: declToTypeHierarchyItem, {0}", ChildSym.has_value()); + } else { + ChildSym = symbolToTypeHierarchyItem(Object, TUPath); + elog("fillSubTypes: symbolToTypeHierarchyItem, {0}", ChildSym.has_value()); + } + if (ChildSym) { if (Levels > 1) { ChildSym->children.emplace(); - fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath); + fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath, + AST); } SubTypes.emplace_back(std::move(*ChildSym)); } @@ -2287,7 +2327,7 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, if (Index) { if (auto ID = getSymbolID(CXXRD)) - fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath); + fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath, AST); } } Results.emplace_back(std::move(*Result)); @@ -2319,9 +2359,9 @@ superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { } std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, - const SymbolIndex *Index) { + const SymbolIndex *Index, const ParsedAST &AST) { std::vector<TypeHierarchyItem> Results; - fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file()); + fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file(), AST); for (auto &ChildSym : Results) ChildSym.data.parents = {Item.data}; return Results; @@ -2329,7 +2369,8 @@ std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, - const SymbolIndex *Index) { + const SymbolIndex *Index, + const ParsedAST &AST) { // We only support typeHierarchy/resolve for children, because for parents // we ignore ResolveLevels and return all levels of parents eagerly. if (!Index || Direction == TypeHierarchyDirection::Parents || @@ -2338,37 +2379,7 @@ void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, Item.children.emplace(); fillSubTypes(Item.data.symbolID, *Item.children, Index, ResolveLevels, - Item.uri.file()); -} - -// Tries to find a NamedDecl in the AST that matches the given Symbol. -// Returns nullptr if the symbol is not found in the current AST. -const NamedDecl *getNamedDeclFromSymbol(const Symbol &Sym, - const ParsedAST &AST) { - // Try to convert the symbol to a location and find the decl at that location - auto SymLoc = symbolToLocation(Sym, AST.tuPath()); - if (!SymLoc) - return nullptr; - - // Check if the symbol location is in the main file - if (SymLoc->uri.file() != AST.tuPath()) - return nullptr; - - // Convert LSP position to source location - const auto &SM = AST.getSourceManager(); - auto CurLoc = sourceLocationInMainFile(SM, SymLoc->range.start); - if (!CurLoc) { - llvm::consumeError(CurLoc.takeError()); - return nullptr; - } - - // Get all decls at this location - auto Decls = getDeclAtPosition(const_cast<ParsedAST &>(AST), *CurLoc, {}); - if (Decls.empty()) - return nullptr; - - // Return the first decl (usually the most specific one) - return Decls[0]; + Item.uri.file(), AST); } std::vector<CallHierarchyItem> diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 6319729ba39e4..1582c481ea5c2 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -137,11 +137,12 @@ std::optional<std::vector<TypeHierarchyItem>> superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index); /// Returns direct children of a TypeHierarchyItem. std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, - const SymbolIndex *Index); + const SymbolIndex *Index, + const ParsedAST &AST); void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, - const SymbolIndex *Index); + const SymbolIndex *Index, const ParsedAST &AST); /// Get call hierarchy information at \p Pos. std::vector<CallHierarchyItem> diff --git a/clang-tools-extra/clangd/test/type-hierarchy-ext.test b/clang-tools-extra/clangd/test/type-hierarchy-ext.test index 983c7538088bf..d635b199d002c 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy-ext.test +++ b/clang-tools-extra/clangd/test/type-hierarchy-ext.test @@ -17,11 +17,11 @@ # CHECK-NEXT: "name": "Child3", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "character": 25, # CHECK-NEXT: "line": 3 # CHECK-NEXT: }, # CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "character": 0, # CHECK-NEXT: "line": 3 # CHECK-NEXT: } # CHECK-NEXT: }, @@ -162,11 +162,11 @@ # CHECK-NEXT: "name": "Child4", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "character": 25, # CHECK-NEXT: "line": 4 # CHECK-NEXT: }, # CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "character": 0, # CHECK-NEXT: "line": 4 # CHECK-NEXT: } # CHECK-NEXT: }, diff --git a/clang-tools-extra/clangd/test/type-hierarchy.test b/clang-tools-extra/clangd/test/type-hierarchy.test index d1dda4b92c29c..065f129e05d2f 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy.test +++ b/clang-tools-extra/clangd/test/type-hierarchy.test @@ -125,11 +125,11 @@ # CHECK-NEXT: "name": "Child3", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "character": 25, # CHECK-NEXT: "line": 3 # CHECK-NEXT: }, # CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "character": 0, # CHECK-NEXT: "line": 3 # CHECK-NEXT: } # CHECK-NEXT: }, diff --git a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp index 406a842f5a008..ae2f806d0b17e 100644 --- a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp @@ -756,7 +756,7 @@ struct Child2b : Child1 {}; parentsNotResolved(), childrenNotResolved())))); resolveTypeHierarchy((*Result.front().children)[0], /*ResolveLevels=*/1, - TypeHierarchyDirection::Children, Index.get()); + TypeHierarchyDirection::Children, Index.get(), AST); EXPECT_THAT( (*Result.front().children)[0], @@ -783,7 +783,7 @@ struct Child : Parent1, Parent2 {}; TypeHierarchyDirection::Children, Index.get(), testPath(TU.Filename)); ASSERT_THAT(Result, SizeIs(1)); - auto Children = subTypes(Result.front(), Index.get()); + auto Children = subTypes(Result.front(), Index.get(), AST); // Make sure parents are populated when getting children. // FIXME: This is partial. >From d74fdd230eae085d6339d8949f7efd9bfa6cf509 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Mon, 15 Dec 2025 16:25:26 +0100 Subject: [PATCH 08/10] Collect symbols tags from AST in the method supertypes. --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 61 ++++++++--------- clang-tools-extra/clangd/ClangdLSPServer.h | 11 ++-- clang-tools-extra/clangd/ClangdServer.cpp | 65 +++++++++---------- clang-tools-extra/clangd/ClangdServer.h | 5 +- clang-tools-extra/clangd/Protocol.h | 3 + clang-tools-extra/clangd/XRefs.cpp | 48 ++++++++------ clang-tools-extra/clangd/XRefs.h | 6 +- .../clangd/test/call-hierarchy.test | 3 + .../clangd/test/type-hierarchy.test | 7 +- .../clangd/unittests/TypeHierarchyTests.cpp | 2 +- 10 files changed, 109 insertions(+), 102 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index e77093f31eda6..ef65d22d16c3d 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -136,10 +136,9 @@ CodeAction toCodeAction(const Fix &F, const URIForFile &File, Edit.textDocument = VersionedTextDocumentIdentifier{{File}, Version}; for (const auto &E : F.Edits) Edit.edits.push_back( - {E.range, E.newText, - SupportChangeAnnotation ? E.annotationId : ""}); + {E.range, E.newText, SupportChangeAnnotation ? E.annotationId : ""}); if (SupportChangeAnnotation) { - for (const auto &[AID, Annotation]: F.Annotations) + for (const auto &[AID, Annotation] : F.Annotations) Action.edit->changeAnnotations[AID] = Annotation; } } @@ -909,24 +908,24 @@ void ClangdLSPServer::onRename(const RenameParams &Params, if (!Server->getDraft(File)) return Reply(llvm::make_error<LSPError>( "onRename called for non-added file", ErrorCode::InvalidParams)); - Server->rename(File, Params.position, Params.newName, Opts.Rename, - [File, Params, Reply = std::move(Reply), - this](llvm::Expected<RenameResult> R) mutable { - if (!R) - return Reply(R.takeError()); - if (auto Err = validateEdits(*Server, R->GlobalChanges)) - return Reply(std::move(Err)); - WorkspaceEdit Result; - // FIXME: use documentChanges if SupportDocumentChanges is - // true. - Result.changes.emplace(); - for (const auto &Rep : R->GlobalChanges) { - (*Result - .changes)[URI::createFile(Rep.first()).toString()] = - Rep.second.asTextEdits(); - } - Reply(Result); - }); + Server->rename( + File, Params.position, Params.newName, Opts.Rename, + [File, Params, Reply = std::move(Reply), + this](llvm::Expected<RenameResult> R) mutable { + if (!R) + return Reply(R.takeError()); + if (auto Err = validateEdits(*Server, R->GlobalChanges)) + return Reply(std::move(Err)); + WorkspaceEdit Result; + // FIXME: use documentChanges if SupportDocumentChanges is + // true. + Result.changes.emplace(); + for (const auto &Rep : R->GlobalChanges) { + (*Result.changes)[URI::createFile(Rep.first()).toString()] = + Rep.second.asTextEdits(); + } + Reply(Result); + }); } void ClangdLSPServer::onDocumentDidClose( @@ -1070,7 +1069,7 @@ void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags; ClangdServer::CodeActionInputs Inputs; - for (const auto& LSPDiag : Params.context.diagnostics) { + for (const auto &LSPDiag : Params.context.diagnostics) { if (auto DiagRef = getDiagRef(File.file(), LSPDiag)) { ToLSPDiags[*DiagRef] = LSPDiag; Inputs.Diagnostics.push_back(*DiagRef); @@ -1079,13 +1078,9 @@ void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, Inputs.File = File.file(); Inputs.Selection = Params.range; Inputs.RequestedActionKinds = Params.context.only; - Inputs.TweakFilter = [this](const Tweak &T) { - return Opts.TweakFilter(T); - }; - auto CB = [this, - Reply = std::move(Reply), - ToLSPDiags = std::move(ToLSPDiags), File, - Selection = Params.range]( + Inputs.TweakFilter = [this](const Tweak &T) { return Opts.TweakFilter(T); }; + auto CB = [this, Reply = std::move(Reply), ToLSPDiags = std::move(ToLSPDiags), + File, Selection = Params.range]( llvm::Expected<ClangdServer::CodeActionResult> Fixits) mutable { if (!Fixits) return Reply(Fixits.takeError()); @@ -1094,8 +1089,7 @@ void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, for (const auto &QF : Fixits->QuickFixes) { CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges, SupportsChangeAnnotation)); - if (auto It = ToLSPDiags.find(QF.Diag); - It != ToLSPDiags.end()) { + if (auto It = ToLSPDiags.find(QF.Diag); It != ToLSPDiags.end()) { CAs.back().diagnostics = {It->second}; } } @@ -1356,7 +1350,8 @@ void ClangdLSPServer::onResolveTypeHierarchy( } Reply(serializeTHIForExtension(std::move(**Resp))); }; - Server->resolveTypeHierarchy(Params.item.uri.file(), Params.item, Params.resolve, Params.direction, + Server->resolveTypeHierarchy(Params.item.uri.file(), Params.item, + Params.resolve, Params.direction, std::move(Serialize)); } @@ -1370,7 +1365,7 @@ void ClangdLSPServer::onPrepareTypeHierarchy( void ClangdLSPServer::onSuperTypes( const ResolveTypeHierarchyItemParams &Params, Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) { - Server->superTypes(Params.item, std::move(Reply)); + Server->superTypes(Params.item.uri.file(), Params.item, std::move(Reply)); } void ClangdLSPServer::onSubTypes( diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 6ada3fd9e6e47..64dcbfbc55325 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -133,7 +133,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks, Callback<std::vector<Location>>); void onGoToImplementation(const TextDocumentPositionParams &, Callback<std::vector<Location>>); - void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>); + void onReference(const ReferenceParams &, + Callback<std::vector<ReferenceLocation>>); void onSwitchSourceHeader(const TextDocumentIdentifier &, Callback<std::optional<URIForFile>>); void onDocumentHighlight(const TextDocumentPositionParams &, @@ -243,7 +244,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks, /// Used to indicate the ClangdLSPServer is being destroyed. std::atomic<bool> IsBeingDestroyed = {false}; - // FIXME: The caching is a temporary solution to get corresponding clangd + // FIXME: The caching is a temporary solution to get corresponding clangd // diagnostic from a LSP diagnostic. // Ideally, ClangdServer can generate an identifier for each diagnostic, // emit them via the LSP's data field (which was newly added in LSP 3.16). @@ -259,11 +260,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks, return {LSPDiag.range, LSPDiag.message}; } /// A map from LSP diagnostic to clangd-naive diagnostic. - typedef std::map<DiagKey, ClangdServer::DiagRef> - DiagnosticToDiagRefMap; + typedef std::map<DiagKey, ClangdServer::DiagRef> DiagnosticToDiagRefMap; /// Caches the mapping LSP and clangd-naive diagnostics per file. - llvm::StringMap<DiagnosticToDiagRefMap> - DiagRefMap; + llvm::StringMap<DiagnosticToDiagRefMap> DiagRefMap; // Last semantic-tokens response, for incremental requests. std::mutex SemanticTokensMutex; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index c210eda6bfd2e..5b6525d8f842e 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -62,8 +62,8 @@ namespace clangd { namespace { // Tracks number of times a tweak has been offered. -static constexpr trace::Metric TweakAvailable( - "tweak_available", trace::Metric::Counter, "tweak_id"); +static constexpr trace::Metric + TweakAvailable("tweak_available", trace::Metric::Counter, "tweak_id"); // Update the FileIndex with new ASTs and plumb the diagnostics responses. struct UpdateIndexCallbacks : public ParsingCallbacks { @@ -872,45 +872,42 @@ void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve, } void ClangdServer::superTypes( - const TypeHierarchyItem &Item, + PathRef File, const TypeHierarchyItem &Item, Callback<std::optional<std::vector<TypeHierarchyItem>>> CB) { - WorkScheduler->run("typeHierarchy/superTypes", /*Path=*/"", - [=, CB = std::move(CB)]() mutable { - CB(clangd::superTypes(Item, Index)); - }); + auto Action = [File = File.str(), Item, CB = std::move(CB), + this](llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::superTypes(Item, Index, InpAST->AST)); + }; + WorkScheduler->runWithAST("superTypes Calls", File, std::move(Action)); } void ClangdServer::subTypes(PathRef File, const TypeHierarchyItem &Item, Callback<std::vector<TypeHierarchyItem>> CB) { - auto Action = [File = File.str(), Item, CB = std::move(CB), this]( - llvm::Expected<InputsAndAST> InpAST) mutable { - if (!InpAST) - return CB(InpAST.takeError()); - CB(clangd::subTypes(Item, Index, InpAST->AST)); - }; - WorkScheduler->runWithAST("subTypes Calls", File, std::move(Action)); - // WorkScheduler->run( - // "typeHierarchy/subTypes", /*Path=*/"", - // [=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); }); + auto Action = [File = File.str(), Item, CB = std::move(CB), + this](llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::subTypes(Item, Index, InpAST->AST)); + }; + WorkScheduler->runWithAST("subTypes Calls", File, std::move(Action)); } -void ClangdServer::resolveTypeHierarchy(PathRef File, - TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, +void ClangdServer::resolveTypeHierarchy( + PathRef File, TypeHierarchyItem Item, int Resolve, + TypeHierarchyDirection Direction, Callback<std::optional<TypeHierarchyItem>> CB) { - auto Action = [=, CB = std::move(CB), this]( - llvm::Expected<InputsAndAST> InpAST) mutable { + auto Action = [=, CB = std::move(CB), + this](llvm::Expected<InputsAndAST> InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index, InpAST->AST); CB(Item); }; - WorkScheduler->runWithAST("resolveTypeHierarchy Calls", File, std::move(Action)); - // WorkScheduler->run( - // "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { - // clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); - // CB(Item); - // }); + WorkScheduler->runWithAST("resolveTypeHierarchy Calls", File, + std::move(Action)); } void ClangdServer::prepareCallHierarchy( @@ -925,11 +922,10 @@ void ClangdServer::prepareCallHierarchy( } void ClangdServer::incomingCalls( - PathRef File, - const CallHierarchyItem &Item, + PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyIncomingCall>> CB) { - auto Action = [Item, CB = std::move(CB), this]( - llvm::Expected<InputsAndAST> InpAST) mutable { + auto Action = [Item, CB = std::move(CB), + this](llvm::Expected<InputsAndAST> InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); CB(clangd::incomingCalls(Item, Index, InpAST->AST)); @@ -949,11 +945,10 @@ void ClangdServer::inlayHints(PathRef File, std::optional<Range> RestrictRange, } void ClangdServer::outgoingCalls( - PathRef File, - const CallHierarchyItem &Item, + PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyOutgoingCall>> CB) { - auto Action = [Item, CB = std::move(CB), this]( - llvm::Expected<InputsAndAST> InpAST) mutable { + auto Action = [Item, CB = std::move(CB), + this](llvm::Expected<InputsAndAST> InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); CB(clangd::outgoingCalls(Item, Index, InpAST->AST)); diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 431856aa1fadb..c582a96691deb 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -283,7 +283,7 @@ class ClangdServer { TypeHierarchyDirection Direction, Callback<std::vector<TypeHierarchyItem>> CB); /// Get direct parents of a type hierarchy item. - void superTypes(const TypeHierarchyItem &Item, + void superTypes(PathRef File, const TypeHierarchyItem &Item, Callback<std::optional<std::vector<TypeHierarchyItem>>> CB); /// Get direct children of a type hierarchy item. void subTypes(PathRef File, const TypeHierarchyItem &Item, @@ -299,8 +299,7 @@ class ClangdServer { Callback<std::vector<CallHierarchyItem>> CB); /// Resolve incoming calls for a given call hierarchy item. - void incomingCalls(PathRef File, - const CallHierarchyItem &Item, + void incomingCalls(PathRef File, const CallHierarchyItem &Item, Callback<std::vector<CallHierarchyIncomingCall>> CB); /// Resolve outgoing calls for a given call hierarchy item. diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index a88c9a391f97a..15faec19bc6a4 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1548,6 +1548,9 @@ struct TypeHierarchyItem { /// The kind of this item. SymbolKind kind; + /// The symbol tags for this item. + std::vector<SymbolTag> tags; + /// More detail for this item, e.g. the signature of a function. std::optional<std::string> detail; diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index b48a834224cd7..fc1bc75de26e5 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1812,6 +1812,7 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) { HI.name = printName(Ctx, ND); HI.detail = printQualifiedName(ND); HI.kind = SK; + HI.tags = getSymbolTags(ND); HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()), sourceLocToPosition(SM, DeclRange->getEnd())}; HI.selectionRange = Range{NameBegin, NameEnd}; @@ -1930,15 +1931,14 @@ static void fillSubTypes(const SymbolID &ID, RelationsRequest Req; Req.Subjects.insert(ID); Req.Predicate = RelationKind::BaseOf; - Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) { + Index->relations(Req, [&Levels, &Index, &SubTypes, &TUPath, + &AST](const SymbolID &Subject, const Symbol &Object) { std::optional<TypeHierarchyItem> ChildSym; if (auto *ND = getNamedDeclFromSymbol(Object, AST)) { ChildSym = declToTypeHierarchyItem(*ND, AST.tuPath()); - elog("fillSubTypes: declToTypeHierarchyItem, {0}", ChildSym.has_value()); } else { ChildSym = symbolToTypeHierarchyItem(Object, TUPath); - elog("fillSubTypes: symbolToTypeHierarchyItem, {0}", ChildSym.has_value()); } if (ChildSym) { if (Levels > 1) { @@ -2167,15 +2167,15 @@ static QualType typeForNode(const ASTContext &Ctx, const HeuristicResolver *H, return QualType(); } -// Given a type targeted by the cursor, return one or more types that are more interesting -// to target. -static void unwrapFindType( - QualType T, const HeuristicResolver* H, llvm::SmallVector<QualType>& Out) { +// Given a type targeted by the cursor, return one or more types that are more +// interesting to target. +static void unwrapFindType(QualType T, const HeuristicResolver *H, + llvm::SmallVector<QualType> &Out) { if (T.isNull()) return; // If there's a specific type alias, point at that rather than unwrapping. - if (const auto* TDT = T->getAs<TypedefType>()) + if (const auto *TDT = T->getAs<TypedefType>()) return Out.push_back(QualType(TDT, 0)); // Pointers etc => pointee type. @@ -2209,8 +2209,8 @@ static void unwrapFindType( } // Convenience overload, to allow calling this without the out-parameter -static llvm::SmallVector<QualType> unwrapFindType( - QualType T, const HeuristicResolver* H) { +static llvm::SmallVector<QualType> unwrapFindType(QualType T, + const HeuristicResolver *H) { llvm::SmallVector<QualType> Result; unwrapFindType(T, H, Result); return Result; @@ -2232,9 +2232,9 @@ std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos, std::vector<LocatedSymbol> LocatedSymbols; // NOTE: unwrapFindType might return duplicates for something like - // unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you some - // information about the type you may have not known before - // (since unique_ptr<unique_ptr<T>> != unique_ptr<T>). + // unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you + // some information about the type you may have not known before (since + // unique_ptr<unique_ptr<T>> != unique_ptr<T>). for (const QualType &Type : unwrapFindType( typeForNode(AST.getASTContext(), AST.getHeuristicResolver(), N), AST.getHeuristicResolver())) @@ -2327,7 +2327,8 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, if (Index) { if (auto ID = getSymbolID(CXXRD)) - fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath, AST); + fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath, + AST); } } Results.emplace_back(std::move(*Result)); @@ -2337,7 +2338,8 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, } std::optional<std::vector<TypeHierarchyItem>> -superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { +superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST) { std::vector<TypeHierarchyItem> Results; if (!Item.data.parents) return std::nullopt; @@ -2349,8 +2351,14 @@ superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { Req.IDs.insert(Parent.symbolID); IDToData[Parent.symbolID] = &Parent; } - Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) { - if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) { + Index->lookup(Req, [&Item, &Results, &IDToData, &AST](const Symbol &S) { + std::optional<TypeHierarchyItem> THI; + if (auto *ND = getNamedDeclFromSymbol(S, AST)) { + THI = declToTypeHierarchyItem(*ND, AST.tuPath()); + } else { + THI = symbolToTypeHierarchyItem(S, Item.uri.file()); + } + if (THI) { THI->data = *IDToData.lookup(S.ID); Results.emplace_back(std::move(*THI)); } @@ -2359,7 +2367,8 @@ superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) { } std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, - const SymbolIndex *Index, const ParsedAST &AST) { + const SymbolIndex *Index, + const ParsedAST &AST) { std::vector<TypeHierarchyItem> Results; fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file(), AST); for (auto &ChildSym : Results) @@ -2369,8 +2378,7 @@ std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, - const SymbolIndex *Index, - const ParsedAST &AST) { + const SymbolIndex *Index, const ParsedAST &AST) { // We only support typeHierarchy/resolve for children, because for parents // we ignore ResolveLevels and return all levels of parents eagerly. if (!Index || Direction == TypeHierarchyDirection::Parents || diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 1582c481ea5c2..f2652b5f274db 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -132,9 +132,11 @@ std::vector<TypeHierarchyItem> getTypeHierarchy( const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{}); /// Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside -/// the item. +/// the item or using the AST. std::optional<std::vector<TypeHierarchyItem>> -superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index); +superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index, + const ParsedAST &AST); + /// Returns direct children of a TypeHierarchyItem. std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index, diff --git a/clang-tools-extra/clangd/test/call-hierarchy.test b/clang-tools-extra/clangd/test/call-hierarchy.test index 44c89d22d9c85..aba1418e3ec84 100644 --- a/clang-tools-extra/clangd/test/call-hierarchy.test +++ b/clang-tools-extra/clangd/test/call-hierarchy.test @@ -32,6 +32,9 @@ # CHECK-NEXT: "line": 0 # CHECK-NEXT: } # CHECK-NEXT: }, +# CHECK-NEXT: "tags": [ +# CHECK-NEXT: 18 +# CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" # CHECK-NEXT: } --- diff --git a/clang-tools-extra/clangd/test/type-hierarchy.test b/clang-tools-extra/clangd/test/type-hierarchy.test index 065f129e05d2f..c7b8a16ae51e2 100644 --- a/clang-tools-extra/clangd/test/type-hierarchy.test +++ b/clang-tools-extra/clangd/test/type-hierarchy.test @@ -46,6 +46,7 @@ # CHECK-NEXT: } # CHECK-NEXT: }, # CHECK-NEXT: "tags": [ +# CHECK-NEXT: 18, # CHECK-NEXT: 19 # CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" @@ -71,11 +72,11 @@ # CHECK-NEXT: "name": "Child1", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 13, +# CHECK-NEXT: "character": 25, # CHECK-NEXT: "line": 1 # CHECK-NEXT: }, # CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "character": 0, # CHECK-NEXT: "line": 1 # CHECK-NEXT: } # CHECK-NEXT: }, @@ -90,6 +91,7 @@ # CHECK-NEXT: } # CHECK-NEXT: }, # CHECK-NEXT: "tags": [ +# CHECK-NEXT: 18, # CHECK-NEXT: 19 # CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" @@ -144,6 +146,7 @@ # CHECK-NEXT: } # CHECK-NEXT: }, # CHECK-NEXT: "tags": [ +# CHECK-NEXT: 18, # CHECK-NEXT: 19 # CHECK-NEXT: ], # CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp" diff --git a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp index ae2f806d0b17e..8696e6d4b0790 100644 --- a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp @@ -809,7 +809,7 @@ struct Chil^d : Parent {}; TypeHierarchyDirection::Children, Index.get(), testPath(TU.Filename)); ASSERT_THAT(Result, SizeIs(1)); - auto Parents = superTypes(Result.front(), Index.get()); + auto Parents = superTypes(Result.front(), Index.get(), AST); EXPECT_THAT(Parents, Optional(UnorderedElementsAre( AllOf(withName("Parent"), >From f40d2c70912aab9d166b0e81330973722d614f66 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Thu, 22 Jan 2026 13:18:40 +0100 Subject: [PATCH 09/10] Extended unit-tests to check the occurrence of symbol tags in call-hierarchy and type-hierarchy. --- .../clangd/unittests/CallHierarchyTests.cpp | 56 +++++++++++++++++++ .../clangd/unittests/TypeHierarchyTests.cpp | 30 ++++++---- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp index 2891b420427d2..1d2ae22463a08 100644 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -48,6 +48,12 @@ MATCHER_P(withDetail, N, "") { return arg.detail == N; } MATCHER_P(withFile, N, "") { return arg.uri.file() == N; } MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; } +template <typename... Tags> +::testing::Matcher<CallHierarchyItem> withSymbolTags(Tags... tags) { + // Matches the tags vector ignoring element order. + return Field(&CallHierarchyItem::tags, UnorderedElementsAre(tags...)); +} + template <class ItemMatcher> ::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) { return Field(&CallHierarchyIncomingCall::from, M); @@ -728,6 +734,56 @@ TEST(CallHierarchy, CallInDifferentFileThanCaller) { ElementsAre(AllOf(from(withName("caller")), iFromRanges()))); } +TEST(CallHierarchy, IncomingCalls) { + Annotations Source(R"cpp( + class A { + public: + void call^ee() {}; + }; + void caller(A &a) { + a.callee(); + } + )cpp"); + TestTU TU = TestTU::withCode(Source.code()); + auto AST = TU.build(); + auto Index = TU.index(); + + std::vector<CallHierarchyItem> Items = + prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); + ASSERT_THAT(Items, ElementsAre(withName("callee"))); + + auto Incoming = incomingCalls(Items[0], Index.get(), AST); + EXPECT_THAT( + Incoming, + UnorderedElementsAre(AllOf(from( + AllOf(withName("caller"), withSymbolTags(SymbolTag::Declaration, + SymbolTag::Definition)))))); +} + +TEST(CallHierarchy, OutgoingCalls) { + Annotations Source(R"cpp( + void callee() {} + class A { + public: + void call^er() { + callee(); + }; + }; + )cpp"); + TestTU TU = TestTU::withCode(Source.code()); + auto AST = TU.build(); + auto Index = TU.index(); + + std::vector<CallHierarchyItem> Items = + prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); + ASSERT_THAT(Items, ElementsAre(withName("caller"))); + + auto Outgoing = outgoingCalls(Items[0], Index.get(), AST); + EXPECT_THAT(Outgoing, UnorderedElementsAre(AllOf( + to(AllOf(withName("callee"), + withSymbolTags(SymbolTag::Declaration, + SymbolTag::Definition)))))); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp index 8696e6d4b0790..21adbb6d0d3a3 100644 --- a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp @@ -54,6 +54,12 @@ MATCHER_P(withResolveParents, M, "") { return testing::ExplainMatchResult(M, arg.data.parents, result_listener); } +template <typename... Tags> +::testing::Matcher<TypeHierarchyItem> withSymbolTags(Tags... tags) { + // Matches the tags vector ignoring element order. + return Field(&TypeHierarchyItem::tags, UnorderedElementsAre(tags...)); +} + TEST(FindRecordTypeAt, TypeOrVariable) { Annotations Source(R"cpp( struct Ch^ild2 { @@ -770,10 +776,10 @@ struct Child2b : Child1 {}; TEST(Standard, SubTypes) { Annotations Source(R"cpp( -struct Pare^nt1 {}; -struct Parent2 {}; -struct Child : Parent1, Parent2 {}; -)cpp"); + struct Pare^nt1 {}; + struct Parent2 {}; + struct Child final : Parent1, Parent2 {}; + )cpp"); TestTU TU = TestTU::withCode(Source.code()); auto AST = TU.build(); @@ -791,15 +797,17 @@ struct Child : Parent1, Parent2 {}; Children, UnorderedElementsAre( AllOf(withName("Child"), + withSymbolTags(SymbolTag::Declaration, SymbolTag::Definition, + SymbolTag::Final), withResolveParents(Optional(UnorderedElementsAre(withResolveID( getSymbolID(&findDecl(AST, "Parent1")).str()))))))); } TEST(Standard, SuperTypes) { Annotations Source(R"cpp( -struct Parent {}; -struct Chil^d : Parent {}; -)cpp"); + struct Parent {}; + struct Chil^d : Parent {}; + )cpp"); TestTU TU = TestTU::withCode(Source.code()); auto AST = TU.build(); @@ -811,9 +819,11 @@ struct Chil^d : Parent {}; ASSERT_THAT(Result, SizeIs(1)); auto Parents = superTypes(Result.front(), Index.get(), AST); - EXPECT_THAT(Parents, Optional(UnorderedElementsAre( - AllOf(withName("Parent"), - withResolveParents(Optional(IsEmpty())))))); + EXPECT_THAT(Parents, + Optional(UnorderedElementsAre(AllOf( + withName("Parent"), + withSymbolTags(SymbolTag::Declaration, SymbolTag::Definition), + withResolveParents(Optional(IsEmpty())))))); } } // namespace } // namespace clangd >From 79c265dff9e7f89225b1210e5868bdebd5d97b69 Mon Sep 17 00:00:00 2001 From: Dimitri Ratz <[email protected]> Date: Fri, 23 Jan 2026 15:46:57 +0100 Subject: [PATCH 10/10] Simplify lambda capture by explicitly moving `Item` and parameters. Fix code format. --- clang-tools-extra/clangd/ClangdServer.cpp | 2 +- clang-tools-extra/clangd/FindSymbols.h | 2 +- clang-tools-extra/clangd/Protocol.cpp | 6 ++-- .../clangd/unittests/CallHierarchyTests.cpp | 28 +++++++++++-------- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 5b6525d8f842e..e304db2914483 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -899,7 +899,7 @@ void ClangdServer::resolveTypeHierarchy( PathRef File, TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, Callback<std::optional<TypeHierarchyItem>> CB) { - auto Action = [=, CB = std::move(CB), + auto Action = [Item = std::move(Item), Resolve, Direction, CB = std::move(CB), this](llvm::Expected<InputsAndAST> InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); diff --git a/clang-tools-extra/clangd/FindSymbols.h b/clang-tools-extra/clangd/FindSymbols.h index 169efa9a84584..d85a03ebe449b 100644 --- a/clang-tools-extra/clangd/FindSymbols.h +++ b/clang-tools-extra/clangd/FindSymbols.h @@ -14,8 +14,8 @@ #include "Protocol.h" #include "index/Symbol.h" -#include "llvm/ADT/StringRef.h" #include "clang/AST/Decl.h" +#include "llvm/ADT/StringRef.h" namespace clang { class NamedDecl; diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 55c06be0ae21c..e1e3180dc4af8 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -209,7 +209,7 @@ bool fromJSON(const llvm::json::Value &Params, ChangeAnnotation &R, O.map("needsConfirmation", R.needsConfirmation) && O.mapOptional("description", R.description); } -llvm::json::Value toJSON(const ChangeAnnotation & CA) { +llvm::json::Value toJSON(const ChangeAnnotation &CA) { llvm::json::Object Result{{"label", CA.label}}; if (CA.needsConfirmation) Result["needsConfirmation"] = *CA.needsConfirmation; @@ -859,7 +859,7 @@ llvm::json::Value toJSON(const SymbolInformation &P) { }; if (P.score) O["score"] = *P.score; - if(!P.tags.empty()) + if (!P.tags.empty()) O["tags"] = P.tags; return std::move(O); } @@ -1439,7 +1439,7 @@ llvm::json::Value toJSON(const TypeHierarchyItem &I) { if (I.detail) Result["detail"] = I.detail; - if(!I.tags.empty()) + if (!I.tags.empty()) Result["tags"] = I.tags; return std::move(Result); } diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp index 1d2ae22463a08..fb911ba07354a 100644 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -305,11 +305,11 @@ TEST(CallHierarchy, OutgoingOneFile) { auto OugoingLevel1 = outgoingCalls(Items[0], Index.get(), AST); ASSERT_THAT( OugoingLevel1, - ElementsAre( - AllOf(to(AllOf(withName("Foo::caller1"), withDetail("ns::Foo::caller1"))), - oFromRanges(Source.range("Caller1C"))), - AllOf(to(AllOf(withName("caller2"), withDetail("caller2"))), - oFromRanges(Source.range("Caller2"))))); + ElementsAre(AllOf(to(AllOf(withName("Foo::caller1"), + withDetail("ns::Foo::caller1"))), + oFromRanges(Source.range("Caller1C"))), + AllOf(to(AllOf(withName("caller2"), withDetail("caller2"))), + oFromRanges(Source.range("Caller2"))))); auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get(), AST); ASSERT_THAT( @@ -408,7 +408,8 @@ TEST(CallHierarchy, MultiFileCpp) { withDetail("nsa::caller1"))), iFromRanges(Caller1C.range())))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); + auto IncomingLevel2 = + incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT( IncomingLevel2, ElementsAre( @@ -417,13 +418,15 @@ TEST(CallHierarchy, MultiFileCpp) { AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))), iFromRanges(Caller3C.range("Caller1"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); + auto IncomingLevel3 = + incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel3, ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))), iFromRanges(Caller3C.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); + auto IncomingLevel4 = + incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); }; @@ -564,7 +567,8 @@ TEST(CallHierarchy, IncomingMultiFileObjC) { ElementsAre(AllOf(from(withName("caller1")), iFromRanges(Caller1C.range())))); - auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get(), AST); + auto IncomingLevel2 = + incomingCalls(IncomingLevel1[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel2, ElementsAre(AllOf(from(withName("caller2")), iFromRanges(Caller2C.range("A"), @@ -572,12 +576,14 @@ TEST(CallHierarchy, IncomingMultiFileObjC) { AllOf(from(withName("caller3")), iFromRanges(Caller3C.range("Caller1"))))); - auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get(), AST); + auto IncomingLevel3 = + incomingCalls(IncomingLevel2[0].from, Index.get(), AST); ASSERT_THAT(IncomingLevel3, ElementsAre(AllOf(from(withName("caller3")), iFromRanges(Caller3C.range("Caller2"))))); - auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get(), AST); + auto IncomingLevel4 = + incomingCalls(IncomingLevel3[0].from, Index.get(), AST); EXPECT_THAT(IncomingLevel4, IsEmpty()); }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
