1 //===--- USRFinder.cpp - Clang refactoring library ------------------------===// 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 /// \file Implements a recursive AST visitor that finds the USR of a symbol at a 10 /// point. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Tooling/Refactoring/Rename/USRFinder.h" 15 #include "clang/AST/AST.h" 16 #include "clang/AST/ASTContext.h" 17 #include "clang/AST/RecursiveASTVisitor.h" 18 #include "clang/Index/USRGeneration.h" 19 #include "clang/Lex/Lexer.h" 20 #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h" 21 #include "llvm/ADT/SmallVector.h" 22 23 using namespace llvm; 24 25 namespace clang { 26 namespace tooling { 27 28 namespace { 29 30 /// Recursively visits each AST node to find the symbol underneath the cursor. 31 class NamedDeclOccurrenceFindingVisitor 32 : public RecursiveSymbolVisitor<NamedDeclOccurrenceFindingVisitor> { 33 public: 34 // Finds the NamedDecl at a point in the source. 35 // \param Point the location in the source to search for the NamedDecl. 36 explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point, 37 const ASTContext &Context) 38 : RecursiveSymbolVisitor(Context.getSourceManager(), 39 Context.getLangOpts()), 40 Point(Point), Context(Context) {} 41 42 bool visitSymbolOccurrence(const NamedDecl *ND, 43 ArrayRef<SourceRange> NameRanges) { 44 if (!ND) 45 return true; 46 for (const auto &Range : NameRanges) { 47 SourceLocation Start = Range.getBegin(); 48 SourceLocation End = Range.getEnd(); 49 if (!Start.isValid() || !Start.isFileID() || !End.isValid() || 50 !End.isFileID() || !isPointWithin(Start, End)) 51 return true; 52 } 53 Result = ND; 54 return false; 55 } 56 57 const NamedDecl *getNamedDecl() const { return Result; } 58 59 private: 60 // Determines if the Point is within Start and End. 61 bool isPointWithin(const SourceLocation Start, const SourceLocation End) { 62 // FIXME: Add tests for Point == End. 63 return Point == Start || Point == End || 64 (Context.getSourceManager().isBeforeInTranslationUnit(Start, 65 Point) && 66 Context.getSourceManager().isBeforeInTranslationUnit(Point, End)); 67 } 68 69 const NamedDecl *Result = nullptr; 70 const SourceLocation Point; // The location to find the NamedDecl. 71 const ASTContext &Context; 72 }; 73 74 } // end anonymous namespace 75 76 const NamedDecl *getNamedDeclAt(const ASTContext &Context, 77 const SourceLocation Point) { 78 const SourceManager &SM = Context.getSourceManager(); 79 NamedDeclOccurrenceFindingVisitor Visitor(Point, Context); 80 81 // Try to be clever about pruning down the number of top-level declarations we 82 // see. If both start and end is either before or after the point we're 83 // looking for the point cannot be inside of this decl. Don't even look at it. 84 for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { 85 SourceLocation StartLoc = CurrDecl->getBeginLoc(); 86 SourceLocation EndLoc = CurrDecl->getEndLoc(); 87 if (StartLoc.isValid() && EndLoc.isValid() && 88 SM.isBeforeInTranslationUnit(StartLoc, Point) != 89 SM.isBeforeInTranslationUnit(EndLoc, Point)) 90 Visitor.TraverseDecl(CurrDecl); 91 } 92 93 return Visitor.getNamedDecl(); 94 } 95 96 namespace { 97 98 /// Recursively visits each NamedDecl node to find the declaration with a 99 /// specific name. 100 class NamedDeclFindingVisitor 101 : public RecursiveASTVisitor<NamedDeclFindingVisitor> { 102 public: 103 explicit NamedDeclFindingVisitor(StringRef Name) : Name(Name) {} 104 105 // We don't have to traverse the uses to find some declaration with a 106 // specific name, so just visit the named declarations. 107 bool VisitNamedDecl(const NamedDecl *ND) { 108 if (!ND) 109 return true; 110 // Fully qualified name is used to find the declaration. 111 if (Name != ND->getQualifiedNameAsString() && 112 Name != "::" + ND->getQualifiedNameAsString()) 113 return true; 114 Result = ND; 115 return false; 116 } 117 118 const NamedDecl *getNamedDecl() const { return Result; } 119 120 private: 121 const NamedDecl *Result = nullptr; 122 StringRef Name; 123 }; 124 125 } // end anonymous namespace 126 127 const NamedDecl *getNamedDeclFor(const ASTContext &Context, 128 const std::string &Name) { 129 NamedDeclFindingVisitor Visitor(Name); 130 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 131 return Visitor.getNamedDecl(); 132 } 133 134 std::string getUSRForDecl(const Decl *Decl) { 135 llvm::SmallVector<char, 128> Buff; 136 137 // FIXME: Add test for the nullptr case. 138 if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff)) 139 return ""; 140 141 return std::string(Buff.data(), Buff.size()); 142 } 143 144 } // end namespace tooling 145 } // end namespace clang 146