clang 19.0.0git
RenamingAction.cpp
Go to the documentation of this file.
1//===--- RenamingAction.cpp - Clang refactoring library -------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// Provides an action to rename every symbol at a point.
11///
12//===----------------------------------------------------------------------===//
13
20#include "clang/Lex/Lexer.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/Support/Errc.h"
34#include "llvm/Support/Error.h"
35#include <string>
36#include <vector>
37
38using namespace llvm;
39
40namespace clang {
41namespace tooling {
42
43namespace {
44
46findSymbolOccurrences(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());
52}
53
54} // end anonymous namespace
55
57 static const RefactoringDescriptor Descriptor = {
58 "local-rename",
59 "Rename",
60 "Finds and renames symbols in code with no indexer support",
61 };
62 return Descriptor;
63}
64
67 SourceRange SelectionRange, std::string NewName) {
68 const NamedDecl *ND =
69 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());
70 if (!ND)
71 return Context.createDiagnosticError(
72 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);
74 std::move(NewName));
75}
76
77const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }
78
80RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
81 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
82 if (!Occurrences)
83 return Occurrences.takeError();
84 // FIXME: Verify that the new name is valid.
85 SymbolName Name(NewName);
87 *Occurrences, Context.getASTContext().getSourceManager(), Name);
88}
89
92 std::string OldQualifiedName,
93 std::string NewQualifiedName) {
94 const NamedDecl *ND =
95 getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
96 if (!ND)
97 return llvm::make_error<llvm::StringError>("Could not find symbol " +
98 OldQualifiedName,
99 llvm::errc::invalid_argument);
100 return QualifiedRenameRule(ND, std::move(NewQualifiedName));
101}
102
104 static const RefactoringDescriptor Descriptor = {
105 /*Name=*/"local-qualified-rename",
106 /*Title=*/"Qualified Rename",
107 /*Description=*/
108 R"(Finds and renames qualified symbols in code within a translation unit.
109It 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.
114For example, rename "A::Foo" to "B::Bar":
115 Old code:
116 namespace foo {
117 class A {};
118 }
119
120 namespace bar {
121 void f(foo::A a) {}
123
124 New code after rename:
125 namespace foo {
126 class B {};
127 }
128
129 namespace bar {
130 void f(B b) {}
131 })"
132 };
133 return Descriptor;
134}
135
137QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
138 auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
139 assert(!USRs.empty());
141 USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
142}
143
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)) {
155 auto Error =
157 NewName.getNamePieces()[Range.index()]);
158 if (Error)
159 return std::move(Error);
161 Changes.push_back(std::move(Change));
163 return std::move(Changes);
164}
165
166/// Takes each atomic change and inserts its replacements into the set of
167/// replacements that belong to the appropriate file.
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 =
174 (*FileToReplaces)[std::string(Replace.getFilePath())].add(Replace);
175 if (Err) {
176 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
177 << llvm::toString(std::move(Err)) << "\n";
178 }
179 }
180 }
182
183class RenamingASTConsumer : public ASTConsumer {
184public:
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,
190 bool PrintLocations)
191 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
192 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
193
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())
198 continue;
199
200 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
201 }
202 }
203
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();
208
210 USRs, PrevName, Context.getTranslationUnitDecl());
211 if (PrintLocations) {
212 for (const auto &Occurrence : Occurrences) {
213 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
214 SourceMgr);
215 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
216 << ":" << FullLoc.getSpellingLineNumber() << ":"
217 << FullLoc.getSpellingColumnNumber() << "\n";
218 }
219 }
220 // FIXME: Support multi-piece names.
221 // FIXME: better error handling (propagate error out).
222 SymbolName NewNameRef(NewName);
224 createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
225 if (!Change) {
226 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
227 << "'! " << llvm::toString(Change.takeError()) << "\n";
228 return;
229 }
230 convertChangesToFileReplacements(*Change, &FileToReplaces);
231 }
232
233private:
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;
237 bool PrintLocations;
238};
239
240// A renamer to rename symbols which are identified by a give USRList to
241// new name.
242//
243// FIXME: Merge with the above RenamingASTConsumer.
244class USRSymbolRenamer : public ASTConsumer {
245public:
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());
252
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
256 // ready.
258 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
260 }
261 }
262
263private:
264 const std::vector<std::string> &NewNames;
265 const std::vector<std::vector<std::string>> &USRList;
266 std::map<std::string, tooling::Replacements> &FileToReplaces;
267};
268
269std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
270 return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
271 FileToReplaces, PrintLocations);
272}
273
274std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
275 return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
276}
277
278} // end namespace tooling
279} // end namespace clang
Defines the clang::ASTContext interface.
#define SM(sm)
Definition: Cuda.cpp:82
Defines the clang::FileManager interface and associated types.
Defines the clang::FrontendAction interface and various convenience abstract classes (clang::ASTFront...
Defines the clang::Preprocessor interface.
Provides an action to rename every symbol at a point.
Methods for determining the USR of a symbol at a location in source code.
Provides an action to find all relevant USRs at a point.
Provides functionality for finding all instances of a USR in a given AST.
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition: ASTConsumer.h:33
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
static CharSourceRange getCharRange(SourceRange R)
This represents a decl that may have a name.
Definition: Decl.h:249
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getBegin() const
An atomic change is used to create and group a set of source edits, e.g.
Definition: AtomicChange.h:37
llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range, llvm::StringRef ReplacementText)
Adds a replacement that replaces the given Range with ReplacementText.
const Replacements & getReplacements() const
Returns a const reference to existing replacements.
Definition: AtomicChange.h:117
static Expected< QualifiedRenameRule > initiate(RefactoringRuleContext &Context, std::string OldQualifiedName, std::string NewQualifiedName)
static const RefactoringDescriptor & describe()
std::unique_ptr< ASTConsumer > newASTConsumer()
A source range independent of the SourceManager.
Definition: Replacement.h:44
The refactoring rule context stores all of the inputs that might be needed by a refactoring action ru...
static const RefactoringDescriptor & describe()
const NamedDecl * getRenameDecl() const
static Expected< RenameOccurrences > initiate(RefactoringRuleContext &Context, SourceRange SelectionRange, std::string NewName)
void HandleOneRename(ASTContext &Context, const std::string &NewName, const std::string &PrevName, const std::vector< std::string > &USRs)
void HandleTranslationUnit(ASTContext &Context) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
std::unique_ptr< ASTConsumer > newASTConsumer()
A name of a symbol.
Definition: SymbolName.h:29
ArrayRef< std::string > getNamePieces() const
Definition: SymbolName.h:39
void HandleTranslationUnit(ASTContext &Context) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:128
static void convertChangesToFileReplacements(ArrayRef< AtomicChange > AtomicChanges, std::map< std::string, tooling::Replacements > *FileToReplaces)
Takes each atomic change and inserts its replacements into the set of replacements that belong to the...
SymbolOccurrences getOccurrencesOfUSRs(ArrayRef< std::string > USRs, StringRef PrevName, Decl *Decl)
Finds the symbol occurrences for the symbol that's identified by the given USR set.
llvm::Expected< std::vector< AtomicChange > > createRenameReplacements(const SymbolOccurrences &Occurrences, const SourceManager &SM, const SymbolName &NewName)
Returns source replacements that correspond to the rename of the given symbol occurrences.
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:77
std::vector< AtomicChange > AtomicChanges
Definition: AtomicChange.h:154
std::vector< SymbolOccurrence > SymbolOccurrences
std::vector< tooling::AtomicChange > createRenameAtomicChanges(llvm::ArrayRef< std::string > USRs, llvm::StringRef NewName, Decl *TranslationUnitDecl)
Create atomic changes for renaming all symbol references which are identified by the USRs set to a gi...
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30