1 | //===- IndexTypeSourceInfo.cpp - Indexing types ---------------------------===// |
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/ASTConcept.h" |
11 | #include "clang/AST/PrettyPrinter.h" |
12 | #include "clang/AST/RecursiveASTVisitor.h" |
13 | #include "clang/AST/TypeLoc.h" |
14 | #include "llvm/ADT/ScopeExit.h" |
15 | |
16 | using namespace clang; |
17 | using namespace index; |
18 | |
19 | namespace { |
20 | |
21 | class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> { |
22 | IndexingContext &IndexCtx; |
23 | const NamedDecl *Parent; |
24 | const DeclContext *ParentDC; |
25 | bool IsBase; |
26 | SmallVector<SymbolRelation, 3> Relations; |
27 | |
28 | typedef RecursiveASTVisitor<TypeIndexer> base; |
29 | |
30 | public: |
31 | TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, |
32 | const DeclContext *DC, bool isBase, bool isIBType) |
33 | : IndexCtx(indexCtx), Parent(parent), ParentDC(DC), IsBase(isBase) { |
34 | if (IsBase) { |
35 | assert(Parent); |
36 | Relations.emplace_back(Args: (unsigned)SymbolRole::RelationBaseOf, Args&: Parent); |
37 | } |
38 | if (isIBType) { |
39 | assert(Parent); |
40 | Relations.emplace_back(Args: (unsigned)SymbolRole::RelationIBTypeOf, Args&: Parent); |
41 | } |
42 | } |
43 | |
44 | bool shouldWalkTypesOfTypeLocs() const { return false; } |
45 | |
46 | #define TRY_TO(CALL_EXPR) \ |
47 | do { \ |
48 | if (!CALL_EXPR) \ |
49 | return false; \ |
50 | } while (0) |
51 | |
52 | bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TTPL) { |
53 | SourceLocation Loc = TTPL.getNameLoc(); |
54 | TemplateTypeParmDecl *TTPD = TTPL.getDecl(); |
55 | return IndexCtx.handleReference(TTPD, Loc, Parent, ParentDC, |
56 | SymbolRoleSet()); |
57 | } |
58 | |
59 | bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { |
60 | SourceLocation Loc = TL.getNameLoc(); |
61 | TypedefNameDecl *ND = TL.getTypedefNameDecl(); |
62 | if (ND->isTransparentTag()) { |
63 | TagDecl *Underlying = ND->getUnderlyingType()->getAsTagDecl(); |
64 | return IndexCtx.handleReference(Underlying, Loc, Parent, |
65 | ParentDC, SymbolRoleSet(), Relations); |
66 | } |
67 | if (IsBase) { |
68 | TRY_TO(IndexCtx.handleReference(ND, Loc, |
69 | Parent, ParentDC, SymbolRoleSet())); |
70 | if (auto *CD = TL.getType()->getAsCXXRecordDecl()) { |
71 | TRY_TO(IndexCtx.handleReference(CD, Loc, Parent, ParentDC, |
72 | (unsigned)SymbolRole::Implicit, |
73 | Relations)); |
74 | } |
75 | } else { |
76 | TRY_TO(IndexCtx.handleReference(ND, Loc, |
77 | Parent, ParentDC, SymbolRoleSet(), |
78 | Relations)); |
79 | } |
80 | return true; |
81 | } |
82 | |
83 | bool VisitAutoTypeLoc(AutoTypeLoc TL) { |
84 | if (auto *C = TL.getNamedConcept()) |
85 | return IndexCtx.handleReference(C, TL.getConceptNameLoc(), Parent, |
86 | ParentDC); |
87 | return true; |
88 | } |
89 | |
90 | bool traverseParamVarHelper(ParmVarDecl *D) { |
91 | TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); |
92 | if (D->getTypeSourceInfo()) |
93 | TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); |
94 | return true; |
95 | } |
96 | |
97 | bool TraverseParmVarDecl(ParmVarDecl *D) { |
98 | // Avoid visiting default arguments from the definition that were already |
99 | // visited in the declaration. |
100 | // FIXME: A free function definition can have default arguments. |
101 | // Avoiding double visitaiton of default arguments should be handled by the |
102 | // visitor probably with a bit in the AST to indicate if the attached |
103 | // default argument was 'inherited' or written in source. |
104 | if (auto FD = dyn_cast<FunctionDecl>(D->getDeclContext())) { |
105 | if (FD->isThisDeclarationADefinition()) { |
106 | return traverseParamVarHelper(D); |
107 | } |
108 | } |
109 | |
110 | return base::TraverseParmVarDecl(D); |
111 | } |
112 | |
113 | bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { |
114 | IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, DC: ParentDC); |
115 | return true; |
116 | } |
117 | |
118 | bool VisitTagTypeLoc(TagTypeLoc TL) { |
119 | TagDecl *D = TL.getDecl(); |
120 | if (!IndexCtx.shouldIndexFunctionLocalSymbols() && |
121 | D->getParentFunctionOrMethod()) |
122 | return true; |
123 | |
124 | if (TL.isDefinition()) { |
125 | IndexCtx.indexTagDecl(D); |
126 | return true; |
127 | } |
128 | |
129 | return IndexCtx.handleReference(D, Loc: TL.getNameLoc(), |
130 | Parent, DC: ParentDC, Roles: SymbolRoleSet(), |
131 | Relations); |
132 | } |
133 | |
134 | bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { |
135 | return IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), |
136 | Parent, ParentDC, SymbolRoleSet(), Relations); |
137 | } |
138 | |
139 | bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { |
140 | for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { |
141 | IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), |
142 | Parent, ParentDC, SymbolRoleSet(), Relations); |
143 | } |
144 | return true; |
145 | } |
146 | |
147 | void HandleTemplateSpecializationTypeLoc(TemplateName TemplName, |
148 | SourceLocation TemplNameLoc, |
149 | CXXRecordDecl *ResolvedClass, |
150 | bool IsTypeAlias) { |
151 | // In presence of type aliases, the resolved class was never written in |
152 | // the code so don't report it. |
153 | if (!IsTypeAlias && ResolvedClass && |
154 | (!ResolvedClass->isImplicit() || |
155 | IndexCtx.shouldIndexImplicitInstantiation())) { |
156 | IndexCtx.handleReference(ResolvedClass, TemplNameLoc, Parent, ParentDC, |
157 | SymbolRoleSet(), Relations); |
158 | } else if (const TemplateDecl *D = TemplName.getAsTemplateDecl()) { |
159 | IndexCtx.handleReference(D, TemplNameLoc, Parent, ParentDC, |
160 | SymbolRoleSet(), Relations); |
161 | } |
162 | } |
163 | |
164 | bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { |
165 | auto *T = TL.getTypePtr(); |
166 | if (!T) |
167 | return true; |
168 | HandleTemplateSpecializationTypeLoc( |
169 | TemplName: T->getTemplateName(), TemplNameLoc: TL.getTemplateNameLoc(), ResolvedClass: T->getAsCXXRecordDecl(), |
170 | IsTypeAlias: T->isTypeAlias()); |
171 | return true; |
172 | } |
173 | |
174 | bool TraverseTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { |
175 | if (!WalkUpFromTemplateSpecializationTypeLoc(TL)) |
176 | return false; |
177 | if (!TraverseTemplateName(Template: TL.getTypePtr()->getTemplateName())) |
178 | return false; |
179 | |
180 | // The relations we have to `Parent` do not apply to our template arguments, |
181 | // so clear them while visiting the args. |
182 | SmallVector<SymbolRelation, 3> SavedRelations = Relations; |
183 | Relations.clear(); |
184 | auto ResetSavedRelations = |
185 | llvm::make_scope_exit(F: [&] { this->Relations = SavedRelations; }); |
186 | for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) { |
187 | if (!TraverseTemplateArgumentLoc(ArgLoc: TL.getArgLoc(i: I))) |
188 | return false; |
189 | } |
190 | |
191 | return true; |
192 | } |
193 | |
194 | bool VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc TL) { |
195 | auto *T = TL.getTypePtr(); |
196 | if (!T) |
197 | return true; |
198 | HandleTemplateSpecializationTypeLoc( |
199 | TemplName: T->getTemplateName(), TemplNameLoc: TL.getTemplateNameLoc(), ResolvedClass: T->getAsCXXRecordDecl(), |
200 | /*IsTypeAlias=*/false); |
201 | return true; |
202 | } |
203 | |
204 | bool VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) { |
205 | return IndexCtx.handleReference(D: TL.getDecl(), Loc: TL.getNameLoc(), Parent, |
206 | DC: ParentDC, Roles: SymbolRoleSet(), Relations); |
207 | } |
208 | |
209 | bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { |
210 | const DependentNameType *DNT = TL.getTypePtr(); |
211 | const NestedNameSpecifier *NNS = DNT->getQualifier(); |
212 | const Type *T = NNS->getAsType(); |
213 | if (!T) |
214 | return true; |
215 | const TemplateSpecializationType *TST = |
216 | T->getAs<TemplateSpecializationType>(); |
217 | if (!TST) |
218 | return true; |
219 | TemplateName TN = TST->getTemplateName(); |
220 | const ClassTemplateDecl *TD = |
221 | dyn_cast_or_null<ClassTemplateDecl>(Val: TN.getAsTemplateDecl()); |
222 | if (!TD) |
223 | return true; |
224 | CXXRecordDecl *RD = TD->getTemplatedDecl(); |
225 | if (!RD->hasDefinition()) |
226 | return true; |
227 | RD = RD->getDefinition(); |
228 | DeclarationName Name(DNT->getIdentifier()); |
229 | std::vector<const NamedDecl *> Symbols = RD->lookupDependentName( |
230 | Name, Filter: [](const NamedDecl *ND) { return isa<TypeDecl>(Val: ND); }); |
231 | if (Symbols.size() != 1) |
232 | return true; |
233 | return IndexCtx.handleReference(D: Symbols[0], Loc: TL.getNameLoc(), Parent, |
234 | DC: ParentDC, Roles: SymbolRoleSet(), Relations); |
235 | } |
236 | |
237 | bool TraverseStmt(Stmt *S) { |
238 | IndexCtx.indexBody(S, Parent, DC: ParentDC); |
239 | return true; |
240 | } |
241 | }; |
242 | |
243 | } // anonymous namespace |
244 | |
245 | void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, |
246 | const NamedDecl *Parent, |
247 | const DeclContext *DC, |
248 | bool isBase, |
249 | bool isIBType) { |
250 | if (!TInfo || TInfo->getTypeLoc().isNull()) |
251 | return; |
252 | |
253 | indexTypeLoc(TL: TInfo->getTypeLoc(), Parent, DC, isBase, isIBType); |
254 | } |
255 | |
256 | void IndexingContext::indexTypeLoc(TypeLoc TL, |
257 | const NamedDecl *Parent, |
258 | const DeclContext *DC, |
259 | bool isBase, |
260 | bool isIBType) { |
261 | if (TL.isNull()) |
262 | return; |
263 | |
264 | if (!DC) |
265 | DC = Parent->getLexicalDeclContext(); |
266 | TypeIndexer(*this, Parent, DC, isBase, isIBType).TraverseTypeLoc(TL); |
267 | } |
268 | |
269 | void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, |
270 | const NamedDecl *Parent, |
271 | const DeclContext *DC) { |
272 | if (!NNS) |
273 | return; |
274 | |
275 | if (NestedNameSpecifierLoc Prefix = NNS.getPrefix()) |
276 | indexNestedNameSpecifierLoc(NNS: Prefix, Parent, DC); |
277 | |
278 | if (!DC) |
279 | DC = Parent->getLexicalDeclContext(); |
280 | SourceLocation Loc = NNS.getLocalBeginLoc(); |
281 | |
282 | switch (NNS.getNestedNameSpecifier()->getKind()) { |
283 | case NestedNameSpecifier::Identifier: |
284 | case NestedNameSpecifier::Global: |
285 | case NestedNameSpecifier::Super: |
286 | break; |
287 | |
288 | case NestedNameSpecifier::Namespace: |
289 | handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), |
290 | Loc, Parent, DC, SymbolRoleSet()); |
291 | break; |
292 | case NestedNameSpecifier::NamespaceAlias: |
293 | handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), |
294 | Loc, Parent, DC, SymbolRoleSet()); |
295 | break; |
296 | |
297 | case NestedNameSpecifier::TypeSpec: |
298 | case NestedNameSpecifier::TypeSpecWithTemplate: |
299 | indexTypeLoc(TL: NNS.getTypeLoc(), Parent, DC); |
300 | break; |
301 | } |
302 | } |
303 | |
304 | void IndexingContext::indexTagDecl(const TagDecl *D, |
305 | ArrayRef<SymbolRelation> Relations) { |
306 | if (!shouldIndex(D)) |
307 | return; |
308 | if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D)) |
309 | return; |
310 | |
311 | if (handleDecl(D, /*Roles=*/SymbolRoleSet(), Relations)) { |
312 | if (D->isThisDeclarationADefinition()) { |
313 | indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); |
314 | if (auto CXXRD = dyn_cast<CXXRecordDecl>(Val: D)) { |
315 | for (const auto &I : CXXRD->bases()) { |
316 | indexTypeSourceInfo(I.getTypeSourceInfo(), CXXRD, CXXRD, /*isBase=*/true); |
317 | } |
318 | } |
319 | indexDeclContext(D); |
320 | } |
321 | } |
322 | } |
323 | |