Clang Project

clang_source_code/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
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
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"
35#include <string>
36#include <vector>
37
38using namespace llvm;
39
40namespace clang {
41namespace tooling {
42
43namespace {
44
45Expected<SymbolOccurrences>
46findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) {
47  std::vector<std::stringUSRs =
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
56const RefactoringDescriptor &RenameOccurrences::describe() {
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
65Expected<RenameOccurrences>
66RenameOccurrences::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);
73  return RenameOccurrences(getCanonicalSymbolDeclaration(ND),
74                           std::move(NewName));
75}
76
77Expected<AtomicChanges>
78RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
79  Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
80  if (!Occurrences)
81    return Occurrences.takeError();
82  // FIXME: Verify that the new name is valid.
83  SymbolName Name(NewName);
84  return createRenameReplacements(
85      *Occurrences, Context.getASTContext().getSourceManager(), Name);
86}
87
88Expected<QualifiedRenameRule>
89QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
90                              std::string OldQualifiedName,
91                              std::string NewQualifiedName) {
92  const NamedDecl *ND =
93      getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
94  if (!ND)
95    return llvm::make_error<llvm::StringError>("Could not find symbol " +
96                                                   OldQualifiedName,
97                                               llvm::errc::invalid_argument);
98  return QualifiedRenameRule(ND, std::move(NewQualifiedName));
99}
100
101const RefactoringDescriptor &QualifiedRenameRule::describe() {
102  static const RefactoringDescriptor Descriptor = {
103      /*Name=*/"local-qualified-rename",
104      /*Title=*/"Qualified Rename",
105      /*Description=*/
106      R"(Finds and renames qualified symbols in code within a translation unit.
107It is used to move/rename a symbol to a new namespace/name:
108  * Supported symbols: classes, class members, functions, enums, and type alias.
109  * Renames all symbol occurrences from the old qualified name to the new
110    qualified name. All symbol references will be correctly qualified; For
111    symbol definitions, only name will be changed.
112For example, rename "A::Foo" to "B::Bar":
113  Old code:
114    namespace foo {
115    class A {};
116    }
117
118    namespace bar {
119    void f(foo::A a) {}
120    }
121
122  New code after rename:
123    namespace foo {
124    class B {};
125    }
126
127    namespace bar {
128    void f(B b) {}
129    })"
130  };
131  return Descriptor;
132}
133
134Expected<AtomicChanges>
135QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
136  auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
137  assert(!USRs.empty());
138  return tooling::createRenameAtomicChanges(
139      USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
140}
141
142Expected<std::vector<AtomicChange>>
143createRenameReplacements(const SymbolOccurrences &Occurrences,
144                         const SourceManager &SM, const SymbolName &NewName) {
145  // FIXME: A true local rename can use just one AtomicChange.
146  std::vector<AtomicChange> Changes;
147  for (const auto &Occurrence : Occurrences) {
148    ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
149     (0) . __assert_fail ("NewName.getNamePieces().size() == Ranges.size() && \"Mismatching number of ranges and name pieces\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp", 150, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(NewName.getNamePieces().size() == Ranges.size() &&
150 (0) . __assert_fail ("NewName.getNamePieces().size() == Ranges.size() && \"Mismatching number of ranges and name pieces\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp", 150, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">           "Mismatching number of ranges and name pieces");
151    AtomicChange Change(SM, Ranges[0].getBegin());
152    for (const auto &Range : llvm::enumerate(Ranges)) {
153      auto Error =
154          Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
155                         NewName.getNamePieces()[Range.index()]);
156      if (Error)
157        return std::move(Error);
158    }
159    Changes.push_back(std::move(Change));
160  }
161  return std::move(Changes);
162}
163
164/// Takes each atomic change and inserts its replacements into the set of
165/// replacements that belong to the appropriate file.
166static void convertChangesToFileReplacements(
167    ArrayRef<AtomicChange> AtomicChanges,
168    std::map<std::stringtooling::Replacements> *FileToReplaces) {
169  for (const auto &AtomicChange : AtomicChanges) {
170    for (const auto &Replace : AtomicChange.getReplacements()) {
171      llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
172      if (Err) {
173        llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
174                     << llvm::toString(std::move(Err)) << "\n";
175      }
176    }
177  }
178}
179
180class RenamingASTConsumer : public ASTConsumer {
181public:
182  RenamingASTConsumer(
183      const std::vector<std::string> &NewNames,
184      const std::vector<std::string> &PrevNames,
185      const std::vector<std::vector<std::string>> &USRList,
186      std::map<std::stringtooling::Replacements> &FileToReplaces,
187      bool PrintLocations)
188      : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
189        FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
190
191  void HandleTranslationUnit(ASTContext &Context) override {
192    for (unsigned I = 0I < NewNames.size(); ++I) {
193      // If the previous name was not found, ignore this rename request.
194      if (PrevNames[I].empty())
195        continue;
196
197      HandleOneRename(ContextNewNames[I], PrevNames[I], USRList[I]);
198    }
199  }
200
201  void HandleOneRename(ASTContext &Contextconst std::string &NewName,
202                       const std::string &PrevName,
203                       const std::vector<std::string> &USRs) {
204    const SourceManager &SourceMgr = Context.getSourceManager();
205
206    SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
207        USRs, PrevName, Context.getTranslationUnitDecl());
208    if (PrintLocations) {
209      for (const auto &Occurrence : Occurrences) {
210        FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
211                              SourceMgr);
212        errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
213               << ":" << FullLoc.getSpellingLineNumber() << ":"
214               << FullLoc.getSpellingColumnNumber() << "\n";
215      }
216    }
217    // FIXME: Support multi-piece names.
218    // FIXME: better error handling (propagate error out).
219    SymbolName NewNameRef(NewName);
220    Expected<std::vector<AtomicChange>> Change =
221        createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
222    if (!Change) {
223      llvm::errs() << "Failed to create renaming replacements for '" << PrevName
224                   << "'! " << llvm::toString(Change.takeError()) << "\n";
225      return;
226    }
227    convertChangesToFileReplacements(*Change, &FileToReplaces);
228  }
229
230private:
231  const std::vector<std::string> &NewNames, &PrevNames;
232  const std::vector<std::vector<std::string>> &USRList;
233  std::map<std::stringtooling::Replacements> &FileToReplaces;
234  bool PrintLocations;
235};
236
237// A renamer to rename symbols which are identified by a give USRList to
238// new name.
239//
240// FIXME: Merge with the above RenamingASTConsumer.
241class USRSymbolRenamer : public ASTConsumer {
242public:
243  USRSymbolRenamer(const std::vector<std::string> &NewNames,
244                   const std::vector<std::vector<std::string>> &USRList,
245                   std::map<std::stringtooling::Replacements> &FileToReplaces)
246      : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
247    assert(USRList.size() == NewNames.size());
248  }
249
250  void HandleTranslationUnit(ASTContext &Context) override {
251    for (unsigned I = 0I < NewNames.size(); ++I) {
252      // FIXME: Apply AtomicChanges directly once the refactoring APIs are
253      // ready.
254      auto AtomicChanges = tooling::createRenameAtomicChanges(
255          USRList[I], NewNames[I], Context.getTranslationUnitDecl());
256      convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
257    }
258  }
259
260private:
261  const std::vector<std::string> &NewNames;
262  const std::vector<std::vector<std::string>> &USRList;
263  std::map<std::stringtooling::Replacements> &FileToReplaces;
264};
265
266std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
267  return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
268                                                FileToReplaces, PrintLocations);
269}
270
271std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
272  return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
273}
274
275// end namespace tooling
276// end namespace clang
277
clang::tooling::RenamingASTConsumer::HandleTranslationUnit
clang::tooling::USRSymbolRenamer::HandleTranslationUnit