diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 9a502a84e36ff4..84316659daaddf 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -330,6 +330,7 @@ struct TargetFinder { llvm::SmallDenseMap> Decls; + llvm::SmallDenseMap Seen; RelSet Flags; template void debug(T &Node, RelSet Flags) { @@ -359,6 +360,15 @@ struct TargetFinder { if (!D) return; debug(*D, Flags); + + // Avoid recursion (which can arise in the presence of heuristic + // resolution of dependent names) by exiting early if we have + // already seen this decl with all flags in Flags. + auto Res = Seen.try_emplace(D); + if (!Res.second && Res.first->second.contains(Flags)) + return; + Res.first->second |= Flags; + if (const UsingDirectiveDecl *UDD = llvm::dyn_cast(D)) D = UDD->getNominatedNamespaceAsWritten(); diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h index 435e4f4ac038b1..92e4354d1eaadc 100644 --- a/clang-tools-extra/clangd/FindTarget.h +++ b/clang-tools-extra/clangd/FindTarget.h @@ -194,6 +194,9 @@ class DeclRelationSet { S &= Other.S; return *this; } + bool contains(DeclRelationSet Other) const { + return (S & Other.S) == Other.S; + } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet); }; // The above operators can't be looked up if both sides are enums. diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index dd7e9878a6d51f..46e17dc053c043 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -787,6 +787,47 @@ TEST_F(TargetDeclTest, DependentTypes) { "template struct B"); } +TEST_F(TargetDeclTest, TypedefCascade) { + Code = R"cpp( + struct C { + using type = int; + }; + struct B { + using type = C::type; + }; + struct A { + using type = B::type; + }; + A::[[type]] waldo; + )cpp"; + EXPECT_DECLS("TypedefTypeLoc", + {"using type = int", Rel::Alias | Rel::Underlying}, + {"using type = C::type", Rel::Alias | Rel::Underlying}, + {"using type = B::type", Rel::Alias}); +} + +TEST_F(TargetDeclTest, RecursiveTemplate) { + Flags.push_back("-std=c++20"); // the test case uses concepts + + Code = R"cpp( + template + concept Leaf = false; + + template + struct descend_left { + using type = typename descend_left::[[type]]; + }; + + template + struct descend_left { + using type = typename Tree::value; + }; + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc", + {"using type = typename descend_left::type", + Rel::Alias | Rel::Underlying}); +} + TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp(