brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.9 KiB · 52e003f Raw
271 lines · 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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// 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/Frontend/CompilerInstance.h"18#include "clang/Lex/Lexer.h"19#include "clang/Tooling/Refactoring/Rename/SymbolName.h"20#include "clang/Tooling/Refactoring/Rename/USRFinder.h"21#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"22#include "clang/Tooling/Refactoring/Rename/USRLocFinder.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 36Expected<SymbolOccurrences>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 namespace46 47const RefactoringDescriptor &RenameOccurrences::describe() {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 56Expected<RenameOccurrences>57RenameOccurrences::initiate(RefactoringRuleContext &Context,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 70Expected<AtomicChanges>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);77  return createRenameReplacements(78      *Occurrences, Context.getASTContext().getSourceManager(), Name);79}80 81Expected<QualifiedRenameRule>82QualifiedRenameRule::initiate(RefactoringRuleContext &Context,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 94const RefactoringDescriptor &QualifiedRenameRule::describe() {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 new103    qualified name. All symbol references will be correctly qualified; For104    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) {}113    }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 127Expected<AtomicChanges>128QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {129  auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());130  assert(!USRs.empty());131  return tooling::createRenameAtomicChanges(132      USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());133}134 135Expected<std::vector<AtomicChange>>136createRenameReplacements(const SymbolOccurrences &Occurrences,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 =147          Change.replace(SM, CharSourceRange::getCharRange(Range.value()),148                         NewName.getNamePieces()[Range.index()]);149      if (Error)150        return std::move(Error);151    }152    Changes.push_back(std::move(Change));153  }154  return std::move(Changes);155}156 157/// Takes each atomic change and inserts its replacements into the set of158/// replacements that belong to the appropriate file.159static void convertChangesToFileReplacements(160    ArrayRef<AtomicChange> AtomicChanges,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  }172}173 174class RenamingASTConsumer : public ASTConsumer {175public:176  RenamingASTConsumer(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 200    SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(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);214    Expected<std::vector<AtomicChange>> Change =215        createRenameReplacements(Occurrences, SourceMgr, NewNameRef);216    if (!Change) {217      llvm::errs() << "Failed to create renaming replacements for '" << PrevName218                   << "'! " << 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 to232// 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());242  }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 are247      // ready.248      auto AtomicChanges = tooling::createRenameAtomicChanges(249          USRList[I], NewNames[I], Context.getTranslationUnitDecl());250      convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);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 tooling270} // end namespace clang271