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