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