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/RefactoringOptions.h"
27 #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
28 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
29 #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
30 #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
31 #include "clang/Tooling/Tooling.h"
32 #include "llvm/ADT/STLExtras.h"
33 #include <string>
34 #include <vector>
35 
36 using namespace llvm;
37 
38 namespace clang {
39 namespace tooling {
40 
41 namespace {
42 
43 class SymbolSelectionRequirement : public SourceRangeSelectionRequirement {
44 public:
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 
61 class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
62 public:
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 
74 private:
75   const NamedDecl *ND;
76 };
77 
78 class RenameOccurrences final : public SourceChangeRefactoringRule {
79 public:
80   RenameOccurrences(const NamedDecl *ND, std::string NewName)
81       : Finder(ND), NewName(NewName) {}
82 
83   Expected<AtomicChanges>
84   createSourceReplacements(RefactoringRuleContext &Context) {
85     Expected<SymbolOccurrences> Occurrences =
86         Finder.findSymbolOccurrences(Context);
87     if (!Occurrences)
88       return Occurrences.takeError();
89     // FIXME: Verify that the new name is valid.
90     SymbolName Name(NewName);
91     return createRenameReplacements(
92         *Occurrences, Context.getASTContext().getSourceManager(), Name);
93   }
94 
95 private:
96   OccurrenceFinder Finder;
97   std::string NewName;
98 };
99 
100 class LocalRename final : public RefactoringAction {
101 public:
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 {
111     RefactoringActionRules Rules;
112     Rules.push_back(createRefactoringActionRule<RenameOccurrences>(
113         SymbolSelectionRequirement(), OptionRequirement<NewNameOption>()));
114     return Rules;
115   }
116 };
117 
118 } // end anonymous namespace
119 
120 std::unique_ptr<RefactoringAction> createLocalRenameAction() {
121   return llvm::make_unique<LocalRename>();
122 }
123 
124 Expected<std::vector<AtomicChange>>
125 createRenameReplacements(const SymbolOccurrences &Occurrences,
126                          const SourceManager &SM, const SymbolName &NewName) {
127   // 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();
131     assert(NewName.getNamePieces().size() == Ranges.size() &&
132            "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()),
137                          NewName.getNamePieces()[Range.index()]);
138       if (Error)
139         return std::move(Error);
140     }
141     Changes.push_back(std::move(Change));
142   }
143   return std::move(Changes);
144 }
145 
146 /// Takes each atomic change and inserts its replacements into the set of
147 /// replacements that belong to the appropriate file.
148 static 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 
162 class RenamingASTConsumer : public ASTConsumer {
163 public:
164   RenamingASTConsumer(
165       const std::vector<std::string> &NewNames,
166       const std::vector<std::string> &PrevNames,
167       const std::vector<std::vector<std::string>> &USRList,
168       std::map<std::string, tooling::Replacements> &FileToReplaces,
169       bool PrintLocations)
170       : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
171         FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
172 
173   void HandleTranslationUnit(ASTContext &Context) override {
174     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 
179       HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
180     }
181   }
182 
183   void HandleOneRename(ASTContext &Context, const std::string &NewName,
184                        const std::string &PrevName,
185                        const std::vector<std::string> &USRs) {
186     const SourceManager &SourceMgr = Context.getSourceManager();
187 
188     SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
189         USRs, PrevName, Context.getTranslationUnitDecl());
190     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)
195                << ":" << FullLoc.getSpellingLineNumber() << ":"
196                << FullLoc.getSpellingColumnNumber() << "\n";
197       }
198     }
199     // FIXME: Support multi-piece names.
200     // FIXME: better error handling (propagate error out).
201     SymbolName NewNameRef(NewName);
202     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);
210   }
211 
212 private:
213   const std::vector<std::string> &NewNames, &PrevNames;
214   const std::vector<std::vector<std::string>> &USRList;
215   std::map<std::string, tooling::Replacements> &FileToReplaces;
216   bool PrintLocations;
217 };
218 
219 // A renamer to rename symbols which are identified by a give USRList to
220 // new name.
221 //
222 // FIXME: Merge with the above RenamingASTConsumer.
223 class USRSymbolRenamer : public ASTConsumer {
224 public:
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.
236       auto AtomicChanges = tooling::createRenameAtomicChanges(
237           USRList[I], NewNames[I], Context.getTranslationUnitDecl());
238       convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
239     }
240   }
241 
242 private:
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 
248 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
249   return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
250                                                 FileToReplaces, PrintLocations);
251 }
252 
253 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
254   return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
255 }
256 
257 } // end namespace tooling
258 } // end namespace clang
259