blob: 44ffae90efa7d232962da9a5ee5a2b7c911c3417 [file] [log] [blame]
Alex Lorenz4abbd922017-06-30 16:36:09 +00001//===--- RenamingAction.cpp - Clang refactoring library -------------------===//
Manuel Klimekde237262014-08-20 01:39:05 +00002//
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
Adrian Prantl9fc8faf2018-05-09 01:00:01 +000011/// Provides an action to rename every symbol at a point.
Manuel Klimekde237262014-08-20 01:39:05 +000012///
13//===----------------------------------------------------------------------===//
14
Alex Lorenz4abbd922017-06-30 16:36:09 +000015#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
Manuel Klimekde237262014-08-20 01:39:05 +000016#include "clang/AST/ASTConsumer.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/Basic/FileManager.h"
19#include "clang/Frontend/CompilerInstance.h"
20#include "clang/Frontend/FrontendAction.h"
Manuel Klimekde237262014-08-20 01:39:05 +000021#include "clang/Lex/Lexer.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000022#include "clang/Lex/Preprocessor.h"
Manuel Klimekde237262014-08-20 01:39:05 +000023#include "clang/Tooling/CommonOptionsParser.h"
24#include "clang/Tooling/Refactoring.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000025#include "clang/Tooling/Refactoring/RefactoringAction.h"
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000026#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
Alex Lorenzad38fbf2017-10-13 01:53:13 +000027#include "clang/Tooling/Refactoring/RefactoringOptions.h"
28#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000029#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
30#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
Alex Lorenz4abbd922017-06-30 16:36:09 +000031#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
Manuel Klimekde237262014-08-20 01:39:05 +000032#include "clang/Tooling/Tooling.h"
Alex Lorenz44b94c72017-08-14 16:19:24 +000033#include "llvm/ADT/STLExtras.h"
Haojian Wu200458f2017-11-08 08:56:56 +000034#include "llvm/Support/Errc.h"
35#include "llvm/Support/Error.h"
Manuel Klimekde237262014-08-20 01:39:05 +000036#include <string>
37#include <vector>
38
39using namespace llvm;
40
41namespace clang {
Alex Lorenz4abbd922017-06-30 16:36:09 +000042namespace tooling {
Manuel Klimekde237262014-08-20 01:39:05 +000043
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000044namespace {
45
Haojian Wuf6c06782017-11-08 14:53:08 +000046Expected<SymbolOccurrences>
47findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) {
48 std::vector<std::string> USRs =
49 getUSRsForDeclaration(ND, Context.getASTContext());
50 std::string PrevName = ND->getNameAsString();
51 return getOccurrencesOfUSRs(USRs, PrevName,
52 Context.getASTContext().getTranslationUnitDecl());
53}
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000054
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000055} // end anonymous namespace
56
Alex Lorenz0beca4d2017-10-27 18:19:11 +000057const RefactoringDescriptor &RenameOccurrences::describe() {
58 static const RefactoringDescriptor Descriptor = {
59 "local-rename",
60 "Rename",
61 "Finds and renames symbols in code with no indexer support",
62 };
63 return Descriptor;
64}
65
66Expected<RenameOccurrences>
67RenameOccurrences::initiate(RefactoringRuleContext &Context,
68 SourceRange SelectionRange, std::string NewName) {
69 const NamedDecl *ND =
70 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());
71 if (!ND)
72 return Context.createDiagnosticError(
73 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);
Haojian Wuf5142bf2017-10-30 11:17:09 +000074 return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
75 std::move(NewName));
Alex Lorenz0beca4d2017-10-27 18:19:11 +000076}
77
78Expected<AtomicChanges>
79RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
Haojian Wuf6c06782017-11-08 14:53:08 +000080 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
Alex Lorenz0beca4d2017-10-27 18:19:11 +000081 if (!Occurrences)
82 return Occurrences.takeError();
83 // FIXME: Verify that the new name is valid.
84 SymbolName Name(NewName);
85 return createRenameReplacements(
86 *Occurrences, Context.getASTContext().getSourceManager(), Name);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000087}
88
Haojian Wu200458f2017-11-08 08:56:56 +000089Expected<QualifiedRenameRule>
90QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
91 std::string OldQualifiedName,
92 std::string NewQualifiedName) {
93 const NamedDecl *ND =
94 getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
95 if (!ND)
96 return llvm::make_error<llvm::StringError>("Could not find symbol " +
97 OldQualifiedName,
98 llvm::errc::invalid_argument);
99 return QualifiedRenameRule(ND, std::move(NewQualifiedName));
100}
101
102const RefactoringDescriptor &QualifiedRenameRule::describe() {
103 static const RefactoringDescriptor Descriptor = {
104 /*Name=*/"local-qualified-rename",
105 /*Title=*/"Qualified Rename",
106 /*Description=*/
107 R"(Finds and renames qualified symbols in code within a translation unit.
108It is used to move/rename a symbol to a new namespace/name:
109 * Supported symbols: classes, class members, functions, enums, and type alias.
110 * Renames all symbol occurrences from the old qualified name to the new
111 qualified name. All symbol references will be correctly qualified; For
112 symbol definitions, only name will be changed.
113For example, rename "A::Foo" to "B::Bar":
114 Old code:
115 namespace foo {
116 class A {};
117 }
118
119 namespace bar {
120 void f(foo::A a) {}
121 }
122
123 New code after rename:
124 namespace foo {
125 class B {};
126 }
127
128 namespace bar {
129 void f(B b) {}
130 })"
131 };
132 return Descriptor;
133}
134
135Expected<AtomicChanges>
136QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
137 auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
138 assert(!USRs.empty());
139 return tooling::createRenameAtomicChanges(
140 USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
141}
142
Alex Lorenz44b94c72017-08-14 16:19:24 +0000143Expected<std::vector<AtomicChange>>
144createRenameReplacements(const SymbolOccurrences &Occurrences,
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000145 const SourceManager &SM, const SymbolName &NewName) {
Alex Lorenz44b94c72017-08-14 16:19:24 +0000146 // FIXME: A true local rename can use just one AtomicChange.
147 std::vector<AtomicChange> Changes;
148 for (const auto &Occurrence : Occurrences) {
149 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000150 assert(NewName.getNamePieces().size() == Ranges.size() &&
Alex Lorenz44b94c72017-08-14 16:19:24 +0000151 "Mismatching number of ranges and name pieces");
152 AtomicChange Change(SM, Ranges[0].getBegin());
153 for (const auto &Range : llvm::enumerate(Ranges)) {
154 auto Error =
155 Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000156 NewName.getNamePieces()[Range.index()]);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000157 if (Error)
158 return std::move(Error);
159 }
160 Changes.push_back(std::move(Change));
161 }
Alexander Shaposhnikovab0400d2017-08-15 19:23:54 +0000162 return std::move(Changes);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000163}
164
165/// Takes each atomic change and inserts its replacements into the set of
166/// replacements that belong to the appropriate file.
167static void convertChangesToFileReplacements(
168 ArrayRef<AtomicChange> AtomicChanges,
169 std::map<std::string, tooling::Replacements> *FileToReplaces) {
170 for (const auto &AtomicChange : AtomicChanges) {
171 for (const auto &Replace : AtomicChange.getReplacements()) {
172 llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
173 if (Err) {
174 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
175 << llvm::toString(std::move(Err)) << "\n";
176 }
177 }
178 }
179}
180
Manuel Klimekde237262014-08-20 01:39:05 +0000181class RenamingASTConsumer : public ASTConsumer {
182public:
Eric Liu267034c2016-08-01 10:16:39 +0000183 RenamingASTConsumer(
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000184 const std::vector<std::string> &NewNames,
185 const std::vector<std::string> &PrevNames,
186 const std::vector<std::vector<std::string>> &USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000187 std::map<std::string, tooling::Replacements> &FileToReplaces,
188 bool PrintLocations)
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000189 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
Eric Liu267034c2016-08-01 10:16:39 +0000190 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
Manuel Klimekde237262014-08-20 01:39:05 +0000191
192 void HandleTranslationUnit(ASTContext &Context) override {
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000193 for (unsigned I = 0; I < NewNames.size(); ++I) {
194 // If the previous name was not found, ignore this rename request.
195 if (PrevNames[I].empty())
196 continue;
197
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000198 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000199 }
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000200 }
201
202 void HandleOneRename(ASTContext &Context, const std::string &NewName,
203 const std::string &PrevName,
204 const std::vector<std::string> &USRs) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000205 const SourceManager &SourceMgr = Context.getSourceManager();
Manuel Klimekde237262014-08-20 01:39:05 +0000206
Alex Lorenz44b94c72017-08-14 16:19:24 +0000207 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
Alex Lorenz4abbd922017-06-30 16:36:09 +0000208 USRs, PrevName, Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000209 if (PrintLocations) {
210 for (const auto &Occurrence : Occurrences) {
211 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
212 SourceMgr);
213 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
Manuel Klimekde237262014-08-20 01:39:05 +0000214 << ":" << FullLoc.getSpellingLineNumber() << ":"
215 << FullLoc.getSpellingColumnNumber() << "\n";
Manuel Klimekde237262014-08-20 01:39:05 +0000216 }
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000217 }
Alex Lorenz44b94c72017-08-14 16:19:24 +0000218 // FIXME: Support multi-piece names.
219 // FIXME: better error handling (propagate error out).
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000220 SymbolName NewNameRef(NewName);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000221 Expected<std::vector<AtomicChange>> Change =
222 createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
223 if (!Change) {
224 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
225 << "'! " << llvm::toString(Change.takeError()) << "\n";
226 return;
227 }
228 convertChangesToFileReplacements(*Change, &FileToReplaces);
Manuel Klimekde237262014-08-20 01:39:05 +0000229 }
230
231private:
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000232 const std::vector<std::string> &NewNames, &PrevNames;
233 const std::vector<std::vector<std::string>> &USRList;
Eric Liu267034c2016-08-01 10:16:39 +0000234 std::map<std::string, tooling::Replacements> &FileToReplaces;
Manuel Klimekde237262014-08-20 01:39:05 +0000235 bool PrintLocations;
236};
237
Haojian Wu74f823a2017-04-04 09:30:06 +0000238// A renamer to rename symbols which are identified by a give USRList to
239// new name.
240//
241// FIXME: Merge with the above RenamingASTConsumer.
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000242class USRSymbolRenamer : public ASTConsumer {
Haojian Wu74f823a2017-04-04 09:30:06 +0000243public:
244 USRSymbolRenamer(const std::vector<std::string> &NewNames,
245 const std::vector<std::vector<std::string>> &USRList,
246 std::map<std::string, tooling::Replacements> &FileToReplaces)
247 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
248 assert(USRList.size() == NewNames.size());
249 }
250
251 void HandleTranslationUnit(ASTContext &Context) override {
252 for (unsigned I = 0; I < NewNames.size(); ++I) {
253 // FIXME: Apply AtomicChanges directly once the refactoring APIs are
254 // ready.
Alex Lorenz4abbd922017-06-30 16:36:09 +0000255 auto AtomicChanges = tooling::createRenameAtomicChanges(
Haojian Wu74f823a2017-04-04 09:30:06 +0000256 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000257 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000258 }
259 }
260
261private:
262 const std::vector<std::string> &NewNames;
263 const std::vector<std::vector<std::string>> &USRList;
264 std::map<std::string, tooling::Replacements> &FileToReplaces;
265};
266
Manuel Klimekde237262014-08-20 01:39:05 +0000267std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000268 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000269 FileToReplaces, PrintLocations);
Manuel Klimekde237262014-08-20 01:39:05 +0000270}
271
Haojian Wu74f823a2017-04-04 09:30:06 +0000272std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000273 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000274}
275
Alex Lorenz4abbd922017-06-30 16:36:09 +0000276} // end namespace tooling
277} // end namespace clang