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 @@ -21,6 +21,7 @@ #include "clang/Index/IndexingOptions.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" +#include #include #include #include @@ -138,6 +139,19 @@ 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 symbols nested more than a certain level to keep + // indexing time reasonable. + static constexpr size_t MaxContextDepth = 10; + size_t ContextDepth = 0; + for (auto *Ctx = D->getDeclContext(); Ctx && !Ctx->isTranslationUnit(); + Ctx = Ctx->getParent()) { + if (++ContextDepth == MaxContextDepth) + 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/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 diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp --- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp @@ -177,16 +177,21 @@ }; } } // namespace nx - )", - ""); - auto AST = File.build(); - EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false)); - EXPECT_TRUE(shouldCollect("ClassInLambda", /*Qualified=*/false)); - EXPECT_TRUE(shouldCollect("LocalBase", /*Qualified=*/false)); - EXPECT_TRUE(shouldCollect("LocalVirtual", /*Qualified=*/false)); - EXPECT_TRUE(shouldCollect("LocalConcrete", /*Qualified=*/false)); - EXPECT_FALSE(shouldCollect("BaseMember", /*Qualified=*/false)); - EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false)); + )"); + for (bool CollectLocalClasses : {true, false}) { + EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false)); + EXPECT_FALSE(shouldCollect("LocalLambda", /*Qualified=*/false)); + EXPECT_FALSE(shouldCollect("BaseMember", /*Qualified=*/false)); + + EXPECT_EQ(CollectLocalClasses, + shouldCollect("ClassInLambda", /*Qualified=*/false)); + EXPECT_EQ(CollectLocalClasses, + shouldCollect("LocalBase", /*Qualified=*/false)); + EXPECT_EQ(CollectLocalClasses, + shouldCollect("LocalVirtual", /*Qualified=*/false)); + EXPECT_EQ(CollectLocalClasses, + shouldCollect("LocalConcrete", /*Qualified=*/false)); + } } TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) { @@ -913,12 +918,12 @@ llvm::StringRef Main; llvm::StringRef TargetSymbolName; } TestCases[] = { - { - R"cpp( + { + R"cpp( struct Foo; #define MACRO Foo )cpp", - R"cpp( + R"cpp( struct $spelled[[Foo]] { $spelled[[Foo]](); ~$spelled[[Foo]](); @@ -926,24 +931,24 @@ $spelled[[Foo]] Variable1; $implicit[[MACRO]] Variable2; )cpp", - "Foo", - }, - { - R"cpp( + "Foo", + }, + { + R"cpp( class Foo { public: Foo() = default; }; )cpp", - R"cpp( + R"cpp( void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();} )cpp", - "Foo::Foo" /// constructor. - }, + "Foo::Foo" /// constructor. + }, }; CollectorOpts.RefFilter = RefKind::All; CollectorOpts.RefsInHeaders = false; - for (const auto& T : TestCases) { + for (const auto &T : TestCases) { Annotations Header(T.Header); Annotations Main(T.Main); // Reset the file system.