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()) {
174 (*FileToReplaces
)[std::string(Replace
.getFilePath())].add(Replace
);
176 llvm::errs() << "Renaming failed in " << Replace
.getFilePath() << "! "
177 << llvm::toString(std::move(Err
)) << "\n";
183 class RenamingASTConsumer
: public ASTConsumer
{
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
,
191 : NewNames(NewNames
), PrevNames(PrevNames
), USRList(USRList
),
192 FileToReplaces(FileToReplaces
), PrintLocations(PrintLocations
) {}
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())
200 HandleOneRename(Context
, NewNames
[I
], PrevNames
[I
], USRList
[I
]);
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();
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(),
215 errs() << "clang-rename: renamed at: " << SourceMgr
.getFilename(FullLoc
)
216 << ":" << FullLoc
.getSpellingLineNumber() << ":"
217 << FullLoc
.getSpellingColumnNumber() << "\n";
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
);
226 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
227 << "'! " << llvm::toString(Change
.takeError()) << "\n";
230 convertChangesToFileReplacements(*Change
, &FileToReplaces
);
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
;
240 // A renamer to rename symbols which are identified by a give USRList to
243 // FIXME: Merge with the above RenamingASTConsumer.
244 class USRSymbolRenamer
: public ASTConsumer
{
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());
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
257 auto AtomicChanges
= tooling::createRenameAtomicChanges(
258 USRList
[I
], NewNames
[I
], Context
.getTranslationUnitDecl());
259 convertChangesToFileReplacements(AtomicChanges
, &FileToReplaces
);
264 const std::vector
<std::string
> &NewNames
;
265 const std::vector
<std::vector
<std::string
>> &USRList
;
266 std::map
<std::string
, tooling::Replacements
> &FileToReplaces
;
269 std::unique_ptr
<ASTConsumer
> RenamingAction::newASTConsumer() {
270 return std::make_unique
<RenamingASTConsumer
>(NewNames
, PrevNames
, USRList
,
271 FileToReplaces
, PrintLocations
);
274 std::unique_ptr
<ASTConsumer
> QualifiedRenamingAction::newASTConsumer() {
275 return std::make_unique
<USRSymbolRenamer
>(NewNames
, USRList
, FileToReplaces
);
278 } // end namespace tooling
279 } // end namespace clang