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