clang-tools  4.0.0
USRFinder.cpp
Go to the documentation of this file.
1 //===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
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 "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 rename {
27 
28 // NamedDeclFindingASTVisitor recursively visits each AST node to find the
29 // symbol underneath the cursor.
30 // FIXME: move to seperate .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  return true;
140  }
141  Result = Decl;
142  return false;
143  }
144 
145  // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
146  // \returns false on success.
147  bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
148  // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
149  return Offset == 0 ||
150  setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
151  }
152 
153  // \brief Determines if the Point is within Start and End.
154  bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
155  // FIXME: Add tests for Point == End.
156  return Point == Start || Point == End ||
157  (Context.getSourceManager().isBeforeInTranslationUnit(Start,
158  Point) &&
159  Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
160  }
161 
162  const NamedDecl *Result;
163  const SourceLocation Point; // The location to find the NamedDecl.
164  const std::string Name;
165  const ASTContext &Context;
166 };
167 } // namespace
168 
169 const NamedDecl *getNamedDeclAt(const ASTContext &Context,
170  const SourceLocation Point) {
171  const SourceManager &SM = Context.getSourceManager();
172  NamedDeclFindingASTVisitor Visitor(Point, Context);
173 
174  // Try to be clever about pruning down the number of top-level declarations we
175  // see. If both start and end is either before or after the point we're
176  // looking for the point cannot be inside of this decl. Don't even look at it.
177  for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
178  SourceLocation StartLoc = CurrDecl->getLocStart();
179  SourceLocation EndLoc = CurrDecl->getLocEnd();
180  if (StartLoc.isValid() && EndLoc.isValid() &&
181  SM.isBeforeInTranslationUnit(StartLoc, Point) !=
182  SM.isBeforeInTranslationUnit(EndLoc, Point))
183  Visitor.TraverseDecl(CurrDecl);
184  }
185 
186  NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
187  for (const auto &Location : Finder.getNestedNameSpecifierLocations())
188  Visitor.handleNestedNameSpecifierLoc(Location);
189 
190  return Visitor.getNamedDecl();
191 }
192 
193 const NamedDecl *getNamedDeclFor(const ASTContext &Context,
194  const std::string &Name) {
195  NamedDeclFindingASTVisitor Visitor(Name, Context);
196  Visitor.TraverseDecl(Context.getTranslationUnitDecl());
197 
198  return Visitor.getNamedDecl();
199 }
200 
201 std::string getUSRForDecl(const Decl *Decl) {
202  llvm::SmallVector<char, 128> Buff;
203 
204  // FIXME: Add test for the nullptr case.
205  if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
206  return "";
207 
208  return std::string(Buff.data(), Buff.size());
209 }
210 
211 } // namespace rename
212 } // namespace clang
SourceLocation Loc
'#' location in the include directive
const std::string Name
Definition: USRFinder.cpp:164
const SourceLocation Point
Definition: USRFinder.cpp:163
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:169
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:201
const ASTContext & Context
Definition: USRFinder.cpp:165
Methods for determining the USR of a symbol at a location in source code.
SourceManager & SM
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:193
std::vector< NestedNameSpecifierLoc > getNestedNameSpecifierLocations()
Definition: USRFinder.h:57
const NamedDecl * Result
Definition: USRFinder.cpp:162