clang 17.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:80
llvm::Error Error
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:247
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.
YAML serialization mapping.
Definition: Dominators.h:30