diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -171,6 +171,12 @@ /// the cached value is incorrect. (clang catches this with an assertion). bool hasUnstableLinkage(const Decl *D); +/// Checks whether \p D is more than \p MaxDepth away from translation unit +/// scope. +/// This is useful for limiting traversals to keep operation latencies +/// reasonable. +bool isDeeplyNested(const Decl *D, unsigned MaxDepth = 10); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -524,5 +524,14 @@ return VD && !VD->getType().isNull() && VD->getType()->isUndeducedType(); } +bool isDeeplyNested(const Decl *D, unsigned MaxDepth) { + size_t ContextDepth = 0; + for (auto *Ctx = D->getDeclContext(); Ctx && !Ctx->isTranslationUnit(); + Ctx = Ctx->getParent()) { + if (++ContextDepth == MaxDepth) + return true; + } + return false; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp --- a/clang-tools-extra/clangd/index/IndexAction.cpp +++ b/clang-tools-extra/clangd/index/IndexAction.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "IndexAction.h" +#include "AST.h" #include "Headers.h" #include "index/Relation.h" #include "index/SymbolOrigin.h" @@ -21,6 +22,7 @@ #include "clang/Index/IndexingOptions.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" +#include #include #include #include @@ -138,6 +140,12 @@ Includes(std::move(Includes)), Opts(Opts), PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) { this->Opts.ShouldTraverseDecl = [this](const Decl *D) { + // Many operations performed during indexing is linear in terms of depth + // of the decl (USR generation, name lookups, figuring out role of a + // reference are some examples). Since we index all the decls nested + // inside, it becomes quadratic. So we give up on nested symbols. + if (isDeeplyNested(D)) + return false; auto &SM = D->getASTContext().getSourceManager(); auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation())); if (!FID.isValid()) diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -351,6 +351,32 @@ } } } + +TEST(ClangdAST, IsDeeplyNested) { + Annotations Test( + R"cpp( + namespace ns { + class Foo { + void bar() { + class Bar {}; + } + }; + })cpp"); + TestTU TU = TestTU::withCode(Test.code()); + ParsedAST AST = TU.build(); + + EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/1)); + EXPECT_FALSE( + isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/2)); + + EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/2)); + EXPECT_FALSE( + isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/3)); + + EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/3)); + EXPECT_FALSE( + isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/4)); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/IndexActionTests.cpp b/clang-tools-extra/clangd/unittests/IndexActionTests.cpp --- a/clang-tools-extra/clangd/unittests/IndexActionTests.cpp +++ b/clang-tools-extra/clangd/unittests/IndexActionTests.cpp @@ -281,6 +281,36 @@ EXPECT_THAT(Ref.Location.FileURI, EndsWith("good.h")); } +TEST_F(IndexActionTest, SkipNestedSymbols) { + std::string MainFilePath = testPath("main.cpp"); + addFile(MainFilePath, R"cpp( + namespace ns1 { + namespace ns2 { + namespace ns3 { + namespace ns4 { + namespace ns5 { + namespace ns6 { + namespace ns7 { + namespace ns8 { + namespace ns9 { + class Bar {}; + void foo() { + class Baz {}; + } + } + } + } + } + } + } + } + } + })cpp"); + IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"}); + EXPECT_THAT(*IndexFile.Symbols, testing::Contains(HasName("foo"))); + EXPECT_THAT(*IndexFile.Symbols, testing::Contains(HasName("Bar"))); + EXPECT_THAT(*IndexFile.Symbols, Not(testing::Contains(HasName("Baz")))); +} } // namespace } // namespace clangd } // namespace clang