blob: 695fa553b452ee22b76af67e384b5112791ad8e9 [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
11/// \brief Provides an action to rename every symbol at a point.
12///
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"
Manuel Klimekde237262014-08-20 01:39:05 +000034#include <string>
35#include <vector>
36
37using namespace llvm;
38
39namespace clang {
Alex Lorenz4abbd922017-06-30 16:36:09 +000040namespace tooling {
Manuel Klimekde237262014-08-20 01:39:05 +000041
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000042namespace {
43
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000044class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
45public:
46 OccurrenceFinder(const NamedDecl *ND) : ND(ND) {}
47
48 Expected<SymbolOccurrences>
49 findSymbolOccurrences(RefactoringRuleContext &Context) override {
50 std::vector<std::string> USRs =
51 getUSRsForDeclaration(ND, Context.getASTContext());
52 std::string PrevName = ND->getNameAsString();
53 return getOccurrencesOfUSRs(
54 USRs, PrevName, Context.getASTContext().getTranslationUnitDecl());
55 }
56
57private:
58 const NamedDecl *ND;
59};
60
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000061} // end anonymous namespace
62
Alex Lorenz0beca4d2017-10-27 18:19:11 +000063const RefactoringDescriptor &RenameOccurrences::describe() {
64 static const RefactoringDescriptor Descriptor = {
65 "local-rename",
66 "Rename",
67 "Finds and renames symbols in code with no indexer support",
68 };
69 return Descriptor;
70}
71
72Expected<RenameOccurrences>
73RenameOccurrences::initiate(RefactoringRuleContext &Context,
74 SourceRange SelectionRange, std::string NewName) {
75 const NamedDecl *ND =
76 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());
77 if (!ND)
78 return Context.createDiagnosticError(
79 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);
Haojian Wuf5142bf2017-10-30 11:17:09 +000080 return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
81 std::move(NewName));
Alex Lorenz0beca4d2017-10-27 18:19:11 +000082}
83
84Expected<AtomicChanges>
85RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
86 Expected<SymbolOccurrences> Occurrences =
87 OccurrenceFinder(ND).findSymbolOccurrences(Context);
88 if (!Occurrences)
89 return Occurrences.takeError();
90 // FIXME: Verify that the new name is valid.
91 SymbolName Name(NewName);
92 return createRenameReplacements(
93 *Occurrences, Context.getASTContext().getSourceManager(), Name);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000094}
95
Alex Lorenz44b94c72017-08-14 16:19:24 +000096Expected<std::vector<AtomicChange>>
97createRenameReplacements(const SymbolOccurrences &Occurrences,
Alex Lorenzad38fbf2017-10-13 01:53:13 +000098 const SourceManager &SM, const SymbolName &NewName) {
Alex Lorenz44b94c72017-08-14 16:19:24 +000099 // FIXME: A true local rename can use just one AtomicChange.
100 std::vector<AtomicChange> Changes;
101 for (const auto &Occurrence : Occurrences) {
102 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000103 assert(NewName.getNamePieces().size() == Ranges.size() &&
Alex Lorenz44b94c72017-08-14 16:19:24 +0000104 "Mismatching number of ranges and name pieces");
105 AtomicChange Change(SM, Ranges[0].getBegin());
106 for (const auto &Range : llvm::enumerate(Ranges)) {
107 auto Error =
108 Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000109 NewName.getNamePieces()[Range.index()]);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000110 if (Error)
111 return std::move(Error);
112 }
113 Changes.push_back(std::move(Change));
114 }
Alexander Shaposhnikovab0400d2017-08-15 19:23:54 +0000115 return std::move(Changes);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000116}
117
118/// Takes each atomic change and inserts its replacements into the set of
119/// replacements that belong to the appropriate file.
120static void convertChangesToFileReplacements(
121 ArrayRef<AtomicChange> AtomicChanges,
122 std::map<std::string, tooling::Replacements> *FileToReplaces) {
123 for (const auto &AtomicChange : AtomicChanges) {
124 for (const auto &Replace : AtomicChange.getReplacements()) {
125 llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
126 if (Err) {
127 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
128 << llvm::toString(std::move(Err)) << "\n";
129 }
130 }
131 }
132}
133
Manuel Klimekde237262014-08-20 01:39:05 +0000134class RenamingASTConsumer : public ASTConsumer {
135public:
Eric Liu267034c2016-08-01 10:16:39 +0000136 RenamingASTConsumer(
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000137 const std::vector<std::string> &NewNames,
138 const std::vector<std::string> &PrevNames,
139 const std::vector<std::vector<std::string>> &USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000140 std::map<std::string, tooling::Replacements> &FileToReplaces,
141 bool PrintLocations)
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000142 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
Eric Liu267034c2016-08-01 10:16:39 +0000143 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
Manuel Klimekde237262014-08-20 01:39:05 +0000144
145 void HandleTranslationUnit(ASTContext &Context) override {
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000146 for (unsigned I = 0; I < NewNames.size(); ++I) {
147 // If the previous name was not found, ignore this rename request.
148 if (PrevNames[I].empty())
149 continue;
150
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000151 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000152 }
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000153 }
154
155 void HandleOneRename(ASTContext &Context, const std::string &NewName,
156 const std::string &PrevName,
157 const std::vector<std::string> &USRs) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000158 const SourceManager &SourceMgr = Context.getSourceManager();
Manuel Klimekde237262014-08-20 01:39:05 +0000159
Alex Lorenz44b94c72017-08-14 16:19:24 +0000160 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
Alex Lorenz4abbd922017-06-30 16:36:09 +0000161 USRs, PrevName, Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000162 if (PrintLocations) {
163 for (const auto &Occurrence : Occurrences) {
164 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
165 SourceMgr);
166 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
Manuel Klimekde237262014-08-20 01:39:05 +0000167 << ":" << FullLoc.getSpellingLineNumber() << ":"
168 << FullLoc.getSpellingColumnNumber() << "\n";
Manuel Klimekde237262014-08-20 01:39:05 +0000169 }
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000170 }
Alex Lorenz44b94c72017-08-14 16:19:24 +0000171 // FIXME: Support multi-piece names.
172 // FIXME: better error handling (propagate error out).
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000173 SymbolName NewNameRef(NewName);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000174 Expected<std::vector<AtomicChange>> Change =
175 createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
176 if (!Change) {
177 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
178 << "'! " << llvm::toString(Change.takeError()) << "\n";
179 return;
180 }
181 convertChangesToFileReplacements(*Change, &FileToReplaces);
Manuel Klimekde237262014-08-20 01:39:05 +0000182 }
183
184private:
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000185 const std::vector<std::string> &NewNames, &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;
Manuel Klimekde237262014-08-20 01:39:05 +0000188 bool PrintLocations;
189};
190
Haojian Wu74f823a2017-04-04 09:30:06 +0000191// A renamer to rename symbols which are identified by a give USRList to
192// new name.
193//
194// FIXME: Merge with the above RenamingASTConsumer.
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000195class USRSymbolRenamer : public ASTConsumer {
Haojian Wu74f823a2017-04-04 09:30:06 +0000196public:
197 USRSymbolRenamer(const std::vector<std::string> &NewNames,
198 const std::vector<std::vector<std::string>> &USRList,
199 std::map<std::string, tooling::Replacements> &FileToReplaces)
200 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
201 assert(USRList.size() == NewNames.size());
202 }
203
204 void HandleTranslationUnit(ASTContext &Context) override {
205 for (unsigned I = 0; I < NewNames.size(); ++I) {
206 // FIXME: Apply AtomicChanges directly once the refactoring APIs are
207 // ready.
Alex Lorenz4abbd922017-06-30 16:36:09 +0000208 auto AtomicChanges = tooling::createRenameAtomicChanges(
Haojian Wu74f823a2017-04-04 09:30:06 +0000209 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000210 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000211 }
212 }
213
214private:
215 const std::vector<std::string> &NewNames;
216 const std::vector<std::vector<std::string>> &USRList;
217 std::map<std::string, tooling::Replacements> &FileToReplaces;
218};
219
Manuel Klimekde237262014-08-20 01:39:05 +0000220std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000221 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000222 FileToReplaces, PrintLocations);
Manuel Klimekde237262014-08-20 01:39:05 +0000223}
224
Haojian Wu74f823a2017-04-04 09:30:06 +0000225std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000226 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000227}
228
Alex Lorenz4abbd922017-06-30 16:36:09 +0000229} // end namespace tooling
230} // end namespace clang