[clangd] Dont index deeply nested symbols

This is fix for some timeouts and OOM problems faced while indexing an
auto-generated file with thousands of nested lambdas.

Differential Revision: https://reviews.llvm.org/D101066
This commit is contained in:
Kadir Cetinkaya 2021-04-22 16:08:48 +02:00
parent 4b13b7581d
commit 4581bf31bb
No known key found for this signature in database
GPG Key ID: E39E36B8D2057ED6
5 changed files with 79 additions and 0 deletions

View File

@ -524,5 +524,14 @@ bool hasUnstableLinkage(const Decl *D) {
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

View File

@ -171,6 +171,12 @@ std::string getQualification(ASTContext &Context,
/// 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

View File

@ -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 <cstddef>
#include <functional>
#include <memory>
#include <utility>
@ -138,6 +140,12 @@ public:
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())

View File

@ -351,6 +351,32 @@ TEST(ClangdAST, PrintType) {
}
}
}
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

View File

@ -281,6 +281,36 @@ TEST_F(IndexActionTest, SkipFiles) {
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