//===- 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);
}