forked from OSchip/llvm-project
[clangd] Don't traverse the AST within uninteresting files during indexing
Summary: We already skip function bodies from these files while parsing, and drop symbols found in them. However, traversing their ASTs still takes a substantial amount of time. Non-scientific benchmark on my machine: background-indexing llvm-project (llvm+clang+clang-tools-extra), wall time before: 7:46 after: 5:13 change: -33% Indexer.cpp libclang should be updated too, I'm less familiar with that code, and it's doing tricky things with the ShouldSkipFunctionBody callback, so it needs to be done separately. Reviewers: kadircet Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D80296
This commit is contained in:
parent
80cc43b420
commit
1abb883a04
|
@ -132,11 +132,19 @@ public:
|
|||
std::function<void(RefSlab)> RefsCallback,
|
||||
std::function<void(RelationSlab)> RelationsCallback,
|
||||
std::function<void(IncludeGraph)> IncludeGraphCallback)
|
||||
: SymbolsCallback(SymbolsCallback),
|
||||
RefsCallback(RefsCallback), RelationsCallback(RelationsCallback),
|
||||
: SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),
|
||||
RelationsCallback(RelationsCallback),
|
||||
IncludeGraphCallback(IncludeGraphCallback), Collector(C),
|
||||
Includes(std::move(Includes)), Opts(Opts),
|
||||
PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}
|
||||
PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {
|
||||
this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
|
||||
auto &SM = D->getASTContext().getSourceManager();
|
||||
auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
|
||||
if (!FID.isValid())
|
||||
return true;
|
||||
return Collector->shouldIndexFile(FID);
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTConsumer>
|
||||
CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
|
||||
|
@ -146,15 +154,8 @@ public:
|
|||
CI.getPreprocessor().addPPCallbacks(
|
||||
std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
|
||||
|
||||
return index::createIndexingASTConsumer(
|
||||
Collector, Opts, CI.getPreprocessorPtr(),
|
||||
/*ShouldSkipFunctionBody=*/[this](const Decl *D) {
|
||||
auto &SM = D->getASTContext().getSourceManager();
|
||||
auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
|
||||
if (!FID.isValid())
|
||||
return false;
|
||||
return !Collector->shouldIndexFile(FID);
|
||||
});
|
||||
return index::createIndexingASTConsumer(Collector, Opts,
|
||||
CI.getPreprocessorPtr());
|
||||
}
|
||||
|
||||
bool BeginInvocation(CompilerInstance &CI) override {
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace {
|
|||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::EndsWith;
|
||||
using ::testing::Not;
|
||||
using ::testing::Pair;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
@ -75,8 +76,7 @@ public:
|
|||
new FileManager(FileSystemOptions(), InMemoryFileSystem));
|
||||
|
||||
auto Action = createStaticIndexingAction(
|
||||
SymbolCollector::Options(),
|
||||
[&](SymbolSlab S) { IndexFile.Symbols = std::move(S); },
|
||||
Opts, [&](SymbolSlab S) { IndexFile.Symbols = std::move(S); },
|
||||
[&](RefSlab R) { IndexFile.Refs = std::move(R); },
|
||||
[&](RelationSlab R) { IndexFile.Relations = std::move(R); },
|
||||
[&](IncludeGraph IG) { IndexFile.Sources = std::move(IG); });
|
||||
|
@ -99,11 +99,12 @@ public:
|
|||
|
||||
void addFile(llvm::StringRef Path, llvm::StringRef Content) {
|
||||
InMemoryFileSystem->addFile(Path, 0,
|
||||
llvm::MemoryBuffer::getMemBuffer(Content));
|
||||
llvm::MemoryBuffer::getMemBufferCopy(Content));
|
||||
FilePaths.push_back(std::string(Path));
|
||||
}
|
||||
|
||||
protected:
|
||||
SymbolCollector::Options Opts;
|
||||
std::vector<std::string> FilePaths;
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
|
||||
};
|
||||
|
@ -250,6 +251,36 @@ TEST_F(IndexActionTest, NoWarnings) {
|
|||
EXPECT_THAT(*IndexFile.Symbols, ElementsAre(HasName("foo"), HasName("bar")));
|
||||
}
|
||||
|
||||
TEST_F(IndexActionTest, SkipFiles) {
|
||||
std::string MainFilePath = testPath("main.cpp");
|
||||
addFile(MainFilePath, R"cpp(
|
||||
// clang-format off
|
||||
#include "good.h"
|
||||
#include "bad.h"
|
||||
// clang-format on
|
||||
)cpp");
|
||||
addFile(testPath("good.h"), R"cpp(
|
||||
struct S { int s; };
|
||||
void f1() { S f; }
|
||||
auto unskippable1() { return S(); }
|
||||
)cpp");
|
||||
addFile(testPath("bad.h"), R"cpp(
|
||||
struct T { S t; };
|
||||
void f2() { S f; }
|
||||
auto unskippable2() { return S(); }
|
||||
)cpp");
|
||||
Opts.FileFilter = [](const SourceManager &SM, FileID F) {
|
||||
return !SM.getFileEntryForID(F)->getName().endswith("bad.h");
|
||||
};
|
||||
IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});
|
||||
EXPECT_THAT(*IndexFile.Symbols,
|
||||
UnorderedElementsAre(HasName("S"), HasName("s"), HasName("f1"),
|
||||
HasName("unskippable1")));
|
||||
for (const auto &Pair : *IndexFile.Refs)
|
||||
for (const auto &Ref : Pair.second)
|
||||
EXPECT_THAT(Ref.Location.FileURI, EndsWith("good.h"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -30,22 +30,21 @@ namespace serialization {
|
|||
}
|
||||
|
||||
namespace index {
|
||||
class IndexDataConsumer;
|
||||
class IndexDataConsumer;
|
||||
|
||||
/// Creates an ASTConsumer that indexes all symbols (macros and AST decls).
|
||||
std::unique_ptr<ASTConsumer>
|
||||
createIndexingASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
const IndexingOptions &Opts,
|
||||
std::shared_ptr<Preprocessor> PP);
|
||||
|
||||
std::unique_ptr<ASTConsumer> createIndexingASTConsumer(
|
||||
std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP,
|
||||
// Prefer to set Opts.ShouldTraverseDecl and use the above overload.
|
||||
// This version is only needed if used to *track* function body parsing.
|
||||
std::function<bool(const Decl *)> ShouldSkipFunctionBody);
|
||||
|
||||
inline std::unique_ptr<ASTConsumer> createIndexingASTConsumer(
|
||||
std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP) {
|
||||
return createIndexingASTConsumer(
|
||||
std::move(DataConsumer), Opts, std::move(PP),
|
||||
/*ShouldSkipFunctionBody=*/[](const Decl *) { return false; });
|
||||
}
|
||||
|
||||
/// Creates a frontend action that indexes all symbols (macros and AST decls).
|
||||
std::unique_ptr<FrontendAction>
|
||||
createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
|
|
|
@ -34,6 +34,12 @@ struct IndexingOptions {
|
|||
// Has no effect if IndexFunctionLocals are false.
|
||||
bool IndexParametersInDeclarations = false;
|
||||
bool IndexTemplateParameters = false;
|
||||
|
||||
// If set, skip indexing inside some declarations for performance.
|
||||
// This prevents traversal, so skipping a struct means its declaration an
|
||||
// members won't be indexed, but references elsewhere to that struct will be.
|
||||
// Currently this is only checked for top-level declarations.
|
||||
std::function<bool(const Decl *)> ShouldTraverseDecl;
|
||||
};
|
||||
|
||||
} // namespace index
|
||||
|
|
|
@ -765,6 +765,9 @@ bool IndexingContext::indexTopLevelDecl(const Decl *D) {
|
|||
if (isa<ObjCMethodDecl>(D))
|
||||
return true; // Wait for the objc container.
|
||||
|
||||
if (IndexOpts.ShouldTraverseDecl && !IndexOpts.ShouldTraverseDecl(D))
|
||||
return true; // skip
|
||||
|
||||
return indexDecl(D);
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,21 @@ std::unique_ptr<ASTConsumer> index::createIndexingASTConsumer(
|
|||
ShouldSkipFunctionBody);
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTConsumer> clang::index::createIndexingASTConsumer(
|
||||
std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP) {
|
||||
std::function<bool(const Decl *)> ShouldSkipFunctionBody = [](const Decl *) {
|
||||
return false;
|
||||
};
|
||||
if (Opts.ShouldTraverseDecl)
|
||||
ShouldSkipFunctionBody =
|
||||
[ShouldTraverseDecl(Opts.ShouldTraverseDecl)](const Decl *D) {
|
||||
return !ShouldTraverseDecl(D);
|
||||
};
|
||||
return createIndexingASTConsumer(std::move(DataConsumer), Opts, std::move(PP),
|
||||
std::move(ShouldSkipFunctionBody));
|
||||
}
|
||||
|
||||
std::unique_ptr<FrontendAction>
|
||||
index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
|
||||
const IndexingOptions &Opts) {
|
||||
|
|
Loading…
Reference in New Issue