//===- 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()) { 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 Relations) { return handleDecl(D, D->getLocation(), Roles, Relations); } bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc, SymbolRoleSet Roles, ArrayRef Relations, const DeclContext *DC) { if (!DC) DC = D->getDeclContext(); const Decl *OrigD = D; if (isa(D)) { D = cast(D)->getPropertyDecl(); } return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast(DC), Roles, Relations, nullptr, OrigD, DC); } bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, SymbolRoleSet Roles, ArrayRef Relations, const Expr *RefE) { if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D)) return true; if (!shouldIndexTemplateParameters() && (isa(D) || isa(D) || isa(D))) { return true; } return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, RefE, nullptr, DC); } static void reportModuleReferences(const Module *Mod, ArrayRef 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(D)) { TKind = SD->getSpecializationKind(); } else if (const FunctionDecl *FD = dyn_cast(D)) { TKind = FD->getTemplateSpecializationKind(); } else if (auto *VD = dyn_cast(D)) { TKind = VD->getTemplateSpecializationKind(); } else if (const auto *RD = dyn_cast(D)) { if (RD->getInstantiatedFromMemberClass()) TKind = RD->getTemplateSpecializationKind(); } else if (const auto *ED = dyn_cast(D)) { if (ED->getInstantiatedFromMemberEnum()) TKind = ED->getTemplateSpecializationKind(); } else if (isa(D) || isa(D) || isa(D)) { if (const auto *Parent = dyn_cast(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(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(D)) return false; if (isa(D)) return false; if (isa(D)) return false; if (isa(D)) return false; if (isa(D)) return false; return true; } static const CXXRecordDecl * getDeclContextForTemplateInstationPattern(const Decl *D) { if (const auto *CTSD = dyn_cast(D->getDeclContext())) return CTSD->getTemplateInstantiationPattern(); else if (const auto *RD = dyn_cast(D->getDeclContext())) return RD->getInstantiatedFromMemberClass(); return nullptr; } static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) { if (const ClassTemplateSpecializationDecl * SD = dyn_cast(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(D)) { return FD->getTemplateInstantiationPattern(); } else if (auto *VD = dyn_cast(D)) { return VD->getTemplateInstantiationPattern(); } else if (const auto *RD = dyn_cast(D)) { return RD->getInstantiatedFromMemberClass(); } else if (const auto *ED = dyn_cast(D)) { return ED->getInstantiatedFromMemberEnum(); } else if (isa(D) || isa(D)) { const auto *ND = cast(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(D)) { if (const auto *ED = dyn_cast(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(D)) return VD->isThisDeclarationADefinition(Ctx); if (auto FD = dyn_cast(D)) return FD->isThisDeclarationADefinition(); if (auto TD = dyn_cast(D)) return TD->isThisDeclarationADefinition(); if (auto MD = dyn_cast(D)) return MD->isThisDeclarationADefinition() || isa(ContainerDC); if (isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(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(ND) && !isa(ND)) || isa(ND); } static const Decl *adjustParent(const Decl *Parent) { if (!Parent) return nullptr; for (;; Parent = cast(Parent->getDeclContext())) { if (isa(Parent)) return nullptr; if (isa(Parent) || isa(Parent)) continue; if (auto NS = dyn_cast(Parent)) { if (NS->isAnonymousNamespace()) continue; } else if (auto RD = dyn_cast(Parent)) { if (RD->isAnonymousStructOrUnion()) continue; } else if (auto ND = dyn_cast(Parent)) { if (shouldSkipNamelessDecl(ND)) continue; } return Parent; } } static const Decl *getCanonicalDecl(const Decl *D) { D = D->getCanonicalDecl(); if (auto TD = dyn_cast(D)) { if (auto TTD = TD->getTemplatedDecl()) { D = TTD; assert(D->isCanonicalDecl()); } } return D; } static bool shouldReportOccurrenceForSystemDeclOnlyMode( bool IsRef, SymbolRoleSet Roles, ArrayRef 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 Relations, const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { if (D->isImplicit() && !isa(D)) return true; if (!isa(D) || shouldSkipNamelessDecl(cast(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 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(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) { if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc)) return; SymbolRoleSet Roles = (unsigned)SymbolRole::Definition; DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc); } void IndexingContext::handleMacroUndefined(const IdentifierInfo &Name, SourceLocation Loc, const MacroInfo &MI) { if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc)) return; SymbolRoleSet Roles = (unsigned)SymbolRole::Undefinition; DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc); } void IndexingContext::handleMacroReference(const IdentifierInfo &Name, SourceLocation Loc, const MacroInfo &MI) { if (!shouldIndexMacroOccurrence(/*IsRef=*/true, Loc)) return; SymbolRoleSet Roles = (unsigned)SymbolRole::Reference; DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc); } bool IndexingContext::shouldIndexMacroOccurrence(bool IsRef, SourceLocation Loc) { if (!IndexOpts.IndexMacros) return false; switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: break; case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: if (!IsRef) return true; break; case IndexingOptions::SystemSymbolFilterKind::All: return true; } SourceManager &SM = Ctx->getSourceManager(); FileID FID = SM.getFileID(SM.getFileLoc(Loc)); if (FID.isInvalid()) return false; bool Invalid = false; const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); if (Invalid || !SEntry.isFile()) return false; return SEntry.getFile().getFileCharacteristic() == SrcMgr::C_User; }