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