You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
16 KiB
477 lines
16 KiB
//===- IndexingContext.cpp - Indexing context data ------------------------===//
|
|
//
|
|
// 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 "IndexingContext.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Index/IndexDataConsumer.h"
|
|
|
|
using namespace clang;
|
|
using namespace index;
|
|
|
|
static bool isGeneratedDecl(const Decl *D) {
|
|
if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) {
|
|
return attr->getGeneratedDeclaration();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IndexingContext::shouldIndex(const Decl *D) {
|
|
return !isGeneratedDecl(D);
|
|
}
|
|
|
|
const LangOptions &IndexingContext::getLangOpts() const {
|
|
return Ctx->getLangOpts();
|
|
}
|
|
|
|
bool IndexingContext::shouldIndexFunctionLocalSymbols() const {
|
|
return IndexOpts.IndexFunctionLocals;
|
|
}
|
|
|
|
bool IndexingContext::shouldIndexImplicitInstantiation() const {
|
|
return IndexOpts.IndexImplicitInstantiation;
|
|
}
|
|
|
|
bool IndexingContext::shouldIndexParametersInDeclarations() const {
|
|
return IndexOpts.IndexParametersInDeclarations;
|
|
}
|
|
|
|
bool IndexingContext::shouldIndexTemplateParameters() const {
|
|
return IndexOpts.IndexTemplateParameters;
|
|
}
|
|
|
|
bool IndexingContext::handleDecl(const Decl *D,
|
|
SymbolRoleSet Roles,
|
|
ArrayRef<SymbolRelation> Relations) {
|
|
return handleDecl(D, D->getLocation(), Roles, Relations);
|
|
}
|
|
|
|
bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc,
|
|
SymbolRoleSet Roles,
|
|
ArrayRef<SymbolRelation> Relations,
|
|
const DeclContext *DC) {
|
|
if (!DC)
|
|
DC = D->getDeclContext();
|
|
|
|
const Decl *OrigD = D;
|
|
if (isa<ObjCPropertyImplDecl>(D)) {
|
|
D = cast<ObjCPropertyImplDecl>(D)->getPropertyDecl();
|
|
}
|
|
return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast<Decl>(DC),
|
|
Roles, Relations,
|
|
nullptr, OrigD, DC);
|
|
}
|
|
|
|
bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc,
|
|
const NamedDecl *Parent,
|
|
const DeclContext *DC,
|
|
SymbolRoleSet Roles,
|
|
ArrayRef<SymbolRelation> Relations,
|
|
const Expr *RefE,
|
|
const Decl *RefD) {
|
|
if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D))
|
|
return true;
|
|
|
|
if (!shouldIndexTemplateParameters() &&
|
|
(isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
|
|
isa<TemplateTemplateParmDecl>(D))) {
|
|
return true;
|
|
}
|
|
|
|
return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations,
|
|
RefE, RefD, DC);
|
|
}
|
|
|
|
static void reportModuleReferences(const Module *Mod,
|
|
ArrayRef<SourceLocation> IdLocs,
|
|
const ImportDecl *ImportD,
|
|
IndexDataConsumer &DataConsumer) {
|
|
if (!Mod)
|
|
return;
|
|
reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,
|
|
DataConsumer);
|
|
DataConsumer.handleModuleOccurrence(
|
|
ImportD, Mod, (SymbolRoleSet)SymbolRole::Reference, IdLocs.back());
|
|
}
|
|
|
|
bool IndexingContext::importedModule(const ImportDecl *ImportD) {
|
|
if (ImportD->isInvalidDecl())
|
|
return true;
|
|
|
|
SourceLocation Loc;
|
|
auto IdLocs = ImportD->getIdentifierLocs();
|
|
if (!IdLocs.empty())
|
|
Loc = IdLocs.back();
|
|
else
|
|
Loc = ImportD->getLocation();
|
|
|
|
SourceManager &SM = Ctx->getSourceManager();
|
|
FileID FID = SM.getFileID(SM.getFileLoc(Loc));
|
|
if (FID.isInvalid())
|
|
return true;
|
|
|
|
bool Invalid = false;
|
|
const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
|
|
if (Invalid || !SEntry.isFile())
|
|
return true;
|
|
|
|
if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
|
|
switch (IndexOpts.SystemSymbolFilter) {
|
|
case IndexingOptions::SystemSymbolFilterKind::None:
|
|
return true;
|
|
case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
|
|
case IndexingOptions::SystemSymbolFilterKind::All:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const Module *Mod = ImportD->getImportedModule();
|
|
if (!ImportD->isImplicit() && Mod->Parent && !IdLocs.empty()) {
|
|
reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,
|
|
DataConsumer);
|
|
}
|
|
|
|
SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration;
|
|
if (ImportD->isImplicit())
|
|
Roles |= (unsigned)SymbolRole::Implicit;
|
|
|
|
return DataConsumer.handleModuleOccurrence(ImportD, Mod, Roles, Loc);
|
|
}
|
|
|
|
bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) {
|
|
TemplateSpecializationKind TKind = TSK_Undeclared;
|
|
if (const ClassTemplateSpecializationDecl *
|
|
SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
TKind = SD->getSpecializationKind();
|
|
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
TKind = FD->getTemplateSpecializationKind();
|
|
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
TKind = VD->getTemplateSpecializationKind();
|
|
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
|
|
if (RD->getInstantiatedFromMemberClass())
|
|
TKind = RD->getTemplateSpecializationKind();
|
|
} else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
|
|
if (ED->getInstantiatedFromMemberEnum())
|
|
TKind = ED->getTemplateSpecializationKind();
|
|
} else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) ||
|
|
isa<EnumConstantDecl>(D)) {
|
|
if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext()))
|
|
return isTemplateImplicitInstantiation(Parent);
|
|
}
|
|
switch (TKind) {
|
|
case TSK_Undeclared:
|
|
// Instantiation maybe not happen yet when we see a SpecializationDecl,
|
|
// e.g. when the type doesn't need to be complete, we still treat it as an
|
|
// instantiation as we'd like to keep the canonicalized result consistent.
|
|
return isa<ClassTemplateSpecializationDecl>(D);
|
|
case TSK_ExplicitSpecialization:
|
|
return false;
|
|
case TSK_ImplicitInstantiation:
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
return true;
|
|
}
|
|
llvm_unreachable("invalid TemplateSpecializationKind");
|
|
}
|
|
|
|
bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) {
|
|
if (isa<ObjCInterfaceDecl>(D))
|
|
return false;
|
|
if (isa<ObjCCategoryDecl>(D))
|
|
return false;
|
|
if (isa<ObjCIvarDecl>(D))
|
|
return false;
|
|
if (isa<ObjCMethodDecl>(D))
|
|
return false;
|
|
if (isa<ImportDecl>(D))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static const CXXRecordDecl *
|
|
getDeclContextForTemplateInstationPattern(const Decl *D) {
|
|
if (const auto *CTSD =
|
|
dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext()))
|
|
return CTSD->getTemplateInstantiationPattern();
|
|
else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext()))
|
|
return RD->getInstantiatedFromMemberClass();
|
|
return nullptr;
|
|
}
|
|
|
|
static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) {
|
|
if (const ClassTemplateSpecializationDecl *
|
|
SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
const auto *Template = SD->getTemplateInstantiationPattern();
|
|
if (Template)
|
|
return Template;
|
|
// Fallback to primary template if no instantiation is available yet (e.g.
|
|
// the type doesn't need to be complete).
|
|
return SD->getSpecializedTemplate()->getTemplatedDecl();
|
|
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
return FD->getTemplateInstantiationPattern();
|
|
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
return VD->getTemplateInstantiationPattern();
|
|
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
|
|
return RD->getInstantiatedFromMemberClass();
|
|
} else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
|
|
return ED->getInstantiatedFromMemberEnum();
|
|
} else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
|
|
const auto *ND = cast<NamedDecl>(D);
|
|
if (const CXXRecordDecl *Pattern =
|
|
getDeclContextForTemplateInstationPattern(ND)) {
|
|
for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) {
|
|
if (BaseND->isImplicit())
|
|
continue;
|
|
if (BaseND->getKind() == ND->getKind())
|
|
return BaseND;
|
|
}
|
|
}
|
|
} else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
|
|
if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
|
|
if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
|
|
for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
|
|
return BaseECD;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) {
|
|
if (auto VD = dyn_cast<VarDecl>(D))
|
|
return VD->isThisDeclarationADefinition(Ctx);
|
|
|
|
if (auto FD = dyn_cast<FunctionDecl>(D))
|
|
return FD->isThisDeclarationADefinition();
|
|
|
|
if (auto TD = dyn_cast<TagDecl>(D))
|
|
return TD->isThisDeclarationADefinition();
|
|
|
|
if (auto MD = dyn_cast<ObjCMethodDecl>(D))
|
|
return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(ContainerDC);
|
|
|
|
if (isa<TypedefNameDecl>(D) ||
|
|
isa<EnumConstantDecl>(D) ||
|
|
isa<FieldDecl>(D) ||
|
|
isa<MSPropertyDecl>(D) ||
|
|
isa<ObjCImplDecl>(D) ||
|
|
isa<ObjCPropertyImplDecl>(D))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Whether the given NamedDecl should be skipped because it has no name.
|
|
static bool shouldSkipNamelessDecl(const NamedDecl *ND) {
|
|
return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&
|
|
!isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND);
|
|
}
|
|
|
|
static const Decl *adjustParent(const Decl *Parent) {
|
|
if (!Parent)
|
|
return nullptr;
|
|
for (;; Parent = cast<Decl>(Parent->getDeclContext())) {
|
|
if (isa<TranslationUnitDecl>(Parent))
|
|
return nullptr;
|
|
if (isa<LinkageSpecDecl>(Parent) || isa<BlockDecl>(Parent))
|
|
continue;
|
|
if (auto NS = dyn_cast<NamespaceDecl>(Parent)) {
|
|
if (NS->isAnonymousNamespace())
|
|
continue;
|
|
} else if (auto RD = dyn_cast<RecordDecl>(Parent)) {
|
|
if (RD->isAnonymousStructOrUnion())
|
|
continue;
|
|
} else if (auto ND = dyn_cast<NamedDecl>(Parent)) {
|
|
if (shouldSkipNamelessDecl(ND))
|
|
continue;
|
|
}
|
|
return Parent;
|
|
}
|
|
}
|
|
|
|
static const Decl *getCanonicalDecl(const Decl *D) {
|
|
D = D->getCanonicalDecl();
|
|
if (auto TD = dyn_cast<TemplateDecl>(D)) {
|
|
if (auto TTD = TD->getTemplatedDecl()) {
|
|
D = TTD;
|
|
assert(D->isCanonicalDecl());
|
|
}
|
|
}
|
|
|
|
return D;
|
|
}
|
|
|
|
static bool shouldReportOccurrenceForSystemDeclOnlyMode(
|
|
bool IsRef, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations) {
|
|
if (!IsRef)
|
|
return true;
|
|
|
|
auto acceptForRelation = [](SymbolRoleSet roles) -> bool {
|
|
bool accept = false;
|
|
applyForEachSymbolRoleInterruptible(roles, [&accept](SymbolRole r) -> bool {
|
|
switch (r) {
|
|
case SymbolRole::RelationChildOf:
|
|
case SymbolRole::RelationBaseOf:
|
|
case SymbolRole::RelationOverrideOf:
|
|
case SymbolRole::RelationExtendedBy:
|
|
case SymbolRole::RelationAccessorOf:
|
|
case SymbolRole::RelationIBTypeOf:
|
|
accept = true;
|
|
return false;
|
|
case SymbolRole::Declaration:
|
|
case SymbolRole::Definition:
|
|
case SymbolRole::Reference:
|
|
case SymbolRole::Read:
|
|
case SymbolRole::Write:
|
|
case SymbolRole::Call:
|
|
case SymbolRole::Dynamic:
|
|
case SymbolRole::AddressOf:
|
|
case SymbolRole::Implicit:
|
|
case SymbolRole::Undefinition:
|
|
case SymbolRole::RelationReceivedBy:
|
|
case SymbolRole::RelationCalledBy:
|
|
case SymbolRole::RelationContainedBy:
|
|
case SymbolRole::RelationSpecializationOf:
|
|
case SymbolRole::NameReference:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unsupported SymbolRole value!");
|
|
});
|
|
return accept;
|
|
};
|
|
|
|
for (auto &Rel : Relations) {
|
|
if (acceptForRelation(Rel.Roles))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc,
|
|
bool IsRef, const Decl *Parent,
|
|
SymbolRoleSet Roles,
|
|
ArrayRef<SymbolRelation> Relations,
|
|
const Expr *OrigE,
|
|
const Decl *OrigD,
|
|
const DeclContext *ContainerDC) {
|
|
if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
|
|
return true;
|
|
if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D)))
|
|
return true;
|
|
|
|
SourceManager &SM = Ctx->getSourceManager();
|
|
FileID FID = SM.getFileID(SM.getFileLoc(Loc));
|
|
if (FID.isInvalid())
|
|
return true;
|
|
|
|
bool Invalid = false;
|
|
const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
|
|
if (Invalid || !SEntry.isFile())
|
|
return true;
|
|
|
|
if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
|
|
switch (IndexOpts.SystemSymbolFilter) {
|
|
case IndexingOptions::SystemSymbolFilterKind::None:
|
|
return true;
|
|
case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
|
|
if (!shouldReportOccurrenceForSystemDeclOnlyMode(IsRef, Roles, Relations))
|
|
return true;
|
|
break;
|
|
case IndexingOptions::SystemSymbolFilterKind::All:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!OrigD)
|
|
OrigD = D;
|
|
|
|
if (isTemplateImplicitInstantiation(D)) {
|
|
if (!IsRef)
|
|
return true;
|
|
D = adjustTemplateImplicitInstantiation(D);
|
|
if (!D)
|
|
return true;
|
|
assert(!isTemplateImplicitInstantiation(D));
|
|
}
|
|
|
|
if (IsRef)
|
|
Roles |= (unsigned)SymbolRole::Reference;
|
|
else if (isDeclADefinition(OrigD, ContainerDC, *Ctx))
|
|
Roles |= (unsigned)SymbolRole::Definition;
|
|
else
|
|
Roles |= (unsigned)SymbolRole::Declaration;
|
|
|
|
D = getCanonicalDecl(D);
|
|
Parent = adjustParent(Parent);
|
|
if (Parent)
|
|
Parent = getCanonicalDecl(Parent);
|
|
|
|
SmallVector<SymbolRelation, 6> FinalRelations;
|
|
FinalRelations.reserve(Relations.size()+1);
|
|
|
|
auto addRelation = [&](SymbolRelation Rel) {
|
|
auto It = llvm::find_if(FinalRelations, [&](SymbolRelation Elem) -> bool {
|
|
return Elem.RelatedSymbol == Rel.RelatedSymbol;
|
|
});
|
|
if (It != FinalRelations.end()) {
|
|
It->Roles |= Rel.Roles;
|
|
} else {
|
|
FinalRelations.push_back(Rel);
|
|
}
|
|
Roles |= Rel.Roles;
|
|
};
|
|
|
|
if (Parent) {
|
|
if (IsRef || (!isa<ParmVarDecl>(D) && isFunctionLocalSymbol(D))) {
|
|
addRelation(SymbolRelation{
|
|
(unsigned)SymbolRole::RelationContainedBy,
|
|
Parent
|
|
});
|
|
} else {
|
|
addRelation(SymbolRelation{
|
|
(unsigned)SymbolRole::RelationChildOf,
|
|
Parent
|
|
});
|
|
}
|
|
}
|
|
|
|
for (auto &Rel : Relations) {
|
|
addRelation(SymbolRelation(Rel.Roles,
|
|
Rel.RelatedSymbol->getCanonicalDecl()));
|
|
}
|
|
|
|
IndexDataConsumer::ASTNodeInfo Node{OrigE, OrigD, Parent, ContainerDC};
|
|
return DataConsumer.handleDeclOccurrence(D, Roles, FinalRelations, Loc, Node);
|
|
}
|
|
|
|
void IndexingContext::handleMacroDefined(const IdentifierInfo &Name,
|
|
SourceLocation Loc,
|
|
const MacroInfo &MI) {
|
|
SymbolRoleSet Roles = (unsigned)SymbolRole::Definition;
|
|
DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
|
|
}
|
|
|
|
void IndexingContext::handleMacroUndefined(const IdentifierInfo &Name,
|
|
SourceLocation Loc,
|
|
const MacroInfo &MI) {
|
|
SymbolRoleSet Roles = (unsigned)SymbolRole::Undefinition;
|
|
DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
|
|
}
|
|
|
|
void IndexingContext::handleMacroReference(const IdentifierInfo &Name,
|
|
SourceLocation Loc,
|
|
const MacroInfo &MI) {
|
|
SymbolRoleSet Roles = (unsigned)SymbolRole::Reference;
|
|
DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
|
|
}
|