xref: /freebsd/contrib/llvm-project/clang/lib/Index/IndexingContext.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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-exception
6 //
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 
19 using namespace clang;
20 using namespace index;
21 
isGeneratedDecl(const Decl * D)22 static bool isGeneratedDecl(const Decl *D) {
23   if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) {
24     return attr->getGeneratedDeclaration();
25   }
26   return false;
27 }
28 
IndexingContext(IndexingOptions IndexOpts,IndexDataConsumer & DataConsumer)29 IndexingContext::IndexingContext(IndexingOptions IndexOpts,
30                                  IndexDataConsumer &DataConsumer)
31     : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}
32 
33 IndexingContext::~IndexingContext() = default;
34 
setASTContext(ASTContext & ctx)35 void IndexingContext::setASTContext(ASTContext &ctx) {
36   Ctx = &ctx;
37   Resolver = Ctx ? std::make_unique<HeuristicResolver>(*Ctx) : nullptr;
38 }
39 
shouldIndex(const Decl * D)40 bool IndexingContext::shouldIndex(const Decl *D) {
41   return !isGeneratedDecl(D);
42 }
43 
getLangOpts() const44 const LangOptions &IndexingContext::getLangOpts() const {
45   return Ctx->getLangOpts();
46 }
47 
shouldIndexFunctionLocalSymbols() const48 bool IndexingContext::shouldIndexFunctionLocalSymbols() const {
49   return IndexOpts.IndexFunctionLocals;
50 }
51 
shouldIndexImplicitInstantiation() const52 bool IndexingContext::shouldIndexImplicitInstantiation() const {
53   return IndexOpts.IndexImplicitInstantiation;
54 }
55 
shouldIndexParametersInDeclarations() const56 bool IndexingContext::shouldIndexParametersInDeclarations() const {
57   return IndexOpts.IndexParametersInDeclarations;
58 }
59 
shouldIndexTemplateParameters() const60 bool IndexingContext::shouldIndexTemplateParameters() const {
61   return IndexOpts.IndexTemplateParameters;
62 }
63 
handleDecl(const Decl * D,SymbolRoleSet Roles,ArrayRef<SymbolRelation> Relations)64 bool IndexingContext::handleDecl(const Decl *D,
65                                  SymbolRoleSet Roles,
66                                  ArrayRef<SymbolRelation> Relations) {
67   return handleDecl(D, D->getLocation(), Roles, Relations);
68 }
69 
handleDecl(const Decl * D,SourceLocation Loc,SymbolRoleSet Roles,ArrayRef<SymbolRelation> Relations,const DeclContext * DC)70 bool 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 
handleReference(const NamedDecl * D,SourceLocation Loc,const NamedDecl * Parent,const DeclContext * DC,SymbolRoleSet Roles,ArrayRef<SymbolRelation> Relations,const Expr * RefE)86 bool 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 
reportModuleReferences(const Module * Mod,ArrayRef<SourceLocation> IdLocs,const ImportDecl * ImportD,IndexDataConsumer & DataConsumer)104 static 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 
importedModule(const ImportDecl * ImportD)116 bool 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   else
125     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 
isTemplateImplicitInstantiation(const Decl * D)160 bool 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 an
184       // 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 
shouldIgnoreIfImplicit(const Decl * D)196 bool 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 
210 static const CXXRecordDecl *
getDeclContextForTemplateInstationPattern(const Decl * D)211 getDeclContextForTemplateInstationPattern(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 
adjustTemplateImplicitInstantiation(const Decl * D)220 static 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 
isDeclADefinition(const Decl * D,const DeclContext * ContainerDC,ASTContext & Ctx)259 static 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.
shouldSkipNamelessDecl(const NamedDecl * ND)281 static bool shouldSkipNamelessDecl(const NamedDecl *ND) {
282   return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&
283           !isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND);
284 }
285 
adjustParent(const Decl * Parent)286 static 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 
getCanonicalDecl(const Decl * D)308 static 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 
shouldReportOccurrenceForSystemDeclOnlyMode(bool IsRef,SymbolRoleSet Roles,ArrayRef<SymbolRelation> Relations)320 static 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 
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)367 bool 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   else
419     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         Parent
446       });
447     } else {
448       addRelation(SymbolRelation{
449         (unsigned)SymbolRole::RelationChildOf,
450         Parent
451       });
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 
handleMacroDefined(const IdentifierInfo & Name,SourceLocation Loc,const MacroInfo & MI)464 void 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 
handleMacroUndefined(const IdentifierInfo & Name,SourceLocation Loc,const MacroInfo & MI)473 void 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 
handleMacroReference(const IdentifierInfo & Name,SourceLocation Loc,const MacroInfo & MI)482 void 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 
shouldIndexMacroOccurrence(bool IsRef,SourceLocation Loc)491 bool 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