clang-tools  3.9.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 SourceManager &SourceMgr,
38  const SourceLocation Point)
39  : Result(nullptr), SourceMgr(SourceMgr),
40  Point(Point) {
41  }
42 
43  // \brief Finds the NamedDecl for a name in the source.
44  // \param Name the fully qualified name.
45  explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
46  const std::string &Name)
47  : Result(nullptr), SourceMgr(SourceMgr),
48  Name(Name) {
49  }
50 
51  // Declaration visitors:
52 
53  // \brief Checks if the point falls within the NameDecl. This covers every
54  // declaration of a named entity that we may come across. Usually, just
55  // checking if the point lies within the length of the name of the declaration
56  // and the start location is sufficient.
57  bool VisitNamedDecl(const NamedDecl *Decl) {
58  return setResult(Decl, Decl->getLocation(),
59  Decl->getNameAsString().length());
60  }
61 
62  // Expression visitors:
63 
64  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
65  // Check the namespace specifier first.
66  if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc()))
67  return false;
68 
69  const auto *Decl = Expr->getFoundDecl();
70  return setResult(Decl, Expr->getLocation(),
71  Decl->getNameAsString().length());
72  }
73 
74  bool VisitMemberExpr(const MemberExpr *Expr) {
75  const auto *Decl = Expr->getFoundDecl().getDecl();
76  return setResult(Decl, Expr->getMemberLoc(),
77  Decl->getNameAsString().length());
78  }
79 
80  // Other:
81 
82  const NamedDecl *getNamedDecl() {
83  return Result;
84  }
85 
86 private:
87  // \brief Determines if a namespace qualifier contains the point.
88  // \returns false on success and sets Result.
89  bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
90  while (NameLoc) {
91  const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
92  if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(),
93  Decl->getNameAsString().length()))
94  return false;
95  NameLoc = NameLoc.getPrefix();
96  }
97  return true;
98  }
99 
100  // \brief Sets Result to Decl if the Point is within Start and End.
101  // \returns false on success.
102  bool setResult(const NamedDecl *Decl, SourceLocation Start,
103  SourceLocation End) {
104  if (Name.empty()) {
105  // Offset is used to find the declaration.
106  if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
107  !End.isFileID() || !isPointWithin(Start, End)) {
108  return true;
109  }
110  } else {
111  // Fully qualified name is used to find the declaration.
112  if (Name != Decl->getQualifiedNameAsString()) {
113  return true;
114  }
115  }
116  Result = Decl;
117  return false;
118  }
119 
120  // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
121  // \returns false on success.
122  bool setResult(const NamedDecl *Decl, SourceLocation Loc,
123  unsigned Offset) {
124  // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
125  return Offset == 0 ||
126  setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
127  }
128 
129  // \brief Determines if the Point is within Start and End.
130  bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
131  // FIXME: Add tests for Point == End.
132  return Point == Start || Point == End ||
133  (SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
134  SourceMgr.isBeforeInTranslationUnit(Point, End));
135  }
136 
137  const NamedDecl *Result;
138  const SourceManager &SourceMgr;
139  const SourceLocation Point; // The location to find the NamedDecl.
140  const std::string Name;
141 };
142 } // namespace
143 
144 const NamedDecl *getNamedDeclAt(const ASTContext &Context,
145  const SourceLocation Point) {
146  const auto &SourceMgr = Context.getSourceManager();
147  const auto SearchFile = SourceMgr.getFilename(Point);
148 
149  NamedDeclFindingASTVisitor Visitor(SourceMgr, Point);
150 
151  // We only want to search the decls that exist in the same file as the point.
152  auto Decls = Context.getTranslationUnitDecl()->decls();
153  for (auto &CurrDecl : Decls) {
154  const auto FileLoc = CurrDecl->getLocStart();
155  const auto FileName = SourceMgr.getFilename(FileLoc);
156  // FIXME: Add test.
157  if (FileName == SearchFile) {
158  Visitor.TraverseDecl(CurrDecl);
159  if (const NamedDecl *Result = Visitor.getNamedDecl()) {
160  return Result;
161  }
162  }
163  }
164 
165  return nullptr;
166 }
167 
168 const NamedDecl *getNamedDeclFor(const ASTContext &Context,
169  const std::string &Name) {
170  const auto &SourceMgr = Context.getSourceManager();
171  NamedDeclFindingASTVisitor Visitor(SourceMgr, Name);
172  auto Decls = Context.getTranslationUnitDecl()->decls();
173 
174  for (auto &CurrDecl : Decls) {
175  Visitor.TraverseDecl(CurrDecl);
176  if (const NamedDecl *Result = Visitor.getNamedDecl()) {
177  return Result;
178  }
179  }
180 
181  return nullptr;
182 }
183 
184 std::string getUSRForDecl(const Decl *Decl) {
185  llvm::SmallVector<char, 128> Buff;
186 
187  // FIXME: Add test for the nullptr case.
188  if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
189  return "";
190 
191  return std::string(Buff.data(), Buff.size());
192 }
193 
194 } // namespace rename
195 } // namespace clang
SourceLocation Loc
'#' location in the include directive
const std::string Name
Definition: USRFinder.cpp:140
const SourceLocation Point
Definition: USRFinder.cpp:139
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:144
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:184
Methods for determining the USR of a symbol at a location in source code.
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:168
const SourceManager & SourceMgr
Definition: USRFinder.cpp:138
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
const NamedDecl * Result
Definition: USRFinder.cpp:137