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
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>(args&: *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, Loc: 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>(Val: D)) {
79 D = cast<ObjCPropertyImplDecl>(Val: D)->getPropertyDecl();
80 }
81 return handleDeclOccurrence(D, Loc, /*IsRef=*/false, Parent: cast<Decl>(Val: DC),
82 Roles, Relations,
83 RefE: nullptr, RefD: OrigD, ContainerDC: 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>(Val: D) || isa<TemplateTypeParmDecl>(Val: D) ||
97 isa<TemplateTemplateParmDecl>(Val: 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: Mod->Parent, IdLocs: IdLocs.drop_back(), ImportD,
111 DataConsumer);
112 DataConsumer.handleModuleOccurrence(
113 ImportD, Mod, Roles: (SymbolRoleSet)SymbolRole::Reference, Loc: 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 else
125 Loc = ImportD->getLocation();
126
127 SourceManager &SM = Ctx->getSourceManager();
128 FileID FID = SM.getFileID(SpellingLoc: SM.getFileLoc(Loc));
129 if (FID.isInvalid())
130 return true;
131
132 bool Invalid = false;
133 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, Invalid: &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: Mod->Parent, IdLocs: 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>(Val: D)) {
164 TKind = SD->getSpecializationKind();
165 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Val: D)) {
166 TKind = FD->getTemplateSpecializationKind();
167 } else if (auto *VD = dyn_cast<VarDecl>(Val: D)) {
168 TKind = VD->getTemplateSpecializationKind();
169 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(Val: D)) {
170 if (RD->getInstantiatedFromMemberClass())
171 TKind = RD->getTemplateSpecializationKind();
172 } else if (const auto *ED = dyn_cast<EnumDecl>(Val: D)) {
173 if (ED->getInstantiatedFromMemberEnum())
174 TKind = ED->getTemplateSpecializationKind();
175 } else if (isa<FieldDecl>(Val: D) || isa<TypedefNameDecl>(Val: D) ||
176 isa<EnumConstantDecl>(Val: D)) {
177 if (const auto *Parent = dyn_cast<Decl>(Val: D->getDeclContext()))
178 return isTemplateImplicitInstantiation(D: 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>(Val: 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>(Val: D))
198 return false;
199 if (isa<ObjCCategoryDecl>(Val: D))
200 return false;
201 if (isa<ObjCIvarDecl>(Val: D))
202 return false;
203 if (isa<ObjCMethodDecl>(Val: D))
204 return false;
205 if (isa<ImportDecl>(Val: 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>(Val: D->getDeclContext()))
214 return CTSD->getTemplateInstantiationPattern();
215 else if (const auto *RD = dyn_cast<CXXRecordDecl>(Val: 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>(Val: 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>(Val: D)) {
230 return FD->getTemplateInstantiationPattern();
231 } else if (auto *VD = dyn_cast<VarDecl>(Val: D)) {
232 return VD->getTemplateInstantiationPattern();
233 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(Val: D)) {
234 return RD->getInstantiatedFromMemberClass();
235 } else if (const auto *ED = dyn_cast<EnumDecl>(Val: D)) {
236 return ED->getInstantiatedFromMemberEnum();
237 } else if (isa<FieldDecl>(Val: D) || isa<TypedefNameDecl>(Val: D)) {
238 const auto *ND = cast<NamedDecl>(Val: 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>(Val: 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>(Val: D))
261 return VD->isThisDeclarationADefinition(Ctx);
262
263 if (auto FD = dyn_cast<FunctionDecl>(Val: D))
264 return FD->isThisDeclarationADefinition();
265
266 if (auto TD = dyn_cast<TagDecl>(Val: D))
267 return TD->isThisDeclarationADefinition();
268
269 if (auto MD = dyn_cast<ObjCMethodDecl>(Val: D))
270 return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(Val: ContainerDC);
271
272 if (isa<TypedefNameDecl>(Val: D) || isa<EnumConstantDecl>(Val: D) ||
273 isa<FieldDecl>(Val: D) || isa<MSPropertyDecl>(Val: D) || isa<ObjCImplDecl>(Val: D) ||
274 isa<ObjCPropertyImplDecl>(Val: D) || isa<ConceptDecl>(Val: 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>(Val: ND) &&
283 !isa<ObjCCategoryDecl>(Val: ND)) || isa<CXXDeductionGuideDecl>(Val: ND);
284}
285
286static const Decl *adjustParent(const Decl *Parent) {
287 if (!Parent)
288 return nullptr;
289 for (;; Parent = cast<Decl>(Val: Parent->getDeclContext())) {
290 if (isa<TranslationUnitDecl>(Val: Parent))
291 return nullptr;
292 if (isa<LinkageSpecDecl>(Val: Parent) || isa<BlockDecl>(Val: Parent))
293 continue;
294 if (auto NS = dyn_cast<NamespaceDecl>(Val: Parent)) {
295 if (NS->isAnonymousNamespace())
296 continue;
297 } else if (auto RD = dyn_cast<RecordDecl>(Val: Parent)) {
298 if (RD->isAnonymousStructOrUnion())
299 continue;
300 } else if (auto ND = dyn_cast<NamedDecl>(Val: 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>(Val: 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: roles, Fn: [&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>(Val: D))
375 return true;
376 if (!isa<NamedDecl>(Val: D) || shouldSkipNamelessDecl(ND: cast<NamedDecl>(Val: D)))
377 return true;
378
379 SourceManager &SM = Ctx->getSourceManager();
380 FileID FID = SM.getFileID(SpellingLoc: SM.getFileLoc(Loc));
381 if (FID.isInvalid())
382 return true;
383
384 bool Invalid = false;
385 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, Invalid: &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(D: OrigD, ContainerDC, Ctx&: *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(D: Parent);
425
426 SmallVector<SymbolRelation, 6> FinalRelations;
427 FinalRelations.reserve(N: Relations.size()+1);
428
429 auto addRelation = [&](SymbolRelation Rel) {
430 auto It = llvm::find_if(Range&: FinalRelations, P: [&](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(Elt: Rel);
437 }
438 Roles |= Rel.Roles;
439 };
440
441 if (Parent) {
442 if (IsRef || (!isa<ParmVarDecl>(Val: 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: OrigE, .OrigD: OrigD, .Parent: Parent, .ContainerDC: ContainerDC};
461 return DataConsumer.handleDeclOccurrence(D, Roles, Relations: FinalRelations, Loc, ASTNode: 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: &Name, MI: &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: &Name, MI: &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: &Name, MI: &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(SpellingLoc: SM.getFileLoc(Loc));
509 if (FID.isInvalid())
510 return false;
511
512 bool Invalid = false;
513 const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, Invalid: &Invalid);
514 if (Invalid || !SEntry.isFile())
515 return false;
516
517 return SEntry.getFile().getFileCharacteristic() == SrcMgr::C_User;
518}
519

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/lib/Index/IndexingContext.cpp