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(NewName) {} 81 82 Expected<AtomicChanges> 83 createSourceReplacements(RefactoringRuleContext &Context) { 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