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