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