RenamingAction.cpp 10 KB

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