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/Rename/USRLocFinder.h" 26 #include "clang/Tooling/Tooling.h" 27 #include "llvm/ADT/STLExtras.h" 28 #include <string> 29 #include <vector> 30 31 using namespace llvm; 32 33 namespace clang { 34 namespace tooling { 35 36 Expected<std::vector<AtomicChange>> 37 createRenameReplacements(const SymbolOccurrences &Occurrences, 38 const SourceManager &SM, 39 ArrayRef<StringRef> NewNameStrings) { 40 // FIXME: A true local rename can use just one AtomicChange. 41 std::vector<AtomicChange> Changes; 42 for (const auto &Occurrence : Occurrences) { 43 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges(); 44 assert(NewNameStrings.size() == Ranges.size() && 45 "Mismatching number of ranges and name pieces"); 46 AtomicChange Change(SM, Ranges[0].getBegin()); 47 for (const auto &Range : llvm::enumerate(Ranges)) { 48 auto Error = 49 Change.replace(SM, CharSourceRange::getCharRange(Range.value()), 50 NewNameStrings[Range.index()]); 51 if (Error) 52 return std::move(Error); 53 } 54 Changes.push_back(std::move(Change)); 55 } 56 return std::move(Changes); 57 } 58 59 /// Takes each atomic change and inserts its replacements into the set of 60 /// replacements that belong to the appropriate file. 61 static void convertChangesToFileReplacements( 62 ArrayRef<AtomicChange> AtomicChanges, 63 std::map<std::string, tooling::Replacements> *FileToReplaces) { 64 for (const auto &AtomicChange : AtomicChanges) { 65 for (const auto &Replace : AtomicChange.getReplacements()) { 66 llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace); 67 if (Err) { 68 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! " 69 << llvm::toString(std::move(Err)) << "\n"; 70 } 71 } 72 } 73 } 74 75 class RenamingASTConsumer : public ASTConsumer { 76 public: 77 RenamingASTConsumer( 78 const std::vector<std::string> &NewNames, 79 const std::vector<std::string> &PrevNames, 80 const std::vector<std::vector<std::string>> &USRList, 81 std::map<std::string, tooling::Replacements> &FileToReplaces, 82 bool PrintLocations) 83 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), 84 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} 85 86 void HandleTranslationUnit(ASTContext &Context) override { 87 for (unsigned I = 0; I < NewNames.size(); ++I) 88 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); 89 } 90 91 void HandleOneRename(ASTContext &Context, const std::string &NewName, 92 const std::string &PrevName, 93 const std::vector<std::string> &USRs) { 94 const SourceManager &SourceMgr = Context.getSourceManager(); 95 96 SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs( 97 USRs, PrevName, Context.getTranslationUnitDecl()); 98 if (PrintLocations) { 99 for (const auto &Occurrence : Occurrences) { 100 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(), 101 SourceMgr); 102 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc) 103 << ":" << FullLoc.getSpellingLineNumber() << ":" 104 << FullLoc.getSpellingColumnNumber() << "\n"; 105 } 106 } 107 // FIXME: Support multi-piece names. 108 // FIXME: better error handling (propagate error out). 109 StringRef NewNameRef = NewName; 110 Expected<std::vector<AtomicChange>> Change = 111 createRenameReplacements(Occurrences, SourceMgr, NewNameRef); 112 if (!Change) { 113 llvm::errs() << "Failed to create renaming replacements for '" << PrevName 114 << "'! " << llvm::toString(Change.takeError()) << "\n"; 115 return; 116 } 117 convertChangesToFileReplacements(*Change, &FileToReplaces); 118 } 119 120 private: 121 const std::vector<std::string> &NewNames, &PrevNames; 122 const std::vector<std::vector<std::string>> &USRList; 123 std::map<std::string, tooling::Replacements> &FileToReplaces; 124 bool PrintLocations; 125 }; 126 127 // A renamer to rename symbols which are identified by a give USRList to 128 // new name. 129 // 130 // FIXME: Merge with the above RenamingASTConsumer. 131 class USRSymbolRenamer : public ASTConsumer { 132 public: 133 USRSymbolRenamer(const std::vector<std::string> &NewNames, 134 const std::vector<std::vector<std::string>> &USRList, 135 std::map<std::string, tooling::Replacements> &FileToReplaces) 136 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) { 137 assert(USRList.size() == NewNames.size()); 138 } 139 140 void HandleTranslationUnit(ASTContext &Context) override { 141 for (unsigned I = 0; I < NewNames.size(); ++I) { 142 // FIXME: Apply AtomicChanges directly once the refactoring APIs are 143 // ready. 144 auto AtomicChanges = tooling::createRenameAtomicChanges( 145 USRList[I], NewNames[I], Context.getTranslationUnitDecl()); 146 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces); 147 } 148 } 149 150 private: 151 const std::vector<std::string> &NewNames; 152 const std::vector<std::vector<std::string>> &USRList; 153 std::map<std::string, tooling::Replacements> &FileToReplaces; 154 }; 155 156 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() { 157 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, 158 FileToReplaces, PrintLocations); 159 } 160 161 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() { 162 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); 163 } 164 165 } // end namespace tooling 166 } // end namespace clang 167