1 //===--- RenamingAction.cpp - Clang refactoring library -------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// Provides an action to rename every symbol at a point.
12 //===----------------------------------------------------------------------===//
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"
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());
54 } // end anonymous namespace
56 const RefactoringDescriptor &RenameOccurrences::describe() {
57 static const RefactoringDescriptor Descriptor = {
60 "Finds and renames symbols in code with no indexer support",
65 Expected<RenameOccurrences>
66 RenameOccurrences::initiate(RefactoringRuleContext &Context,
67 SourceRange SelectionRange, std::string NewName) {
69 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());
71 return Context.createDiagnosticError(
72 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);
73 return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
77 const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }
79 Expected<AtomicChanges>
80 RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
81 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
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);
90 Expected<QualifiedRenameRule>
91 QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
92 std::string OldQualifiedName,
93 std::string NewQualifiedName) {
95 getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
97 return llvm::make_error<llvm::StringError>("Could not find symbol " +
99 llvm::errc::invalid_argument);
100 return QualifiedRenameRule(ND, std::move(NewQualifiedName));
103 const RefactoringDescriptor &QualifiedRenameRule::describe() {
104 static const RefactoringDescriptor Descriptor = {
105 /*Name=*/"local-qualified-rename",
106 /*Title=*/"Qualified Rename",
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":
124 New code after rename:
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());
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)) {
156 Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
157 NewName.getNamePieces()[Range.index()]);
159 return std::move(Error);
161 Changes.push_back(std::move(Change));
163 return std::move(Changes);
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);
175 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
176 << llvm::toString(std::move(Err)) << "\n";
182 class RenamingASTConsumer : public ASTConsumer {
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,
190 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
191 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
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())
199 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
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();
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(),
214 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
215 << ":" << FullLoc.getSpellingLineNumber() << ":"
216 << FullLoc.getSpellingColumnNumber() << "\n";
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);
225 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
226 << "'! " << llvm::toString(Change.takeError()) << "\n";
229 convertChangesToFileReplacements(*Change, &FileToReplaces);
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;
239 // A renamer to rename symbols which are identified by a give USRList to
242 // FIXME: Merge with the above RenamingASTConsumer.
243 class USRSymbolRenamer : public ASTConsumer {
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());
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
256 auto AtomicChanges = tooling::createRenameAtomicChanges(
257 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
258 convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
263 const std::vector<std::string> &NewNames;
264 const std::vector<std::vector<std::string>> &USRList;
265 std::map<std::string, tooling::Replacements> &FileToReplaces;
268 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
269 return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
270 FileToReplaces, PrintLocations);
273 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
274 return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
277 } // end namespace tooling
278 } // end namespace clang