1 //===--- RenamingAction.cpp - Clang refactoring library -------------------===//
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
11 /// \brief Provides an action to rename every symbol at a point.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
16 #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"
21 #include "clang/Lex/Lexer.h"
22 #include "clang/Lex/Preprocessor.h"
23 #include "clang/Tooling/CommonOptionsParser.h"
24 #include "clang/Tooling/Refactoring.h"
25 #include "clang/Tooling/Refactoring/RefactoringAction.h"
26 #include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
27 #include "clang/Tooling/Refactoring/RefactoringOptions.h"
28 #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
29 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
30 #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
31 #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
32 #include "clang/Tooling/Tooling.h"
33 #include "llvm/ADT/STLExtras.h"
34 #include <string>
35 #include <vector>
36 
37 using namespace llvm;
38 
39 namespace clang {
40 namespace tooling {
41 
42 namespace {
43 
44 class SymbolSelectionRequirement : public SourceRangeSelectionRequirement {
45 public:
46   Expected<const NamedDecl *> evaluate(RefactoringRuleContext &Context) const {
47     Expected<SourceRange> Selection =
48         SourceRangeSelectionRequirement::evaluate(Context);
49     if (!Selection)
50       return Selection.takeError();
51     const NamedDecl *ND =
52         getNamedDeclAt(Context.getASTContext(), Selection->getBegin());
53     if (!ND)
54       return Context.createDiagnosticError(
55           Selection->getBegin(), diag::err_refactor_selection_no_symbol);
56     return getCanonicalSymbolDeclaration(ND);
57   }
58 };
59 
60 class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
61 public:
62   OccurrenceFinder(const NamedDecl *ND) : ND(ND) {}
63 
64   Expected<SymbolOccurrences>
65   findSymbolOccurrences(RefactoringRuleContext &Context) override {
66     std::vector<std::string> USRs =
67         getUSRsForDeclaration(ND, Context.getASTContext());
68     std::string PrevName = ND->getNameAsString();
69     return getOccurrencesOfUSRs(
70         USRs, PrevName, Context.getASTContext().getTranslationUnitDecl());
71   }
72 
73 private:
74   const NamedDecl *ND;
75 };
76 
77 class RenameOccurrences final : public SourceChangeRefactoringRule {
78 public:
79   RenameOccurrences(const NamedDecl *ND, std::string NewName)
80       : Finder(ND), NewName(std::move(NewName)) {}
81 
82   Expected<AtomicChanges>
83   createSourceReplacements(RefactoringRuleContext &Context) override {
84     Expected<SymbolOccurrences> Occurrences =
85         Finder.findSymbolOccurrences(Context);
86     if (!Occurrences)
87       return Occurrences.takeError();
88     // FIXME: Verify that the new name is valid.
89     SymbolName Name(NewName);
90     return createRenameReplacements(
91         *Occurrences, Context.getASTContext().getSourceManager(), Name);
92   }
93 
94 private:
95   OccurrenceFinder Finder;
96   std::string NewName;
97 };
98 
99 class LocalRename final : public RefactoringAction {
100 public:
101   StringRef getCommand() const override { return "local-rename"; }
102 
103   StringRef getDescription() const override {
104     return "Finds and renames symbols in code with no indexer support";
105   }
106 
107   /// Returns a set of refactoring actions rules that are defined by this
108   /// action.
109   RefactoringActionRules createActionRules() const override {
110     RefactoringActionRules Rules;
111     Rules.push_back(createRefactoringActionRule<RenameOccurrences>(
112         SymbolSelectionRequirement(), OptionRequirement<NewNameOption>()));
113     return Rules;
114   }
115 };
116 
117 } // end anonymous namespace
118 
119 std::unique_ptr<RefactoringAction> createLocalRenameAction() {
120   return llvm::make_unique<LocalRename>();
121 }
122 
123 Expected<std::vector<AtomicChange>>
124 createRenameReplacements(const SymbolOccurrences &Occurrences,
125                          const SourceManager &SM, const SymbolName &NewName) {
126   // FIXME: A true local rename can use just one AtomicChange.
127   std::vector<AtomicChange> Changes;
128   for (const auto &Occurrence : Occurrences) {
129     ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
130     assert(NewName.getNamePieces().size() == Ranges.size() &&
131            "Mismatching number of ranges and name pieces");
132     AtomicChange Change(SM, Ranges[0].getBegin());
133     for (const auto &Range : llvm::enumerate(Ranges)) {
134       auto Error =
135           Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
136                          NewName.getNamePieces()[Range.index()]);
137       if (Error)
138         return std::move(Error);
139     }
140     Changes.push_back(std::move(Change));
141   }
142   return std::move(Changes);
143 }
144 
145 /// Takes each atomic change and inserts its replacements into the set of
146 /// replacements that belong to the appropriate file.
147 static void convertChangesToFileReplacements(
148     ArrayRef<AtomicChange> AtomicChanges,
149     std::map<std::string, tooling::Replacements> *FileToReplaces) {
150   for (const auto &AtomicChange : AtomicChanges) {
151     for (const auto &Replace : AtomicChange.getReplacements()) {
152       llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
153       if (Err) {
154         llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
155                      << llvm::toString(std::move(Err)) << "\n";
156       }
157     }
158   }
159 }
160 
161 class RenamingASTConsumer : public ASTConsumer {
162 public:
163   RenamingASTConsumer(
164       const std::vector<std::string> &NewNames,
165       const std::vector<std::string> &PrevNames,
166       const std::vector<std::vector<std::string>> &USRList,
167       std::map<std::string, tooling::Replacements> &FileToReplaces,
168       bool PrintLocations)
169       : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
170         FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
171 
172   void HandleTranslationUnit(ASTContext &Context) override {
173     for (unsigned I = 0; I < NewNames.size(); ++I) {
174       // If the previous name was not found, ignore this rename request.
175       if (PrevNames[I].empty())
176         continue;
177 
178       HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
179     }
180   }
181 
182   void HandleOneRename(ASTContext &Context, const std::string &NewName,
183                        const std::string &PrevName,
184                        const std::vector<std::string> &USRs) {
185     const SourceManager &SourceMgr = Context.getSourceManager();
186 
187     SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
188         USRs, PrevName, Context.getTranslationUnitDecl());
189     if (PrintLocations) {
190       for (const auto &Occurrence : Occurrences) {
191         FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
192                               SourceMgr);
193         errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
194                << ":" << FullLoc.getSpellingLineNumber() << ":"
195                << FullLoc.getSpellingColumnNumber() << "\n";
196       }
197     }
198     // FIXME: Support multi-piece names.
199     // FIXME: better error handling (propagate error out).
200     SymbolName NewNameRef(NewName);
201     Expected<std::vector<AtomicChange>> Change =
202         createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
203     if (!Change) {
204       llvm::errs() << "Failed to create renaming replacements for '" << PrevName
205                    << "'! " << llvm::toString(Change.takeError()) << "\n";
206       return;
207     }
208     convertChangesToFileReplacements(*Change, &FileToReplaces);
209   }
210 
211 private:
212   const std::vector<std::string> &NewNames, &PrevNames;
213   const std::vector<std::vector<std::string>> &USRList;
214   std::map<std::string, tooling::Replacements> &FileToReplaces;
215   bool PrintLocations;
216 };
217 
218 // A renamer to rename symbols which are identified by a give USRList to
219 // new name.
220 //
221 // FIXME: Merge with the above RenamingASTConsumer.
222 class USRSymbolRenamer : public ASTConsumer {
223 public:
224   USRSymbolRenamer(const std::vector<std::string> &NewNames,
225                    const std::vector<std::vector<std::string>> &USRList,
226                    std::map<std::string, tooling::Replacements> &FileToReplaces)
227       : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
228     assert(USRList.size() == NewNames.size());
229   }
230 
231   void HandleTranslationUnit(ASTContext &Context) override {
232     for (unsigned I = 0; I < NewNames.size(); ++I) {
233       // FIXME: Apply AtomicChanges directly once the refactoring APIs are
234       // ready.
235       auto AtomicChanges = tooling::createRenameAtomicChanges(
236           USRList[I], NewNames[I], Context.getTranslationUnitDecl());
237       convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
238     }
239   }
240 
241 private:
242   const std::vector<std::string> &NewNames;
243   const std::vector<std::vector<std::string>> &USRList;
244   std::map<std::string, tooling::Replacements> &FileToReplaces;
245 };
246 
247 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
248   return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
249                                                 FileToReplaces, PrintLocations);
250 }
251 
252 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
253   return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
254 }
255 
256 } // end namespace tooling
257 } // end namespace clang
258