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