| //===- IndexingAction.cpp - Frontend index action -------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Index/IndexingAction.h" |
| #include "IndexingContext.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/FrontendAction.h" |
| #include "clang/Frontend/MultiplexConsumer.h" |
| #include "clang/Index/IndexDataConsumer.h" |
| #include "clang/Lex/PPCallbacks.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Serialization/ASTReader.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include <memory> |
| |
| using namespace clang; |
| using namespace clang::index; |
| |
| namespace { |
| |
| class IndexPPCallbacks final : public PPCallbacks { |
| std::shared_ptr<IndexingContext> IndexCtx; |
| |
| public: |
| IndexPPCallbacks(std::shared_ptr<IndexingContext> IndexCtx) |
| : IndexCtx(std::move(IndexCtx)) {} |
| |
| void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
| SourceRange Range, const MacroArgs *Args) override { |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| Range.getBegin(), *MD.getMacroInfo()); |
| } |
| |
| void MacroDefined(const Token &MacroNameTok, |
| const MacroDirective *MD) override { |
| IndexCtx->handleMacroDefined(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD->getMacroInfo()); |
| } |
| |
| void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, |
| const MacroDirective *Undef) override { |
| if (!MD.getMacroInfo()) // Ignore noop #undef. |
| return; |
| IndexCtx->handleMacroUndefined(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| |
| void Defined(const Token &MacroNameTok, const MacroDefinition &MD, |
| SourceRange Range) override { |
| if (!MD.getMacroInfo()) // Ignore nonexistent macro. |
| return; |
| // Note: this is defined(M), not #define M |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| void Ifdef(SourceLocation Loc, const Token &MacroNameTok, |
| const MacroDefinition &MD) override { |
| if (!MD.getMacroInfo()) // Ignore non-existent macro. |
| return; |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| void Ifndef(SourceLocation Loc, const Token &MacroNameTok, |
| const MacroDefinition &MD) override { |
| if (!MD.getMacroInfo()) // Ignore nonexistent macro. |
| return; |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| |
| using PPCallbacks::Elifdef; |
| using PPCallbacks::Elifndef; |
| void Elifdef(SourceLocation Loc, const Token &MacroNameTok, |
| const MacroDefinition &MD) override { |
| if (!MD.getMacroInfo()) // Ignore non-existent macro. |
| return; |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| void Elifndef(SourceLocation Loc, const Token &MacroNameTok, |
| const MacroDefinition &MD) override { |
| if (!MD.getMacroInfo()) // Ignore non-existent macro. |
| return; |
| IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), |
| MacroNameTok.getLocation(), |
| *MD.getMacroInfo()); |
| } |
| }; |
| |
| class IndexASTConsumer final : public ASTConsumer { |
| std::shared_ptr<IndexDataConsumer> DataConsumer; |
| std::shared_ptr<IndexingContext> IndexCtx; |
| std::shared_ptr<Preprocessor> PP; |
| std::function<bool(const Decl *)> ShouldSkipFunctionBody; |
| |
| public: |
| IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer, |
| const IndexingOptions &Opts, |
| std::shared_ptr<Preprocessor> PP, |
| std::function<bool(const Decl *)> ShouldSkipFunctionBody) |
| : DataConsumer(std::move(DataConsumer)), |
| IndexCtx(new IndexingContext(Opts, *this->DataConsumer)), |
| PP(std::move(PP)), |
| ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)) { |
| assert(this->DataConsumer != nullptr); |
| assert(this->PP != nullptr); |
| } |
| |
| protected: |
| void Initialize(ASTContext &Context) override { |
| IndexCtx->setASTContext(Context); |
| IndexCtx->getDataConsumer().initialize(Context); |
| IndexCtx->getDataConsumer().setPreprocessor(PP); |
| PP->addPPCallbacks(std::make_unique<IndexPPCallbacks>(IndexCtx)); |
| } |
| |
| bool HandleTopLevelDecl(DeclGroupRef DG) override { |
| return IndexCtx->indexDeclGroupRef(DG); |
| } |
| |
| void HandleInterestingDecl(DeclGroupRef DG) override { |
| // Ignore deserialized decls. |
| } |
| |
| void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { |
| IndexCtx->indexDeclGroupRef(DG); |
| } |
| |
| void HandleTranslationUnit(ASTContext &Ctx) override { |
| DataConsumer->finish(); |
| } |
| |
| bool shouldSkipFunctionBody(Decl *D) override { |
| return ShouldSkipFunctionBody(D); |
| } |
| }; |
| |
| class IndexAction final : public ASTFrontendAction { |
| std::shared_ptr<IndexDataConsumer> DataConsumer; |
| IndexingOptions Opts; |
| |
| public: |
| IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, |
| const IndexingOptions &Opts) |
| : DataConsumer(std::move(DataConsumer)), Opts(Opts) { |
| assert(this->DataConsumer != nullptr); |
| } |
| |
| protected: |
| std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, |
| StringRef InFile) override { |
| return std::make_unique<IndexASTConsumer>( |
| DataConsumer, Opts, CI.getPreprocessorPtr(), |
| /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| std::unique_ptr<ASTConsumer> index::createIndexingASTConsumer( |
| std::shared_ptr<IndexDataConsumer> DataConsumer, |
| const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, |
| std::function<bool(const Decl *)> ShouldSkipFunctionBody) { |
| return std::make_unique<IndexASTConsumer>(DataConsumer, Opts, PP, |
| 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) { |
| assert(DataConsumer != nullptr); |
| return std::make_unique<IndexAction>(std::move(DataConsumer), Opts); |
| } |
| |
| static bool topLevelDeclVisitor(void *context, const Decl *D) { |
| IndexingContext &IndexCtx = *static_cast<IndexingContext*>(context); |
| return IndexCtx.indexTopLevelDecl(D); |
| } |
| |
| static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { |
| Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); |
| } |
| |
| static void indexPreprocessorMacro(const IdentifierInfo *II, |
| const MacroInfo *MI, |
| MacroDirective::Kind DirectiveKind, |
| SourceLocation Loc, |
| IndexDataConsumer &DataConsumer) { |
| // When using modules, it may happen that we find #undef of a macro that |
| // was defined in another module. In such case, MI may be nullptr, since |
| // we only look for macro definitions in the current TU. In that case, |
| // there is nothing to index. |
| if (!MI) |
| return; |
| |
| // Skip implicit visibility change. |
| if (DirectiveKind == MacroDirective::MD_Visibility) |
| return; |
| |
| auto Role = DirectiveKind == MacroDirective::MD_Define |
| ? SymbolRole::Definition |
| : SymbolRole::Undefinition; |
| DataConsumer.handleMacroOccurrence(II, MI, static_cast<unsigned>(Role), Loc); |
| } |
| |
| static void indexPreprocessorMacros(Preprocessor &PP, |
| IndexDataConsumer &DataConsumer) { |
| for (const auto &M : PP.macros()) { |
| for (auto *MD = M.second.getLatest(); MD; MD = MD->getPrevious()) { |
| indexPreprocessorMacro(M.first, MD->getMacroInfo(), MD->getKind(), |
| MD->getLocation(), DataConsumer); |
| } |
| } |
| } |
| |
| static void indexPreprocessorModuleMacros(Preprocessor &PP, |
| serialization::ModuleFile &Mod, |
| IndexDataConsumer &DataConsumer) { |
| for (const auto &M : PP.macros()) { |
| if (M.second.getLatest() == nullptr) { |
| for (auto *MM : PP.getLeafModuleMacros(M.first)) { |
| auto *OwningMod = MM->getOwningModule(); |
| if (OwningMod && OwningMod->getASTFile() == Mod.File) { |
| if (auto *MI = MM->getMacroInfo()) { |
| indexPreprocessorMacro(M.first, MI, MacroDirective::MD_Define, |
| MI->getDefinitionLoc(), DataConsumer); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void index::indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, |
| IndexingOptions Opts) { |
| IndexingContext IndexCtx(Opts, DataConsumer); |
| IndexCtx.setASTContext(Unit.getASTContext()); |
| DataConsumer.initialize(Unit.getASTContext()); |
| DataConsumer.setPreprocessor(Unit.getPreprocessorPtr()); |
| |
| if (Opts.IndexMacrosInPreprocessor) |
| indexPreprocessorMacros(Unit.getPreprocessor(), DataConsumer); |
| indexTranslationUnit(Unit, IndexCtx); |
| DataConsumer.finish(); |
| } |
| |
| void index::indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, |
| ArrayRef<const Decl *> Decls, |
| IndexDataConsumer &DataConsumer, |
| IndexingOptions Opts) { |
| IndexingContext IndexCtx(Opts, DataConsumer); |
| IndexCtx.setASTContext(Ctx); |
| |
| DataConsumer.initialize(Ctx); |
| |
| if (Opts.IndexMacrosInPreprocessor) |
| indexPreprocessorMacros(PP, DataConsumer); |
| |
| for (const Decl *D : Decls) |
| IndexCtx.indexTopLevelDecl(D); |
| DataConsumer.finish(); |
| } |
| |
| std::unique_ptr<PPCallbacks> |
| index::indexMacrosCallback(IndexDataConsumer &Consumer, IndexingOptions Opts) { |
| return std::make_unique<IndexPPCallbacks>( |
| std::make_shared<IndexingContext>(Opts, Consumer)); |
| } |
| |
| void index::indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, |
| IndexDataConsumer &DataConsumer, |
| IndexingOptions Opts) { |
| ASTContext &Ctx = Reader.getContext(); |
| IndexingContext IndexCtx(Opts, DataConsumer); |
| IndexCtx.setASTContext(Ctx); |
| DataConsumer.initialize(Ctx); |
| |
| if (Opts.IndexMacrosInPreprocessor) { |
| indexPreprocessorModuleMacros(Reader.getPreprocessor(), Mod, DataConsumer); |
| } |
| |
| for (const Decl *D : Reader.getModuleFileLevelDecls(Mod)) { |
| IndexCtx.indexTopLevelDecl(D); |
| } |
| DataConsumer.finish(); |
| } |