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