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 OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
45 public:
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 
57 private:
58   const NamedDecl *ND;
59 };
60 
61 } // end anonymous namespace
62 
63 const 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 
72 Expected<RenameOccurrences>
73 RenameOccurrences::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);
80   return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
81                            std::move(NewName));
82 }
83 
84 Expected<AtomicChanges>
85 RenameOccurrences::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);
94 }
95 
96 Expected<std::vector<AtomicChange>>
97 createRenameReplacements(const SymbolOccurrences &Occurrences,
98                          const SourceManager &SM, const SymbolName &NewName) {
99   // 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();
103     assert(NewName.getNamePieces().size() == Ranges.size() &&
104            "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()),
109                          NewName.getNamePieces()[Range.index()]);
110       if (Error)
111         return std::move(Error);
112     }
113     Changes.push_back(std::move(Change));
114   }
115   return std::move(Changes);
116 }
117 
118 /// Takes each atomic change and inserts its replacements into the set of
119 /// replacements that belong to the appropriate file.
120 static 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 
134 class RenamingASTConsumer : public ASTConsumer {
135 public:
136   RenamingASTConsumer(
137       const std::vector<std::string> &NewNames,
138       const std::vector<std::string> &PrevNames,
139       const std::vector<std::vector<std::string>> &USRList,
140       std::map<std::string, tooling::Replacements> &FileToReplaces,
141       bool PrintLocations)
142       : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
143         FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
144 
145   void HandleTranslationUnit(ASTContext &Context) override {
146     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 
151       HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
152     }
153   }
154 
155   void HandleOneRename(ASTContext &Context, const std::string &NewName,
156                        const std::string &PrevName,
157                        const std::vector<std::string> &USRs) {
158     const SourceManager &SourceMgr = Context.getSourceManager();
159 
160     SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
161         USRs, PrevName, Context.getTranslationUnitDecl());
162     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)
167                << ":" << FullLoc.getSpellingLineNumber() << ":"
168                << FullLoc.getSpellingColumnNumber() << "\n";
169       }
170     }
171     // FIXME: Support multi-piece names.
172     // FIXME: better error handling (propagate error out).
173     SymbolName NewNameRef(NewName);
174     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);
182   }
183 
184 private:
185   const std::vector<std::string> &NewNames, &PrevNames;
186   const std::vector<std::vector<std::string>> &USRList;
187   std::map<std::string, tooling::Replacements> &FileToReplaces;
188   bool PrintLocations;
189 };
190 
191 // A renamer to rename symbols which are identified by a give USRList to
192 // new name.
193 //
194 // FIXME: Merge with the above RenamingASTConsumer.
195 class USRSymbolRenamer : public ASTConsumer {
196 public:
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.
208       auto AtomicChanges = tooling::createRenameAtomicChanges(
209           USRList[I], NewNames[I], Context.getTranslationUnitDecl());
210       convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
211     }
212   }
213 
214 private:
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 
220 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
221   return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
222                                                 FileToReplaces, PrintLocations);
223 }
224 
225 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
226   return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
227 }
228 
229 } // end namespace tooling
230 } // end namespace clang
231