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 
15 #include "clang/AST/ASTConsumer.h"
16 #include "clang/AST/ASTContext.h"
20 #include "clang/Lex/Lexer.h"
21 #include "clang/Lex/Preprocessor.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"
35 #include <string>
36 #include <vector>
37 
38 using namespace llvm;
39 
40 namespace clang {
41 namespace tooling {
42 
43 namespace {
44 
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());
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 
66 RenameOccurrences::initiate(RefactoringRuleContext &Context,
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 
77 const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }
78 
80 RenameOccurrences::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 
91 QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
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.
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":
115  Old code:
116  namespace foo {
117  class A {};
118  }
119 
120  namespace bar {
121  void f(foo::A a) {}
122  }
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 
137 QualifiedRenameRule::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 =
156  Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
157  NewName.getNamePieces()[Range.index()]);
158  if (Error)
159  return std::move(Error);
160  }
161  Changes.push_back(std::move(Change));
162  }
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  }
181 }
182 
183 class RenamingASTConsumer : public ASTConsumer {
184 public:
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 
233 private:
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.
244 class USRSymbolRenamer : public ASTConsumer {
245 public:
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());
251  }
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 
263 private:
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 
269 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
270  return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
271  FileToReplaces, PrintLocations);
272 }
273 
274 std::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:83
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:185
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
const Replacements & getReplacements() const
Returns a const reference to existing replacements.
Definition: AtomicChange.h:117
llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range, llvm::StringRef ReplacementText)
Adds a replacement that replaces the given Range with ReplacementText.
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...
A name of a symbol.
Definition: SymbolName.h:29
ArrayRef< std::string > getNamePieces() const
Definition: SymbolName.h:39
std::string toString(const til::SExpr *E)
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.
Stencil describe(llvm::StringRef Id)
Produces a human-readable rendering of the node bound to Id, suitable for diagnostics and debugging.
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30