519 lines · cpp
1//===- IndexingContext.cpp - Indexing context data ------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "IndexingContext.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Attr.h"12#include "clang/AST/DeclObjC.h"13#include "clang/AST/DeclTemplate.h"14#include "clang/Basic/SourceLocation.h"15#include "clang/Basic/SourceManager.h"16#include "clang/Index/IndexDataConsumer.h"17#include "clang/Sema/HeuristicResolver.h"18 19using namespace clang;20using namespace index;21 22static bool isGeneratedDecl(const Decl *D) {23 if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) {24 return attr->getGeneratedDeclaration();25 }26 return false;27}28 29IndexingContext::IndexingContext(IndexingOptions IndexOpts,30 IndexDataConsumer &DataConsumer)31 : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}32 33IndexingContext::~IndexingContext() = default;34 35void IndexingContext::setASTContext(ASTContext &ctx) {36 Ctx = &ctx;37 Resolver = Ctx ? std::make_unique<HeuristicResolver>(*Ctx) : nullptr;38}39 40bool IndexingContext::shouldIndex(const Decl *D) {41 return !isGeneratedDecl(D);42}43 44const LangOptions &IndexingContext::getLangOpts() const {45 return Ctx->getLangOpts();46}47 48bool IndexingContext::shouldIndexFunctionLocalSymbols() const {49 return IndexOpts.IndexFunctionLocals;50}51 52bool IndexingContext::shouldIndexImplicitInstantiation() const {53 return IndexOpts.IndexImplicitInstantiation;54}55 56bool IndexingContext::shouldIndexParametersInDeclarations() const {57 return IndexOpts.IndexParametersInDeclarations;58}59 60bool IndexingContext::shouldIndexTemplateParameters() const {61 return IndexOpts.IndexTemplateParameters;62}63 64bool IndexingContext::handleDecl(const Decl *D,65 SymbolRoleSet Roles,66 ArrayRef<SymbolRelation> Relations) {67 return handleDecl(D, D->getLocation(), Roles, Relations);68}69 70bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc,71 SymbolRoleSet Roles,72 ArrayRef<SymbolRelation> Relations,73 const DeclContext *DC) {74 if (!DC)75 DC = D->getDeclContext();76 77 const Decl *OrigD = D;78 if (isa<ObjCPropertyImplDecl>(D)) {79 D = cast<ObjCPropertyImplDecl>(D)->getPropertyDecl();80 }81 return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast<Decl>(DC),82 Roles, Relations,83 nullptr, OrigD, DC);84}85 86bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc,87 const NamedDecl *Parent,88 const DeclContext *DC,89 SymbolRoleSet Roles,90 ArrayRef<SymbolRelation> Relations,91 const Expr *RefE) {92 if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D))93 return true;94 95 if (!shouldIndexTemplateParameters() &&96 (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||97 isa<TemplateTemplateParmDecl>(D))) {98 return true;99 }100 return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations,101 RefE, nullptr, DC);102}103 104static void reportModuleReferences(const Module *Mod,105 ArrayRef<SourceLocation> IdLocs,106 const ImportDecl *ImportD,107 IndexDataConsumer &DataConsumer) {108 if (!Mod)109 return;110 reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,111 DataConsumer);112 DataConsumer.handleModuleOccurrence(113 ImportD, Mod, (SymbolRoleSet)SymbolRole::Reference, IdLocs.back());114}115 116bool IndexingContext::importedModule(const ImportDecl *ImportD) {117 if (ImportD->isInvalidDecl())118 return true;119 120 SourceLocation Loc;121 auto IdLocs = ImportD->getIdentifierLocs();122 if (!IdLocs.empty())123 Loc = IdLocs.back();124 else125 Loc = ImportD->getLocation();126 127 SourceManager &SM = Ctx->getSourceManager();128 FileID FID = SM.getFileID(SM.getFileLoc(Loc));129 if (FID.isInvalid())130 return true;131 132 bool Invalid = false;133 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);134 if (Invalid || !SEntry.isFile())135 return true;136 137 if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {138 switch (IndexOpts.SystemSymbolFilter) {139 case IndexingOptions::SystemSymbolFilterKind::None:140 return true;141 case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:142 case IndexingOptions::SystemSymbolFilterKind::All:143 break;144 }145 }146 147 const Module *Mod = ImportD->getImportedModule();148 if (!ImportD->isImplicit() && Mod->Parent && !IdLocs.empty()) {149 reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,150 DataConsumer);151 }152 153 SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration;154 if (ImportD->isImplicit())155 Roles |= (unsigned)SymbolRole::Implicit;156 157 return DataConsumer.handleModuleOccurrence(ImportD, Mod, Roles, Loc);158}159 160bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) {161 TemplateSpecializationKind TKind = TSK_Undeclared;162 if (const ClassTemplateSpecializationDecl *163 SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {164 TKind = SD->getSpecializationKind();165 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {166 TKind = FD->getTemplateSpecializationKind();167 } else if (auto *VD = dyn_cast<VarDecl>(D)) {168 TKind = VD->getTemplateSpecializationKind();169 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {170 if (RD->getInstantiatedFromMemberClass())171 TKind = RD->getTemplateSpecializationKind();172 } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {173 if (ED->getInstantiatedFromMemberEnum())174 TKind = ED->getTemplateSpecializationKind();175 } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) ||176 isa<EnumConstantDecl>(D)) {177 if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext()))178 return isTemplateImplicitInstantiation(Parent);179 }180 switch (TKind) {181 case TSK_Undeclared:182 // Instantiation maybe not happen yet when we see a SpecializationDecl,183 // e.g. when the type doesn't need to be complete, we still treat it as an184 // instantiation as we'd like to keep the canonicalized result consistent.185 return isa<ClassTemplateSpecializationDecl>(D);186 case TSK_ExplicitSpecialization:187 return false;188 case TSK_ImplicitInstantiation:189 case TSK_ExplicitInstantiationDeclaration:190 case TSK_ExplicitInstantiationDefinition:191 return true;192 }193 llvm_unreachable("invalid TemplateSpecializationKind");194}195 196bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) {197 if (isa<ObjCInterfaceDecl>(D))198 return false;199 if (isa<ObjCCategoryDecl>(D))200 return false;201 if (isa<ObjCIvarDecl>(D))202 return false;203 if (isa<ObjCMethodDecl>(D))204 return false;205 if (isa<ImportDecl>(D))206 return false;207 return true;208}209 210static const CXXRecordDecl *211getDeclContextForTemplateInstationPattern(const Decl *D) {212 if (const auto *CTSD =213 dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext()))214 return CTSD->getTemplateInstantiationPattern();215 else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext()))216 return RD->getInstantiatedFromMemberClass();217 return nullptr;218}219 220static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) {221 if (const ClassTemplateSpecializationDecl *222 SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {223 const auto *Template = SD->getTemplateInstantiationPattern();224 if (Template)225 return Template;226 // Fallback to primary template if no instantiation is available yet (e.g.227 // the type doesn't need to be complete).228 return SD->getSpecializedTemplate()->getTemplatedDecl();229 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {230 return FD->getTemplateInstantiationPattern();231 } else if (auto *VD = dyn_cast<VarDecl>(D)) {232 return VD->getTemplateInstantiationPattern();233 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {234 return RD->getInstantiatedFromMemberClass();235 } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {236 return ED->getInstantiatedFromMemberEnum();237 } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {238 const auto *ND = cast<NamedDecl>(D);239 if (const CXXRecordDecl *Pattern =240 getDeclContextForTemplateInstationPattern(ND)) {241 for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) {242 if (BaseND->isImplicit())243 continue;244 if (BaseND->getKind() == ND->getKind())245 return BaseND;246 }247 }248 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {249 if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {250 if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {251 for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))252 return BaseECD;253 }254 }255 }256 return nullptr;257}258 259static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) {260 if (auto VD = dyn_cast<VarDecl>(D))261 return VD->isThisDeclarationADefinition(Ctx);262 263 if (auto FD = dyn_cast<FunctionDecl>(D))264 return FD->isThisDeclarationADefinition();265 266 if (auto TD = dyn_cast<TagDecl>(D))267 return TD->isThisDeclarationADefinition();268 269 if (auto MD = dyn_cast<ObjCMethodDecl>(D))270 return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(ContainerDC);271 272 if (isa<TypedefNameDecl>(D) || isa<EnumConstantDecl>(D) ||273 isa<FieldDecl>(D) || isa<MSPropertyDecl>(D) || isa<ObjCImplDecl>(D) ||274 isa<ObjCPropertyImplDecl>(D) || isa<ConceptDecl>(D))275 return true;276 277 return false;278}279 280/// Whether the given NamedDecl should be skipped because it has no name.281static bool shouldSkipNamelessDecl(const NamedDecl *ND) {282 return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&283 !isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND);284}285 286static const Decl *adjustParent(const Decl *Parent) {287 if (!Parent)288 return nullptr;289 for (;; Parent = cast<Decl>(Parent->getDeclContext())) {290 if (isa<TranslationUnitDecl>(Parent))291 return nullptr;292 if (isa<LinkageSpecDecl>(Parent) || isa<BlockDecl>(Parent))293 continue;294 if (auto NS = dyn_cast<NamespaceDecl>(Parent)) {295 if (NS->isAnonymousNamespace())296 continue;297 } else if (auto RD = dyn_cast<RecordDecl>(Parent)) {298 if (RD->isAnonymousStructOrUnion())299 continue;300 } else if (auto ND = dyn_cast<NamedDecl>(Parent)) {301 if (shouldSkipNamelessDecl(ND))302 continue;303 }304 return Parent;305 }306}307 308static const Decl *getCanonicalDecl(const Decl *D) {309 D = D->getCanonicalDecl();310 if (auto TD = dyn_cast<TemplateDecl>(D)) {311 if (auto TTD = TD->getTemplatedDecl()) {312 D = TTD;313 assert(D->isCanonicalDecl());314 }315 }316 317 return D;318}319 320static bool shouldReportOccurrenceForSystemDeclOnlyMode(321 bool IsRef, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations) {322 if (!IsRef)323 return true;324 325 auto acceptForRelation = [](SymbolRoleSet roles) -> bool {326 bool accept = false;327 applyForEachSymbolRoleInterruptible(roles, [&accept](SymbolRole r) -> bool {328 switch (r) {329 case SymbolRole::RelationChildOf:330 case SymbolRole::RelationBaseOf:331 case SymbolRole::RelationOverrideOf:332 case SymbolRole::RelationExtendedBy:333 case SymbolRole::RelationAccessorOf:334 case SymbolRole::RelationIBTypeOf:335 accept = true;336 return false;337 case SymbolRole::Declaration:338 case SymbolRole::Definition:339 case SymbolRole::Reference:340 case SymbolRole::Read:341 case SymbolRole::Write:342 case SymbolRole::Call:343 case SymbolRole::Dynamic:344 case SymbolRole::AddressOf:345 case SymbolRole::Implicit:346 case SymbolRole::Undefinition:347 case SymbolRole::RelationReceivedBy:348 case SymbolRole::RelationCalledBy:349 case SymbolRole::RelationContainedBy:350 case SymbolRole::RelationSpecializationOf:351 case SymbolRole::NameReference:352 return true;353 }354 llvm_unreachable("Unsupported SymbolRole value!");355 });356 return accept;357 };358 359 for (auto &Rel : Relations) {360 if (acceptForRelation(Rel.Roles))361 return true;362 }363 364 return false;365}366 367bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc,368 bool IsRef, const Decl *Parent,369 SymbolRoleSet Roles,370 ArrayRef<SymbolRelation> Relations,371 const Expr *OrigE,372 const Decl *OrigD,373 const DeclContext *ContainerDC) {374 if (D->isImplicit() && !isa<ObjCMethodDecl>(D))375 return true;376 if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D)))377 return true;378 379 SourceManager &SM = Ctx->getSourceManager();380 FileID FID = SM.getFileID(SM.getFileLoc(Loc));381 if (FID.isInvalid())382 return true;383 384 bool Invalid = false;385 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);386 if (Invalid || !SEntry.isFile())387 return true;388 389 if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {390 switch (IndexOpts.SystemSymbolFilter) {391 case IndexingOptions::SystemSymbolFilterKind::None:392 return true;393 case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:394 if (!shouldReportOccurrenceForSystemDeclOnlyMode(IsRef, Roles, Relations))395 return true;396 break;397 case IndexingOptions::SystemSymbolFilterKind::All:398 break;399 }400 }401 402 if (!OrigD)403 OrigD = D;404 405 if (isTemplateImplicitInstantiation(D)) {406 if (!IsRef)407 return true;408 D = adjustTemplateImplicitInstantiation(D);409 if (!D)410 return true;411 assert(!isTemplateImplicitInstantiation(D));412 }413 414 if (IsRef)415 Roles |= (unsigned)SymbolRole::Reference;416 else if (isDeclADefinition(OrigD, ContainerDC, *Ctx))417 Roles |= (unsigned)SymbolRole::Definition;418 else419 Roles |= (unsigned)SymbolRole::Declaration;420 421 D = getCanonicalDecl(D);422 Parent = adjustParent(Parent);423 if (Parent)424 Parent = getCanonicalDecl(Parent);425 426 SmallVector<SymbolRelation, 6> FinalRelations;427 FinalRelations.reserve(Relations.size()+1);428 429 auto addRelation = [&](SymbolRelation Rel) {430 auto It = llvm::find_if(FinalRelations, [&](SymbolRelation Elem) -> bool {431 return Elem.RelatedSymbol == Rel.RelatedSymbol;432 });433 if (It != FinalRelations.end()) {434 It->Roles |= Rel.Roles;435 } else {436 FinalRelations.push_back(Rel);437 }438 Roles |= Rel.Roles;439 };440 441 if (Parent) {442 if (IsRef || (!isa<ParmVarDecl>(D) && isFunctionLocalSymbol(D))) {443 addRelation(SymbolRelation{444 (unsigned)SymbolRole::RelationContainedBy,445 Parent446 });447 } else {448 addRelation(SymbolRelation{449 (unsigned)SymbolRole::RelationChildOf,450 Parent451 });452 }453 }454 455 for (auto &Rel : Relations) {456 addRelation(SymbolRelation(Rel.Roles,457 Rel.RelatedSymbol->getCanonicalDecl()));458 }459 460 IndexDataConsumer::ASTNodeInfo Node{OrigE, OrigD, Parent, ContainerDC};461 return DataConsumer.handleDeclOccurrence(D, Roles, FinalRelations, Loc, Node);462}463 464void IndexingContext::handleMacroDefined(const IdentifierInfo &Name,465 SourceLocation Loc,466 const MacroInfo &MI) {467 if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc))468 return;469 SymbolRoleSet Roles = (unsigned)SymbolRole::Definition;470 DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);471}472 473void IndexingContext::handleMacroUndefined(const IdentifierInfo &Name,474 SourceLocation Loc,475 const MacroInfo &MI) {476 if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc))477 return;478 SymbolRoleSet Roles = (unsigned)SymbolRole::Undefinition;479 DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);480}481 482void IndexingContext::handleMacroReference(const IdentifierInfo &Name,483 SourceLocation Loc,484 const MacroInfo &MI) {485 if (!shouldIndexMacroOccurrence(/*IsRef=*/true, Loc))486 return;487 SymbolRoleSet Roles = (unsigned)SymbolRole::Reference;488 DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);489}490 491bool IndexingContext::shouldIndexMacroOccurrence(bool IsRef,492 SourceLocation Loc) {493 if (!IndexOpts.IndexMacros)494 return false;495 496 switch (IndexOpts.SystemSymbolFilter) {497 case IndexingOptions::SystemSymbolFilterKind::None:498 break;499 case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:500 if (!IsRef)501 return true;502 break;503 case IndexingOptions::SystemSymbolFilterKind::All:504 return true;505 }506 507 SourceManager &SM = Ctx->getSourceManager();508 FileID FID = SM.getFileID(SM.getFileLoc(Loc));509 if (FID.isInvalid())510 return false;511 512 bool Invalid = false;513 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);514 if (Invalid || !SEntry.isFile())515 return false;516 517 return SEntry.getFile().getFileCharacteristic() == SrcMgr::C_User;518}519