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
16using namespace clang;
17using namespace index;
18
19namespace {
20
21class 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
30public:
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
245void 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
256void 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
269void 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
304void 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