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