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 /// 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 "llvm/Support/Errc.h" 35 #include "llvm/Support/Error.h" 36 #include <string> 37 #include <vector> 38 39 using namespace llvm; 40 41 namespace clang { 42 namespace tooling { 43 44 namespace { 45 46 Expected<SymbolOccurrences> 47 findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) { 48 std::vector<std::string> USRs = 49 getUSRsForDeclaration(ND, Context.getASTContext()); 50 std::string PrevName = ND->getNameAsString(); 51 return getOccurrencesOfUSRs(USRs, PrevName, 52 Context.getASTContext().getTranslationUnitDecl()); 53 } 54 55 } // end anonymous namespace 56 57 const RefactoringDescriptor &RenameOccurrences::describe() { 58 static const RefactoringDescriptor Descriptor = { 59 "local-rename", 60 "Rename", 61 "Finds and renames symbols in code with no indexer support", 62 }; 63 return Descriptor; 64 } 65 66 Expected<RenameOccurrences> 67 RenameOccurrences::initiate(RefactoringRuleContext &Context, 68 SourceRange SelectionRange, std::string NewName) { 69 const NamedDecl *ND = 70 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin()); 71 if (!ND) 72 return Context.createDiagnosticError( 73 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol); 74 return RenameOccurrences(getCanonicalSymbolDeclaration(ND), 75 std::move(NewName)); 76 } 77 78 Expected<AtomicChanges> 79 RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) { 80 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context); 81 if (!Occurrences) 82 return Occurrences.takeError(); 83 // FIXME: Verify that the new name is valid. 84 SymbolName Name(NewName); 85 return createRenameReplacements( 86 *Occurrences, Context.getASTContext().getSourceManager(), Name); 87 } 88 89 Expected<QualifiedRenameRule> 90 QualifiedRenameRule::initiate(RefactoringRuleContext &Context, 91 std::string OldQualifiedName, 92 std::string NewQualifiedName) { 93 const NamedDecl *ND = 94 getNamedDeclFor(Context.getASTContext(), OldQualifiedName); 95 if (!ND) 96 return llvm::make_error<llvm::StringError>("Could not find symbol " + 97 OldQualifiedName, 98 llvm::errc::invalid_argument); 99 return QualifiedRenameRule(ND, std::move(NewQualifiedName)); 100 } 101 102 const RefactoringDescriptor &QualifiedRenameRule::describe() { 103 static const RefactoringDescriptor Descriptor = { 104 /*Name=*/"local-qualified-rename", 105 /*Title=*/"Qualified Rename", 106 /*Description=*/ 107 R"(Finds and renames qualified symbols in code within a translation unit. 108 It is used to move/rename a symbol to a new namespace/name: 109 * Supported symbols: classes, class members, functions, enums, and type alias. 110 * Renames all symbol occurrences from the old qualified name to the new 111 qualified name. All symbol references will be correctly qualified; For 112 symbol definitions, only name will be changed. 113 For example, rename "A::Foo" to "B::Bar": 114 Old code: 115 namespace foo { 116 class A {}; 117 } 118 119 namespace bar { 120 void f(foo::A a) {} 121 } 122 123 New code after rename: 124 namespace foo { 125 class B {}; 126 } 127 128 namespace bar { 129 void f(B b) {} 130 })" 131 }; 132 return Descriptor; 133 } 134 135 Expected<AtomicChanges> 136 QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) { 137 auto USRs = getUSRsForDeclaration(ND, Context.getASTContext()); 138 assert(!USRs.empty()); 139 return tooling::createRenameAtomicChanges( 140 USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl()); 141 } 142 143 Expected<std::vector<AtomicChange>> 144 createRenameReplacements(const SymbolOccurrences &Occurrences, 145 const SourceManager &SM, const SymbolName &NewName) { 146 // FIXME: A true local rename can use just one AtomicChange. 147 std::vector<AtomicChange> Changes; 148 for (const auto &Occurrence : Occurrences) { 149 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges(); 150 assert(NewName.getNamePieces().size() == Ranges.size() && 151 "Mismatching number of ranges and name pieces"); 152 AtomicChange Change(SM, Ranges[0].getBegin()); 153 for (const auto &Range : llvm::enumerate(Ranges)) { 154 auto Error = 155 Change.replace(SM, CharSourceRange::getCharRange(Range.value()), 156 NewName.getNamePieces()[Range.index()]); 157 if (Error) 158 return std::move(Error); 159 } 160 Changes.push_back(std::move(Change)); 161 } 162 return std::move(Changes); 163 } 164 165 /// Takes each atomic change and inserts its replacements into the set of 166 /// replacements that belong to the appropriate file. 167 static void convertChangesToFileReplacements( 168 ArrayRef<AtomicChange> AtomicChanges, 169 std::map<std::string, tooling::Replacements> *FileToReplaces) { 170 for (const auto &AtomicChange : AtomicChanges) { 171 for (const auto &Replace : AtomicChange.getReplacements()) { 172 llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace); 173 if (Err) { 174 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! " 175 << llvm::toString(std::move(Err)) << "\n"; 176 } 177 } 178 } 179 } 180 181 class RenamingASTConsumer : public ASTConsumer { 182 public: 183 RenamingASTConsumer( 184 const std::vector<std::string> &NewNames, 185 const std::vector<std::string> &PrevNames, 186 const std::vector<std::vector<std::string>> &USRList, 187 std::map<std::string, tooling::Replacements> &FileToReplaces, 188 bool PrintLocations) 189 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), 190 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} 191 192 void HandleTranslationUnit(ASTContext &Context) override { 193 for (unsigned I = 0; I < NewNames.size(); ++I) { 194 // If the previous name was not found, ignore this rename request. 195 if (PrevNames[I].empty()) 196 continue; 197 198 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); 199 } 200 } 201 202 void HandleOneRename(ASTContext &Context, const std::string &NewName, 203 const std::string &PrevName, 204 const std::vector<std::string> &USRs) { 205 const SourceManager &SourceMgr = Context.getSourceManager(); 206 207 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs( 208 USRs, PrevName, Context.getTranslationUnitDecl()); 209 if (PrintLocations) { 210 for (const auto &Occurrence : Occurrences) { 211 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(), 212 SourceMgr); 213 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc) 214 << ":" << FullLoc.getSpellingLineNumber() << ":" 215 << FullLoc.getSpellingColumnNumber() << "\n"; 216 } 217 } 218 // FIXME: Support multi-piece names. 219 // FIXME: better error handling (propagate error out). 220 SymbolName NewNameRef(NewName); 221 Expected<std::vector<AtomicChange>> Change = 222 createRenameReplacements(Occurrences, SourceMgr, NewNameRef); 223 if (!Change) { 224 llvm::errs() << "Failed to create renaming replacements for '" << PrevName 225 << "'! " << llvm::toString(Change.takeError()) << "\n"; 226 return; 227 } 228 convertChangesToFileReplacements(*Change, &FileToReplaces); 229 } 230 231 private: 232 const std::vector<std::string> &NewNames, &PrevNames; 233 const std::vector<std::vector<std::string>> &USRList; 234 std::map<std::string, tooling::Replacements> &FileToReplaces; 235 bool PrintLocations; 236 }; 237 238 // A renamer to rename symbols which are identified by a give USRList to 239 // new name. 240 // 241 // FIXME: Merge with the above RenamingASTConsumer. 242 class USRSymbolRenamer : public ASTConsumer { 243 public: 244 USRSymbolRenamer(const std::vector<std::string> &NewNames, 245 const std::vector<std::vector<std::string>> &USRList, 246 std::map<std::string, tooling::Replacements> &FileToReplaces) 247 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) { 248 assert(USRList.size() == NewNames.size()); 249 } 250 251 void HandleTranslationUnit(ASTContext &Context) override { 252 for (unsigned I = 0; I < NewNames.size(); ++I) { 253 // FIXME: Apply AtomicChanges directly once the refactoring APIs are 254 // ready. 255 auto AtomicChanges = tooling::createRenameAtomicChanges( 256 USRList[I], NewNames[I], Context.getTranslationUnitDecl()); 257 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces); 258 } 259 } 260 261 private: 262 const std::vector<std::string> &NewNames; 263 const std::vector<std::vector<std::string>> &USRList; 264 std::map<std::string, tooling::Replacements> &FileToReplaces; 265 }; 266 267 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() { 268 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, 269 FileToReplaces, PrintLocations); 270 } 271 272 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() { 273 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); 274 } 275 276 } // end namespace tooling 277 } // end namespace clang 278