forked from OSchip/llvm-project
294 lines
11 KiB
C++
294 lines
11 KiB
C++
//===- 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());
|
|
}
|
|
};
|
|
|
|
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();
|
|
}
|