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.
NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,const ASTContext & Context)35 explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,
36 const ASTContext &Context)
37 : RecursiveSymbolVisitor(Context.getSourceManager(),
38 Context.getLangOpts()),
39 Point(Point), Context(Context) {}
40
visitSymbolOccurrence(const NamedDecl * ND,ArrayRef<SourceRange> NameRanges)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
getNamedDecl() const56 const NamedDecl *getNamedDecl() const { return Result; }
57
58 private:
59 // Determines if the Point is within Start and End.
isPointWithin(const SourceLocation Start,const SourceLocation 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
getNamedDeclAt(const ASTContext & Context,const SourceLocation Point)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:
NamedDeclFindingVisitor(StringRef Name)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.
VisitNamedDecl(const NamedDecl * ND)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
getNamedDecl() const117 const NamedDecl *getNamedDecl() const { return Result; }
118
119 private:
120 const NamedDecl *Result = nullptr;
121 StringRef Name;
122 };
123
124 } // end anonymous namespace
125
getNamedDeclFor(const ASTContext & Context,const std::string & Name)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
getUSRForDecl(const Decl * Decl)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