| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 1 | //===--- RenamingAction.cpp - Clang refactoring library -------------------===// |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 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 | |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 15 | #include "clang/Tooling/Refactoring/Rename/RenamingAction.h" |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 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" |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 21 | #include "clang/Lex/Lexer.h" |
| Chandler Carruth | 3cbd71c | 2015-01-14 11:24:38 +0000 | [diff] [blame] | 22 | #include "clang/Lex/Preprocessor.h" |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 23 | #include "clang/Tooling/CommonOptionsParser.h" |
| 24 | #include "clang/Tooling/Refactoring.h" |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 25 | #include "clang/Tooling/Refactoring/RefactoringAction.h" |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 26 | #include "clang/Tooling/Refactoring/RefactoringOptions.h" |
| 27 | #include "clang/Tooling/Refactoring/Rename/SymbolName.h" |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 28 | #include "clang/Tooling/Refactoring/Rename/USRFinder.h" |
| 29 | #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h" |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 30 | #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 31 | #include "clang/Tooling/Tooling.h" |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 32 | #include "llvm/ADT/STLExtras.h" |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 33 | #include <string> |
| 34 | #include <vector> |
| 35 | |
| 36 | using namespace llvm; |
| 37 | |
| 38 | namespace clang { |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 39 | namespace tooling { |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 40 | |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 41 | namespace { |
| 42 | |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 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: |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 80 | RenameOccurrences(const NamedDecl *ND, std::string NewName) |
| 81 | : Finder(ND), NewName(NewName) {} |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 82 | |
| 83 | Expected<AtomicChanges> |
| 84 | createSourceReplacements(RefactoringRuleContext &Context) { |
| 85 | Expected<SymbolOccurrences> Occurrences = |
| 86 | Finder.findSymbolOccurrences(Context); |
| 87 | if (!Occurrences) |
| 88 | return Occurrences.takeError(); |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 89 | // FIXME: Verify that the new name is valid. |
| 90 | SymbolName Name(NewName); |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 91 | return createRenameReplacements( |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 92 | *Occurrences, Context.getASTContext().getSourceManager(), Name); |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | private: |
| 96 | OccurrenceFinder Finder; |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 97 | std::string NewName; |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 98 | }; |
| 99 | |
| 100 | class LocalRename final : public RefactoringAction { |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 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 { |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 111 | RefactoringActionRules Rules; |
| Alex Lorenz | 1c0a26b | 2017-10-02 18:42:43 +0000 | [diff] [blame] | 112 | Rules.push_back(createRefactoringActionRule<RenameOccurrences>( |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 113 | SymbolSelectionRequirement(), OptionRequirement<NewNameOption>())); |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 114 | return Rules; |
| 115 | } |
| Alex Lorenz | b54ef6a | 2017-09-14 10:06:52 +0000 | [diff] [blame] | 116 | }; |
| 117 | |
| 118 | } // end anonymous namespace |
| 119 | |
| 120 | std::unique_ptr<RefactoringAction> createLocalRenameAction() { |
| 121 | return llvm::make_unique<LocalRename>(); |
| 122 | } |
| 123 | |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 124 | Expected<std::vector<AtomicChange>> |
| 125 | createRenameReplacements(const SymbolOccurrences &Occurrences, |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 126 | const SourceManager &SM, const SymbolName &NewName) { |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 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(); |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 131 | assert(NewName.getNamePieces().size() == Ranges.size() && |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 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()), |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 137 | NewName.getNamePieces()[Range.index()]); |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 138 | if (Error) |
| 139 | return std::move(Error); |
| 140 | } |
| 141 | Changes.push_back(std::move(Change)); |
| 142 | } |
| Alexander Shaposhnikov | ab0400d | 2017-08-15 19:23:54 +0000 | [diff] [blame] | 143 | return std::move(Changes); |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 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 | |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 162 | class RenamingASTConsumer : public ASTConsumer { |
| 163 | public: |
| Eric Liu | 267034c | 2016-08-01 10:16:39 +0000 | [diff] [blame] | 164 | RenamingASTConsumer( |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 165 | const std::vector<std::string> &NewNames, |
| 166 | const std::vector<std::string> &PrevNames, |
| 167 | const std::vector<std::vector<std::string>> &USRList, |
| Eric Liu | 267034c | 2016-08-01 10:16:39 +0000 | [diff] [blame] | 168 | std::map<std::string, tooling::Replacements> &FileToReplaces, |
| 169 | bool PrintLocations) |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 170 | : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), |
| Eric Liu | 267034c | 2016-08-01 10:16:39 +0000 | [diff] [blame] | 171 | FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 172 | |
| 173 | void HandleTranslationUnit(ASTContext &Context) override { |
| Miklos Vajna | bf0d49c4 | 2017-09-11 20:18:38 +0000 | [diff] [blame] | 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 | |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 179 | HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); |
| Miklos Vajna | bf0d49c4 | 2017-09-11 20:18:38 +0000 | [diff] [blame] | 180 | } |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | void HandleOneRename(ASTContext &Context, const std::string &NewName, |
| 184 | const std::string &PrevName, |
| 185 | const std::vector<std::string> &USRs) { |
| Kirill Bobyrev | 6b7d8c2 | 2016-08-15 23:20:05 +0000 | [diff] [blame] | 186 | const SourceManager &SourceMgr = Context.getSourceManager(); |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 187 | |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 188 | SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs( |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 189 | USRs, PrevName, Context.getTranslationUnitDecl()); |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 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) |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 195 | << ":" << FullLoc.getSpellingLineNumber() << ":" |
| 196 | << FullLoc.getSpellingColumnNumber() << "\n"; |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 197 | } |
| Kirill Bobyrev | a3432fa | 2016-07-22 13:41:09 +0000 | [diff] [blame] | 198 | } |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 199 | // FIXME: Support multi-piece names. |
| 200 | // FIXME: better error handling (propagate error out). |
| Alex Lorenz | ad38fbf | 2017-10-13 01:53:13 +0000 | [diff] [blame^] | 201 | SymbolName NewNameRef(NewName); |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 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); |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | private: |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 213 | const std::vector<std::string> &NewNames, &PrevNames; |
| 214 | const std::vector<std::vector<std::string>> &USRList; |
| Eric Liu | 267034c | 2016-08-01 10:16:39 +0000 | [diff] [blame] | 215 | std::map<std::string, tooling::Replacements> &FileToReplaces; |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 216 | bool PrintLocations; |
| 217 | }; |
| 218 | |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 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. |
| Miklos Vajna | a60cae2 | 2017-04-23 16:07:06 +0000 | [diff] [blame] | 223 | class USRSymbolRenamer : public ASTConsumer { |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 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. |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 236 | auto AtomicChanges = tooling::createRenameAtomicChanges( |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 237 | USRList[I], NewNames[I], Context.getTranslationUnitDecl()); |
| Alex Lorenz | 44b94c7 | 2017-08-14 16:19:24 +0000 | [diff] [blame] | 238 | convertChangesToFileReplacements(AtomicChanges, &FileToReplaces); |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 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 | |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 248 | std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() { |
| Miklos Vajna | aaec9b6 | 2016-08-02 09:51:31 +0000 | [diff] [blame] | 249 | return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, |
| Eric Liu | 267034c | 2016-08-01 10:16:39 +0000 | [diff] [blame] | 250 | FileToReplaces, PrintLocations); |
| Manuel Klimek | de23726 | 2014-08-20 01:39:05 +0000 | [diff] [blame] | 251 | } |
| 252 | |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 253 | std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() { |
| Miklos Vajna | a60cae2 | 2017-04-23 16:07:06 +0000 | [diff] [blame] | 254 | return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); |
| Haojian Wu | 74f823a | 2017-04-04 09:30:06 +0000 | [diff] [blame] | 255 | } |
| 256 | |
| Alex Lorenz | 4abbd92 | 2017-06-30 16:36:09 +0000 | [diff] [blame] | 257 | } // end namespace tooling |
| 258 | } // end namespace clang |