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 = 174 (*FileToReplaces)[std::string(Replace.getFilePath())].add(Replace); 175 if (Err) { 176 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! " 177 << llvm::toString(std::move(Err)) << "\n"; 178 } 179 } 180 } 181 } 182 183 class RenamingASTConsumer : public ASTConsumer { 184 public: 185 RenamingASTConsumer( 186 const std::vector<std::string> &NewNames, 187 const std::vector<std::string> &PrevNames, 188 const std::vector<std::vector<std::string>> &USRList, 189 std::map<std::string, tooling::Replacements> &FileToReplaces, 190 bool PrintLocations) 191 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), 192 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} 193 194 void HandleTranslationUnit(ASTContext &Context) override { 195 for (unsigned I = 0; I < NewNames.size(); ++I) { 196 // If the previous name was not found, ignore this rename request. 197 if (PrevNames[I].empty()) 198 continue; 199 200 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); 201 } 202 } 203 204 void HandleOneRename(ASTContext &Context, const std::string &NewName, 205 const std::string &PrevName, 206 const std::vector<std::string> &USRs) { 207 const SourceManager &SourceMgr = Context.getSourceManager(); 208 209 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs( 210 USRs, PrevName, Context.getTranslationUnitDecl()); 211 if (PrintLocations) { 212 for (const auto &Occurrence : Occurrences) { 213 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(), 214 SourceMgr); 215 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc) 216 << ":" << FullLoc.getSpellingLineNumber() << ":" 217 << FullLoc.getSpellingColumnNumber() << "\n"; 218 } 219 } 220 // FIXME: Support multi-piece names. 221 // FIXME: better error handling (propagate error out). 222 SymbolName NewNameRef(NewName); 223 Expected<std::vector<AtomicChange>> Change = 224 createRenameReplacements(Occurrences, SourceMgr, NewNameRef); 225 if (!Change) { 226 llvm::errs() << "Failed to create renaming replacements for '" << PrevName 227 << "'! " << llvm::toString(Change.takeError()) << "\n"; 228 return; 229 } 230 convertChangesToFileReplacements(*Change, &FileToReplaces); 231 } 232 233 private: 234 const std::vector<std::string> &NewNames, &PrevNames; 235 const std::vector<std::vector<std::string>> &USRList; 236 std::map<std::string, tooling::Replacements> &FileToReplaces; 237 bool PrintLocations; 238 }; 239 240 // A renamer to rename symbols which are identified by a give USRList to 241 // new name. 242 // 243 // FIXME: Merge with the above RenamingASTConsumer. 244 class USRSymbolRenamer : public ASTConsumer { 245 public: 246 USRSymbolRenamer(const std::vector<std::string> &NewNames, 247 const std::vector<std::vector<std::string>> &USRList, 248 std::map<std::string, tooling::Replacements> &FileToReplaces) 249 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) { 250 assert(USRList.size() == NewNames.size()); 251 } 252 253 void HandleTranslationUnit(ASTContext &Context) override { 254 for (unsigned I = 0; I < NewNames.size(); ++I) { 255 // FIXME: Apply AtomicChanges directly once the refactoring APIs are 256 // ready. 257 auto AtomicChanges = tooling::createRenameAtomicChanges( 258 USRList[I], NewNames[I], Context.getTranslationUnitDecl()); 259 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces); 260 } 261 } 262 263 private: 264 const std::vector<std::string> &NewNames; 265 const std::vector<std::vector<std::string>> &USRList; 266 std::map<std::string, tooling::Replacements> &FileToReplaces; 267 }; 268 269 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() { 270 return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, 271 FileToReplaces, PrintLocations); 272 } 273 274 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() { 275 return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); 276 } 277 278 } // end namespace tooling 279 } // end namespace clang 280