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