1 //===--- USRFinder.cpp - Clang refactoring library ------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file Implements a recursive AST visitor that finds the USR of a symbol at a 11 /// point. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Tooling/Refactoring/Rename/USRFinder.h" 16 #include "clang/AST/AST.h" 17 #include "clang/AST/ASTContext.h" 18 #include "clang/AST/RecursiveASTVisitor.h" 19 #include "clang/Index/USRGeneration.h" 20 #include "clang/Lex/Lexer.h" 21 #include "llvm/ADT/SmallVector.h" 22 23 using namespace llvm; 24 25 namespace clang { 26 namespace tooling { 27 28 // NamedDeclFindingASTVisitor recursively visits each AST node to find the 29 // symbol underneath the cursor. 30 // FIXME: move to separate .h/.cc file if this gets too large. 31 namespace { 32 class NamedDeclFindingASTVisitor 33 : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> { 34 public: 35 // \brief Finds the NamedDecl at a point in the source. 36 // \param Point the location in the source to search for the NamedDecl. 37 explicit NamedDeclFindingASTVisitor(const SourceLocation Point, 38 const ASTContext &Context) 39 : Result(nullptr), Point(Point), Context(Context) {} 40 41 // \brief Finds the NamedDecl for a name in the source. 42 // \param Name the fully qualified name. 43 explicit NamedDeclFindingASTVisitor(const std::string &Name, 44 const ASTContext &Context) 45 : Result(nullptr), Name(Name), Context(Context) {} 46 47 // Declaration visitors: 48 49 // \brief Checks if the point falls within the NameDecl. This covers every 50 // declaration of a named entity that we may come across. Usually, just 51 // checking if the point lies within the length of the name of the declaration 52 // and the start location is sufficient. 53 bool VisitNamedDecl(const NamedDecl *Decl) { 54 return dyn_cast<CXXConversionDecl>(Decl) 55 ? true 56 : setResult(Decl, Decl->getLocation(), 57 Decl->getNameAsString().length()); 58 } 59 60 // Expression visitors: 61 62 bool VisitDeclRefExpr(const DeclRefExpr *Expr) { 63 const NamedDecl *Decl = Expr->getFoundDecl(); 64 return setResult(Decl, Expr->getLocation(), 65 Decl->getNameAsString().length()); 66 } 67 68 bool VisitMemberExpr(const MemberExpr *Expr) { 69 const NamedDecl *Decl = Expr->getFoundDecl().getDecl(); 70 return setResult(Decl, Expr->getMemberLoc(), 71 Decl->getNameAsString().length()); 72 } 73 74 // Other visitors: 75 76 bool VisitTypeLoc(const TypeLoc Loc) { 77 const SourceLocation TypeBeginLoc = Loc.getBeginLoc(); 78 const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken( 79 TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); 80 if (const auto *TemplateTypeParm = 81 dyn_cast<TemplateTypeParmType>(Loc.getType())) 82 return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc); 83 if (const auto *TemplateSpecType = 84 dyn_cast<TemplateSpecializationType>(Loc.getType())) { 85 return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(), 86 TypeBeginLoc, TypeEndLoc); 87 } 88 return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, 89 TypeEndLoc); 90 } 91 92 bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { 93 for (const auto *Initializer : ConstructorDecl->inits()) { 94 // Ignore implicit initializers. 95 if (!Initializer->isWritten()) 96 continue; 97 if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) { 98 const SourceLocation InitBeginLoc = Initializer->getSourceLocation(), 99 InitEndLoc = Lexer::getLocForEndOfToken( 100 InitBeginLoc, 0, Context.getSourceManager(), 101 Context.getLangOpts()); 102 if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc)) 103 return false; 104 } 105 } 106 return true; 107 } 108 109 // Other: 110 111 const NamedDecl *getNamedDecl() { return Result; } 112 113 // \brief Determines if a namespace qualifier contains the point. 114 // \returns false on success and sets Result. 115 void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { 116 while (NameLoc) { 117 const NamespaceDecl *Decl = 118 NameLoc.getNestedNameSpecifier()->getAsNamespace(); 119 setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc()); 120 NameLoc = NameLoc.getPrefix(); 121 } 122 } 123 124 private: 125 // \brief Sets Result to Decl if the Point is within Start and End. 126 // \returns false on success. 127 bool setResult(const NamedDecl *Decl, SourceLocation Start, 128 SourceLocation End) { 129 if (!Decl) 130 return true; 131 if (Name.empty()) { 132 // Offset is used to find the declaration. 133 if (!Start.isValid() || !Start.isFileID() || !End.isValid() || 134 !End.isFileID() || !isPointWithin(Start, End)) 135 return true; 136 } else { 137 // Fully qualified name is used to find the declaration. 138 if (Name != Decl->getQualifiedNameAsString() && 139 Name != "::" + Decl->getQualifiedNameAsString()) 140 return true; 141 } 142 Result = Decl; 143 return false; 144 } 145 146 // \brief Sets Result to Decl if Point is within Loc and Loc + Offset. 147 // \returns false on success. 148 bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) { 149 // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc). 150 return Offset == 0 || 151 setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1)); 152 } 153 154 // \brief Determines if the Point is within Start and End. 155 bool isPointWithin(const SourceLocation Start, const SourceLocation End) { 156 // FIXME: Add tests for Point == End. 157 return Point == Start || Point == End || 158 (Context.getSourceManager().isBeforeInTranslationUnit(Start, 159 Point) && 160 Context.getSourceManager().isBeforeInTranslationUnit(Point, End)); 161 } 162 163 const NamedDecl *Result; 164 const SourceLocation Point; // The location to find the NamedDecl. 165 const std::string Name; 166 const ASTContext &Context; 167 }; 168 } // namespace 169 170 const NamedDecl *getNamedDeclAt(const ASTContext &Context, 171 const SourceLocation Point) { 172 const SourceManager &SM = Context.getSourceManager(); 173 NamedDeclFindingASTVisitor Visitor(Point, Context); 174 175 // Try to be clever about pruning down the number of top-level declarations we 176 // see. If both start and end is either before or after the point we're 177 // looking for the point cannot be inside of this decl. Don't even look at it. 178 for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { 179 SourceLocation StartLoc = CurrDecl->getLocStart(); 180 SourceLocation EndLoc = CurrDecl->getLocEnd(); 181 if (StartLoc.isValid() && EndLoc.isValid() && 182 SM.isBeforeInTranslationUnit(StartLoc, Point) != 183 SM.isBeforeInTranslationUnit(EndLoc, Point)) 184 Visitor.TraverseDecl(CurrDecl); 185 } 186 187 NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context)); 188 for (const auto &Location : Finder.getNestedNameSpecifierLocations()) 189 Visitor.handleNestedNameSpecifierLoc(Location); 190 191 return Visitor.getNamedDecl(); 192 } 193 194 const NamedDecl *getNamedDeclFor(const ASTContext &Context, 195 const std::string &Name) { 196 NamedDeclFindingASTVisitor Visitor(Name, Context); 197 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 198 199 return Visitor.getNamedDecl(); 200 } 201 202 std::string getUSRForDecl(const Decl *Decl) { 203 llvm::SmallVector<char, 128> Buff; 204 205 // FIXME: Add test for the nullptr case. 206 if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff)) 207 return ""; 208 209 return std::string(Buff.data(), Buff.size()); 210 } 211 212 } // end namespace tooling 213 } // end namespace clang 214