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