Author: Nathan Ridge Date: 2021-01-12T13:57:54-05:00 New Revision: 4718ec01669b01373180f4cd1256c6e2dd6f3999
URL: https://github.com/llvm/llvm-project/commit/4718ec01669b01373180f4cd1256c6e2dd6f3999 DIFF: https://github.com/llvm/llvm-project/commit/4718ec01669b01373180f4cd1256c6e2dd6f3999.diff LOG: [clangd] Avoid recursion in TargetFinder::add() Fixes https://github.com/clangd/clangd/issues/633 Differential Revision: https://reviews.llvm.org/D94382 Added: Modified: clang-tools-extra/clangd/FindTarget.cpp clang-tools-extra/clangd/FindTarget.h clang-tools-extra/clangd/unittests/FindTargetTests.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 9a502a84e36f..84316659daad 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -330,6 +330,7 @@ struct TargetFinder { llvm::SmallDenseMap<const NamedDecl *, std::pair<RelSet, /*InsertionOrder*/ size_t>> Decls; + llvm::SmallDenseMap<const Decl *, RelSet> Seen; RelSet Flags; template <typename T> 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<UsingDirectiveDecl>(D)) D = UDD->getNominatedNamespaceAsWritten(); diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h index 435e4f4ac038..92e4354d1eaa 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 dd7e9878a6d5..46e17dc053c0 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 <typename> 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 <typename T> + concept Leaf = false; + + template <typename Tree> + struct descend_left { + using type = typename descend_left<typename Tree::left>::[[type]]; + }; + + template <Leaf Tree> + struct descend_left<Tree> { + using type = typename Tree::value; + }; + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc", + {"using type = typename descend_left<typename Tree::left>::type", + Rel::Alias | Rel::Underlying}); +} + TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp( _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits