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 @@ -11,9 +11,17 @@ #include "Logger.h" #include "index/Relation.h" #include "index/SymbolOrigin.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Index/IndexingAction.h" #include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" +#include +#include #include namespace clang { @@ -113,6 +121,40 @@ IncludeGraph &IG; }; +/// Returns an ASTConsumer that wraps \p Inner and additionally instructs the +/// parser to skip bodies of functions in the files that should not be +/// processed. +static std::unique_ptr +skipProcessedFunctions(std::unique_ptr Inner, + std::function ShouldProcessFile) { + class SkipProcessedFunctions : public ASTConsumer { + public: + SkipProcessedFunctions(std::function FileFilter) + : ShouldProcessFile(std::move(FileFilter)), Context(nullptr) { + assert(this->ShouldProcessFile); + } + + void Initialize(ASTContext &Context) override { this->Context = &Context; } + bool shouldSkipFunctionBody(Decl *D) override { + assert(Context && "Initialize() was never called."); + auto &SM = Context->getSourceManager(); + auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation())); + if (!FID.isValid()) + return false; + return !ShouldProcessFile(FID); + } + + private: + std::function ShouldProcessFile; + const ASTContext *Context; + }; + std::vector> Consumers; + Consumers.push_back( + llvm::make_unique(ShouldProcessFile)); + Consumers.push_back(std::move(Inner)); + return llvm::make_unique(std::move(Consumers)); +} + // Wraps the index action and reports index data after each translation unit. class IndexAction : public WrapperFrontendAction { public: @@ -137,7 +179,9 @@ if (IncludeGraphCallback != nullptr) CI.getPreprocessor().addPPCallbacks( llvm::make_unique(CI.getSourceManager(), IG)); - return WrapperFrontendAction::CreateASTConsumer(CI, InFile); + return skipProcessedFunctions( + WrapperFrontendAction::CreateASTConsumer(CI, InFile), + [this](FileID FID) { return Collector->shouldProcessFile(FID); }); } bool BeginInvocation(CompilerInstance &CI) override { @@ -147,6 +191,10 @@ // Avoids some analyses too. Set in two places as we're late to the party. CI.getDiagnosticOpts().IgnoreWarnings = true; CI.getDiagnostics().setIgnoreAllWarnings(true); + /// Instruct the parser to ask our ASTConsumer if it should skip function + /// bodies. The ASTConsumer will take care of skipping only functions inside + /// the files that we have already processed. + CI.getFrontendOpts().SkipFunctionBodies = true; return WrapperFrontendAction::BeginInvocation(CI); } diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h --- a/clang-tools-extra/clangd/index/SymbolCollector.h +++ b/clang-tools-extra/clangd/index/SymbolCollector.h @@ -112,6 +112,11 @@ RefSlab takeRefs() { return std::move(Refs).build(); } RelationSlab takeRelations() { return std::move(Relations).build(); } + /// Returns true if we are interested in references and declarations from \p + /// FID. If this function return false, bodies of functions inside those files + /// will be skipped to decrease indexing time. + bool shouldProcessFile(FileID FID); + void finish() override; private: diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -742,5 +742,12 @@ return false; } +bool SymbolCollector::shouldProcessFile(FileID FID) { + assert(ASTCtx); + if (!Opts.FileFilter) + return true; + return Opts.FileFilter(ASTCtx->getSourceManager(), FID); +} + } // namespace clangd } // namespace clang