blob: 494bc75b56b37fcb3d80933c7939b5c1f2d3081a [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.
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000037 explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
38 const ASTContext &Context)
39 : Result(nullptr), Point(Point), Context(Context) {}
Manuel Klimekde237262014-08-20 01:39:05 +000040
Miklos Vajna47bd4632016-06-21 19:48:57 +000041 // \brief Finds the NamedDecl for a name in the source.
42 // \param Name the fully qualified name.
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000043 explicit NamedDeclFindingASTVisitor(const std::string &Name,
44 const ASTContext &Context)
45 : Result(nullptr), Name(Name), Context(Context) {}
Miklos Vajna47bd4632016-06-21 19:48:57 +000046
Manuel Klimekde237262014-08-20 01:39:05 +000047 // 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) {
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000054 return dyn_cast<CXXConversionDecl>(Decl)
55 ? true
56 : setResult(Decl, Decl->getLocation(),
57 Decl->getNameAsString().length());
Manuel Klimekde237262014-08-20 01:39:05 +000058 }
59
60 // Expression visitors:
61
62 bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +000063 const NamedDecl *Decl = Expr->getFoundDecl();
Manuel Klimekde237262014-08-20 01:39:05 +000064 return setResult(Decl, Expr->getLocation(),
65 Decl->getNameAsString().length());
66 }
67
68 bool VisitMemberExpr(const MemberExpr *Expr) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +000069 const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
Manuel Klimekde237262014-08-20 01:39:05 +000070 return setResult(Decl, Expr->getMemberLoc(),
71 Decl->getNameAsString().length());
72 }
73
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000074 // Other visitors:
75
76 bool VisitTypeLoc(const TypeLoc Loc) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +000077 const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
78 const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
Miklos Vajna5a992072016-08-30 07:24:57 +000079 TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
Kirill Bobyrev9e0dab92016-08-02 09:38:38 +000080 if (const auto *TemplateTypeParm =
Kirill Bobyrev87697782016-09-04 22:50:41 +000081 dyn_cast<TemplateTypeParmType>(Loc.getType()))
Kirill Bobyrev9e0dab92016-08-02 09:38:38 +000082 return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
Kirill Bobyrevd6ab7d42016-08-03 23:00:32 +000083 if (const auto *TemplateSpecType =
84 dyn_cast<TemplateSpecializationType>(Loc.getType())) {
85 return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
86 TypeBeginLoc, TypeEndLoc);
87 }
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000088 return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
89 TypeEndLoc);
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000090 }
91
Kirill Bobyrev31fd7fb2016-08-09 07:14:48 +000092 bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
Kirill Bobyrev7fa33952016-08-09 10:03:33 +000093 for (const auto *Initializer : ConstructorDecl->inits()) {
Kirill Bobyrev87697782016-09-04 22:50:41 +000094 // Ignore implicit initializers.
95 if (!Initializer->isWritten())
Kirill Bobyrev31fd7fb2016-08-09 07:14:48 +000096 continue;
Kirill Bobyrev31fd7fb2016-08-09 07:14:48 +000097 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());
Kirill Bobyrev87697782016-09-04 22:50:41 +0000102 if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
Kirill Bobyrev31fd7fb2016-08-09 07:14:48 +0000103 return false;
Kirill Bobyrev31fd7fb2016-08-09 07:14:48 +0000104 }
105 }
106 return true;
107 }
108
Manuel Klimekde237262014-08-20 01:39:05 +0000109 // Other:
110
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000111 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) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000117 const NamespaceDecl *Decl =
118 NameLoc.getNestedNameSpecifier()->getAsNamespace();
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000119 setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000120 NameLoc = NameLoc.getPrefix();
121 }
Manuel Klimekde237262014-08-20 01:39:05 +0000122 }
123
124private:
Manuel Klimekde237262014-08-20 01:39:05 +0000125 // \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) {
Kirill Bobyrev87697782016-09-04 22:50:41 +0000129 if (!Decl)
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000130 return true;
Miklos Vajna47bd4632016-06-21 19:48:57 +0000131 if (Name.empty()) {
132 // Offset is used to find the declaration.
133 if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
Kirill Bobyrev87697782016-09-04 22:50:41 +0000134 !End.isFileID() || !isPointWithin(Start, End))
Miklos Vajna47bd4632016-06-21 19:48:57 +0000135 return true;
Miklos Vajna47bd4632016-06-21 19:48:57 +0000136 } else {
137 // Fully qualified name is used to find the declaration.
Kirill Bobyrev87697782016-09-04 22:50:41 +0000138 if (Name != Decl->getQualifiedNameAsString())
Miklos Vajna47bd4632016-06-21 19:48:57 +0000139 return true;
Manuel Klimekde237262014-08-20 01:39:05 +0000140 }
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.
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000147 bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
Manuel Klimekde237262014-08-20 01:39:05 +0000148 // 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 ||
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000157 (Context.getSourceManager().isBeforeInTranslationUnit(Start,
158 Point) &&
159 Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
Manuel Klimekde237262014-08-20 01:39:05 +0000160 }
161
162 const NamedDecl *Result;
Manuel Klimekde237262014-08-20 01:39:05 +0000163 const SourceLocation Point; // The location to find the NamedDecl.
Miklos Vajna47bd4632016-06-21 19:48:57 +0000164 const std::string Name;
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000165 const ASTContext &Context;
Manuel Klimekde237262014-08-20 01:39:05 +0000166};
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000167} // namespace
Manuel Klimekde237262014-08-20 01:39:05 +0000168
169const NamedDecl *getNamedDeclAt(const ASTContext &Context,
170 const SourceLocation Point) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000171 StringRef SearchFile = Context.getSourceManager().getFilename(Point);
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000172 NamedDeclFindingASTVisitor Visitor(Point, Context);
Manuel Klimekde237262014-08-20 01:39:05 +0000173
174 // We only want to search the decls that exist in the same file as the point.
Kirill Bobyrevbc84fc92016-08-16 06:19:06 +0000175 for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000176 const SourceLocation FileLoc = CurrDecl->getLocStart();
177 StringRef FileName = Context.getSourceManager().getFilename(FileLoc);
Manuel Klimekde237262014-08-20 01:39:05 +0000178 // FIXME: Add test.
Kirill Bobyrev87697782016-09-04 22:50:41 +0000179 if (FileName == SearchFile)
Manuel Klimekde237262014-08-20 01:39:05 +0000180 Visitor.TraverseDecl(CurrDecl);
Manuel Klimekde237262014-08-20 01:39:05 +0000181 }
182
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000183 NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
Kirill Bobyrev87697782016-09-04 22:50:41 +0000184 for (const auto &Location : Finder.getNestedNameSpecifierLocations())
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000185 Visitor.handleNestedNameSpecifierLoc(Location);
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000186
187 return Visitor.getNamedDecl();
Manuel Klimekde237262014-08-20 01:39:05 +0000188}
189
Miklos Vajna47bd4632016-06-21 19:48:57 +0000190const NamedDecl *getNamedDeclFor(const ASTContext &Context,
191 const std::string &Name) {
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +0000192 NamedDeclFindingASTVisitor Visitor(Name, Context);
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000193 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
Miklos Vajna47bd4632016-06-21 19:48:57 +0000194
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000195 return Visitor.getNamedDecl();
Miklos Vajna47bd4632016-06-21 19:48:57 +0000196}
197
Manuel Klimekde237262014-08-20 01:39:05 +0000198std::string getUSRForDecl(const Decl *Decl) {
199 llvm::SmallVector<char, 128> Buff;
200
201 // FIXME: Add test for the nullptr case.
202 if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
203 return "";
204
205 return std::string(Buff.data(), Buff.size());
206}
207
Manuel Klimekde237262014-08-20 01:39:05 +0000208} // namespace rename
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000209} // namespace clang