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((unsigned)SymbolRole::RelationBaseOf, Parent);
37     }
38     if (isIBType) {
39       assert(Parent);
40       Relations.emplace_back((unsigned)SymbolRole::RelationIBTypeOf, 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, 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, TL.getNameLoc(),
130                                     Parent, ParentDC, 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         T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
170         T->isTypeAlias());
171     return true;
172   }
173 
174   bool TraverseTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
175     if (!WalkUpFromTemplateSpecializationTypeLoc(TL))
176       return false;
177     if (!TraverseTemplateName(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([&] { this->Relations = SavedRelations; });
186     for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
187       if (!TraverseTemplateArgumentLoc(TL.getArgLoc(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         T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
200         /*IsTypeAlias=*/false);
201     return true;
202   }
203 
204   bool VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
205     return IndexCtx.handleReference(TL.getDecl(), TL.getNameLoc(), Parent,
206                                     ParentDC, 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>(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, [](const NamedDecl *ND) { return isa<TypeDecl>(ND); });
231     if (Symbols.size() != 1)
232       return true;
233     return IndexCtx.handleReference(Symbols[0], TL.getNameLoc(), Parent,
234                                     ParentDC, SymbolRoleSet(), Relations);
235   }
236 
237   bool TraverseStmt(Stmt *S) {
238     IndexCtx.indexBody(S, Parent, 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(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(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(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>(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