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