clang 22.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
18#include "clang/Lex/Lexer.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/Support/Errc.h"
25#include "llvm/Support/Error.h"
26#include <string>
27#include <vector>
28
29using namespace llvm;
30
31namespace clang {
32namespace tooling {
33
34namespace {
35
37findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) {
38 std::vector<std::string> USRs =
39 getUSRsForDeclaration(ND, Context.getASTContext());
40 std::string PrevName = ND->getNameAsString();
41 return getOccurrencesOfUSRs(USRs, PrevName,
42 Context.getASTContext().getTranslationUnitDecl());
43}
44
45} // end anonymous namespace
46
48 static const RefactoringDescriptor Descriptor = {
49 "local-rename",
50 "Rename",
51 "Finds and renames symbols in code with no indexer support",
52 };
53 return Descriptor;
54}
55
58 SourceRange SelectionRange, std::string NewName) {
59 const NamedDecl *ND =
60 getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());
61 if (!ND)
62 return Context.createDiagnosticError(
63 SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);
64 return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
65 std::move(NewName));
66}
67
68const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }
69
71RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
72 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
73 if (!Occurrences)
74 return Occurrences.takeError();
75 // FIXME: Verify that the new name is valid.
76 SymbolName Name(NewName);
78 *Occurrences, Context.getASTContext().getSourceManager(), Name);
79}
80
83 std::string OldQualifiedName,
84 std::string NewQualifiedName) {
85 const NamedDecl *ND =
86 getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
87 if (!ND)
88 return llvm::make_error<llvm::StringError>("Could not find symbol " +
89 OldQualifiedName,
90 llvm::errc::invalid_argument);
91 return QualifiedRenameRule(ND, std::move(NewQualifiedName));
92}
93
95 static const RefactoringDescriptor Descriptor = {
96 /*Name=*/"local-qualified-rename",
97 /*Title=*/"Qualified Rename",
98 /*Description=*/
99 R"(Finds and renames qualified symbols in code within a translation unit.
100It is used to move/rename a symbol to a new namespace/name:
101 * Supported symbols: classes, class members, functions, enums, and type alias.
102 * Renames all symbol occurrences from the old qualified name to the new
103 qualified name. All symbol references will be correctly qualified; For
104 symbol definitions, only name will be changed.
105For example, rename "A::Foo" to "B::Bar":
106 Old code:
107 namespace foo {
108 class A {};
109 }
110
111 namespace bar {
112 void f(foo::A a) {}
114
115 New code after rename:
116 namespace foo {
117 class B {};
118 }
119
120 namespace bar {
121 void f(B b) {}
122 })"
123 };
124 return Descriptor;
125}
126
128QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
129 auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
130 assert(!USRs.empty());
132 USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
133}
134
137 const SourceManager &SM, const SymbolName &NewName) {
138 // FIXME: A true local rename can use just one AtomicChange.
139 std::vector<AtomicChange> Changes;
140 for (const auto &Occurrence : Occurrences) {
141 ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
142 assert(NewName.getNamePieces().size() == Ranges.size() &&
143 "Mismatching number of ranges and name pieces");
144 AtomicChange Change(SM, Ranges[0].getBegin());
145 for (const auto &Range : llvm::enumerate(Ranges)) {
146 auto Error =
148 NewName.getNamePieces()[Range.index()]);
149 if (Error)
150 return std::move(Error);
152 Changes.push_back(std::move(Change));
154 return std::move(Changes);
155}
156
157/// Takes each atomic change and inserts its replacements into the set of
158/// replacements that belong to the appropriate file.
161 std::map<std::string, tooling::Replacements> *FileToReplaces) {
162 for (const auto &AtomicChange : AtomicChanges) {
163 for (const auto &Replace : AtomicChange.getReplacements()) {
164 llvm::Error Err =
165 (*FileToReplaces)[std::string(Replace.getFilePath())].add(Replace);
166 if (Err) {
167 llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
168 << llvm::toString(std::move(Err)) << "\n";
169 }
170 }
171 }
173
174class RenamingASTConsumer : public ASTConsumer {
175public:
177 const std::vector<std::string> &NewNames,
178 const std::vector<std::string> &PrevNames,
179 const std::vector<std::vector<std::string>> &USRList,
180 std::map<std::string, tooling::Replacements> &FileToReplaces,
181 bool PrintLocations)
182 : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
183 FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
184
185 void HandleTranslationUnit(ASTContext &Context) override {
186 for (unsigned I = 0; I < NewNames.size(); ++I) {
187 // If the previous name was not found, ignore this rename request.
188 if (PrevNames[I].empty())
189 continue;
190
191 HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
192 }
193 }
194
195 void HandleOneRename(ASTContext &Context, const std::string &NewName,
196 const std::string &PrevName,
197 const std::vector<std::string> &USRs) {
198 const SourceManager &SourceMgr = Context.getSourceManager();
199
201 USRs, PrevName, Context.getTranslationUnitDecl());
202 if (PrintLocations) {
203 for (const auto &Occurrence : Occurrences) {
204 FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
205 SourceMgr);
206 errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
207 << ":" << FullLoc.getSpellingLineNumber() << ":"
208 << FullLoc.getSpellingColumnNumber() << "\n";
209 }
210 }
211 // FIXME: Support multi-piece names.
212 // FIXME: better error handling (propagate error out).
213 SymbolName NewNameRef(NewName);
215 createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
216 if (!Change) {
217 llvm::errs() << "Failed to create renaming replacements for '" << PrevName
218 << "'! " << llvm::toString(Change.takeError()) << "\n";
219 return;
220 }
221 convertChangesToFileReplacements(*Change, &FileToReplaces);
222 }
223
224private:
225 const std::vector<std::string> &NewNames, &PrevNames;
226 const std::vector<std::vector<std::string>> &USRList;
227 std::map<std::string, tooling::Replacements> &FileToReplaces;
228 bool PrintLocations;
229};
230
231// A renamer to rename symbols which are identified by a give USRList to
232// new name.
233//
234// FIXME: Merge with the above RenamingASTConsumer.
235class USRSymbolRenamer : public ASTConsumer {
236public:
237 USRSymbolRenamer(const std::vector<std::string> &NewNames,
238 const std::vector<std::vector<std::string>> &USRList,
239 std::map<std::string, tooling::Replacements> &FileToReplaces)
240 : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
241 assert(USRList.size() == NewNames.size());
243
244 void HandleTranslationUnit(ASTContext &Context) override {
245 for (unsigned I = 0; I < NewNames.size(); ++I) {
246 // FIXME: Apply AtomicChanges directly once the refactoring APIs are
247 // ready.
249 USRList[I], NewNames[I], Context.getTranslationUnitDecl());
251 }
252 }
253
254private:
255 const std::vector<std::string> &NewNames;
256 const std::vector<std::vector<std::string>> &USRList;
257 std::map<std::string, tooling::Replacements> &FileToReplaces;
258};
259
260std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
261 return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
262 FileToReplaces, PrintLocations);
263}
264
265std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
266 return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
267}
268
269} // end namespace tooling
270} // end namespace clang
Defines the clang::ASTContext interface.
#define SM(sm)
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.
SourceManager & getSourceManager()
Definition ASTContext.h:798
TranslationUnitDecl * getTranslationUnitDecl() const
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition ASTConsumer.h:34
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:188
static CharSourceRange getCharRange(SourceRange R)
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:524
This represents a decl that may have a name.
Definition Decl.h:273
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.
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.
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)
RenamingASTConsumer(const std::vector< std::string > &NewNames, const std::vector< std::string > &PrevNames, const std::vector< std::vector< std::string > > &USRList, std::map< std::string, tooling::Replacements > &FileToReplaces, bool PrintLocations)
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
USRSymbolRenamer(const std::vector< std::string > &NewNames, const std::vector< std::vector< std::string > > &USRList, std::map< std::string, tooling::Replacements > &FileToReplaces)
void HandleTranslationUnit(ASTContext &Context) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
std::vector< AtomicChange > AtomicChanges
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
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...
std::vector< SymbolOccurrence > SymbolOccurrences
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:75
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