blob: 7e99e7046e479b6c2ec69dc235d3f45902749ea4 [file] [log] [blame]
Manuel Klimekde237262014-08-20 01:39:05 +00001//===--- 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"
Manuel Klimekde237262014-08-20 01:39:05 +000019#include "clang/Index/USRGeneration.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000020#include "clang/Lex/Lexer.h"
Manuel Klimekde237262014-08-20 01:39:05 +000021#include "llvm/ADT/SmallVector.h"
22
23using namespace llvm;
24
25namespace clang {
26namespace 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.
31namespace {
32class NamedDeclFindingASTVisitor
33 : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
34public:
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
Miklos Vajna47bd4632016-06-21 19:48:57 +000043 // \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
Manuel Klimekde237262014-08-20 01:39:05 +000051 // 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
86private:
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) {
Miklos Vajna47bd4632016-06-21 19:48:57 +0000104 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 }
Manuel Klimekde237262014-08-20 01:39:05 +0000115 }
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.
Miklos Vajna47bd4632016-06-21 19:48:57 +0000140 const std::string Name;
Manuel Klimekde237262014-08-20 01:39:05 +0000141};
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000142} // namespace
Manuel Klimekde237262014-08-20 01:39:05 +0000143
144const 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
Miklos Vajna47bd4632016-06-21 19:48:57 +0000168const 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
Manuel Klimekde237262014-08-20 01:39:05 +0000184std::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
Manuel Klimekde237262014-08-20 01:39:05 +0000194} // namespace rename
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000195} // namespace clang