blob: 547d7bb6bcdceaf1e84a5a862a6a015b474c95cd [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 Lorenzad38fbf2017-10-13 01:53:13 +000026#include "clang/Tooling/Refactoring/RefactoringOptions.h"
27#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000028#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
29#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
Alex Lorenz4abbd922017-06-30 16:36:09 +000030#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
Manuel Klimekde237262014-08-20 01:39:05 +000031#include "clang/Tooling/Tooling.h"
Alex Lorenz44b94c72017-08-14 16:19:24 +000032#include "llvm/ADT/STLExtras.h"
Manuel Klimekde237262014-08-20 01:39:05 +000033#include <string>
34#include <vector>
35
36using namespace llvm;
37
38namespace clang {
Alex Lorenz4abbd922017-06-30 16:36:09 +000039namespace tooling {
Manuel Klimekde237262014-08-20 01:39:05 +000040
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000041namespace {
42
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000043class SymbolSelectionRequirement : public SourceRangeSelectionRequirement {
44public:
45 Expected<const NamedDecl *> evaluate(RefactoringRuleContext &Context) const {
46 Expected<SourceRange> Selection =
47 SourceRangeSelectionRequirement::evaluate(Context);
48 if (!Selection)
49 return Selection.takeError();
50 const NamedDecl *ND =
51 getNamedDeclAt(Context.getASTContext(), Selection->getBegin());
52 if (!ND) {
53 // FIXME: Use a diagnostic.
54 return llvm::make_error<StringError>("no symbol selected",
55 llvm::inconvertibleErrorCode());
56 }
57 return getCanonicalSymbolDeclaration(ND);
58 }
59};
60
61class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
62public:
63 OccurrenceFinder(const NamedDecl *ND) : ND(ND) {}
64
65 Expected<SymbolOccurrences>
66 findSymbolOccurrences(RefactoringRuleContext &Context) override {
67 std::vector<std::string> USRs =
68 getUSRsForDeclaration(ND, Context.getASTContext());
69 std::string PrevName = ND->getNameAsString();
70 return getOccurrencesOfUSRs(
71 USRs, PrevName, Context.getASTContext().getTranslationUnitDecl());
72 }
73
74private:
75 const NamedDecl *ND;
76};
77
78class RenameOccurrences final : public SourceChangeRefactoringRule {
79public:
Alex Lorenzad38fbf2017-10-13 01:53:13 +000080 RenameOccurrences(const NamedDecl *ND, std::string NewName)
81 : Finder(ND), NewName(NewName) {}
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000082
83 Expected<AtomicChanges>
84 createSourceReplacements(RefactoringRuleContext &Context) {
85 Expected<SymbolOccurrences> Occurrences =
86 Finder.findSymbolOccurrences(Context);
87 if (!Occurrences)
88 return Occurrences.takeError();
Alex Lorenzad38fbf2017-10-13 01:53:13 +000089 // FIXME: Verify that the new name is valid.
90 SymbolName Name(NewName);
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000091 return createRenameReplacements(
Alex Lorenzad38fbf2017-10-13 01:53:13 +000092 *Occurrences, Context.getASTContext().getSourceManager(), Name);
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000093 }
94
95private:
96 OccurrenceFinder Finder;
Alex Lorenzad38fbf2017-10-13 01:53:13 +000097 std::string NewName;
Alex Lorenz1c0a26b2017-10-02 18:42:43 +000098};
99
100class LocalRename final : public RefactoringAction {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000101public:
102 StringRef getCommand() const override { return "local-rename"; }
103
104 StringRef getDescription() const override {
105 return "Finds and renames symbols in code with no indexer support";
106 }
107
108 /// Returns a set of refactoring actions rules that are defined by this
109 /// action.
110 RefactoringActionRules createActionRules() const override {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000111 RefactoringActionRules Rules;
Alex Lorenz1c0a26b2017-10-02 18:42:43 +0000112 Rules.push_back(createRefactoringActionRule<RenameOccurrences>(
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000113 SymbolSelectionRequirement(), OptionRequirement<NewNameOption>()));
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000114 return Rules;
115 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000116};
117
118} // end anonymous namespace
119
120std::unique_ptr<RefactoringAction> createLocalRenameAction() {
121 return llvm::make_unique<LocalRename>();
122}
123
Alex Lorenz44b94c72017-08-14 16:19:24 +0000124Expected<std::vector<AtomicChange>>
125createRenameReplacements(const SymbolOccurrences &Occurrences,
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000126 const SourceManager &SM, const SymbolName &NewName) {
Alex Lorenz44b94c72017-08-14 16:19:24 +0000127 // FIXME: A true local rename can use just one AtomicChange.
128 std::vector<AtomicChange> Changes;
129 for (const auto &Occurrence : Occurrences) {
130 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000131 assert(NewName.getNamePieces().size() == Ranges.size() &&
Alex Lorenz44b94c72017-08-14 16:19:24 +0000132 "Mismatching number of ranges and name pieces");
133 AtomicChange Change(SM, Ranges[0].getBegin());
134 for (const auto &Range : llvm::enumerate(Ranges)) {
135 auto Error =
136 Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000137 NewName.getNamePieces()[Range.index()]);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000138 if (Error)
139 return std::move(Error);
140 }
141 Changes.push_back(std::move(Change));
142 }
Alexander Shaposhnikovab0400d2017-08-15 19:23:54 +0000143 return std::move(Changes);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000144}
145
146/// Takes each atomic change and inserts its replacements into the set of
147/// replacements that belong to the appropriate file.
148static void convertChangesToFileReplacements(
149 ArrayRef<AtomicChange> AtomicChanges,
150 std::map<std::string, tooling::Replacements> *FileToReplaces) {
151 for (const auto &AtomicChange : AtomicChanges) {
152 for (const auto &Replace : AtomicChange.getReplacements()) {
153 llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
154 if (Err) {
155 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
156 << llvm::toString(std::move(Err)) << "\n";
157 }
158 }
159 }
160}
161
Manuel Klimekde237262014-08-20 01:39:05 +0000162class RenamingASTConsumer : public ASTConsumer {
163public:
Eric Liu267034c2016-08-01 10:16:39 +0000164 RenamingASTConsumer(
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000165 const std::vector<std::string> &NewNames,
166 const std::vector<std::string> &PrevNames,
167 const std::vector<std::vector<std::string>> &USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000168 std::map<std::string, tooling::Replacements> &FileToReplaces,
169 bool PrintLocations)
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000170 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
Eric Liu267034c2016-08-01 10:16:39 +0000171 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
Manuel Klimekde237262014-08-20 01:39:05 +0000172
173 void HandleTranslationUnit(ASTContext &Context) override {
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000174 for (unsigned I = 0; I < NewNames.size(); ++I) {
175 // If the previous name was not found, ignore this rename request.
176 if (PrevNames[I].empty())
177 continue;
178
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000179 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
Miklos Vajnabf0d49c42017-09-11 20:18:38 +0000180 }
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000181 }
182
183 void HandleOneRename(ASTContext &Context, const std::string &NewName,
184 const std::string &PrevName,
185 const std::vector<std::string> &USRs) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +0000186 const SourceManager &SourceMgr = Context.getSourceManager();
Manuel Klimekde237262014-08-20 01:39:05 +0000187
Alex Lorenz44b94c72017-08-14 16:19:24 +0000188 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
Alex Lorenz4abbd922017-06-30 16:36:09 +0000189 USRs, PrevName, Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000190 if (PrintLocations) {
191 for (const auto &Occurrence : Occurrences) {
192 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
193 SourceMgr);
194 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
Manuel Klimekde237262014-08-20 01:39:05 +0000195 << ":" << FullLoc.getSpellingLineNumber() << ":"
196 << FullLoc.getSpellingColumnNumber() << "\n";
Manuel Klimekde237262014-08-20 01:39:05 +0000197 }
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000198 }
Alex Lorenz44b94c72017-08-14 16:19:24 +0000199 // FIXME: Support multi-piece names.
200 // FIXME: better error handling (propagate error out).
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000201 SymbolName NewNameRef(NewName);
Alex Lorenz44b94c72017-08-14 16:19:24 +0000202 Expected<std::vector<AtomicChange>> Change =
203 createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
204 if (!Change) {
205 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
206 << "'! " << llvm::toString(Change.takeError()) << "\n";
207 return;
208 }
209 convertChangesToFileReplacements(*Change, &FileToReplaces);
Manuel Klimekde237262014-08-20 01:39:05 +0000210 }
211
212private:
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000213 const std::vector<std::string> &NewNames, &PrevNames;
214 const std::vector<std::vector<std::string>> &USRList;
Eric Liu267034c2016-08-01 10:16:39 +0000215 std::map<std::string, tooling::Replacements> &FileToReplaces;
Manuel Klimekde237262014-08-20 01:39:05 +0000216 bool PrintLocations;
217};
218
Haojian Wu74f823a2017-04-04 09:30:06 +0000219// A renamer to rename symbols which are identified by a give USRList to
220// new name.
221//
222// FIXME: Merge with the above RenamingASTConsumer.
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000223class USRSymbolRenamer : public ASTConsumer {
Haojian Wu74f823a2017-04-04 09:30:06 +0000224public:
225 USRSymbolRenamer(const std::vector<std::string> &NewNames,
226 const std::vector<std::vector<std::string>> &USRList,
227 std::map<std::string, tooling::Replacements> &FileToReplaces)
228 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
229 assert(USRList.size() == NewNames.size());
230 }
231
232 void HandleTranslationUnit(ASTContext &Context) override {
233 for (unsigned I = 0; I < NewNames.size(); ++I) {
234 // FIXME: Apply AtomicChanges directly once the refactoring APIs are
235 // ready.
Alex Lorenz4abbd922017-06-30 16:36:09 +0000236 auto AtomicChanges = tooling::createRenameAtomicChanges(
Haojian Wu74f823a2017-04-04 09:30:06 +0000237 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
Alex Lorenz44b94c72017-08-14 16:19:24 +0000238 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000239 }
240 }
241
242private:
243 const std::vector<std::string> &NewNames;
244 const std::vector<std::vector<std::string>> &USRList;
245 std::map<std::string, tooling::Replacements> &FileToReplaces;
246};
247
Manuel Klimekde237262014-08-20 01:39:05 +0000248std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000249 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
Eric Liu267034c2016-08-01 10:16:39 +0000250 FileToReplaces, PrintLocations);
Manuel Klimekde237262014-08-20 01:39:05 +0000251}
252
Haojian Wu74f823a2017-04-04 09:30:06 +0000253std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000254 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
Haojian Wu74f823a2017-04-04 09:30:06 +0000255}
256
Alex Lorenz4abbd922017-06-30 16:36:09 +0000257} // end namespace tooling
258} // end namespace clang