Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
35295 views
//===--- RenamingAction.cpp - Clang refactoring library -------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// Provides an action to rename every symbol at a point.10///11//===----------------------------------------------------------------------===//1213#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>3637using namespace llvm;3839namespace clang {40namespace tooling {4142namespace {4344Expected<SymbolOccurrences>45findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) {46std::vector<std::string> USRs =47getUSRsForDeclaration(ND, Context.getASTContext());48std::string PrevName = ND->getNameAsString();49return getOccurrencesOfUSRs(USRs, PrevName,50Context.getASTContext().getTranslationUnitDecl());51}5253} // end anonymous namespace5455const RefactoringDescriptor &RenameOccurrences::describe() {56static const RefactoringDescriptor Descriptor = {57"local-rename",58"Rename",59"Finds and renames symbols in code with no indexer support",60};61return Descriptor;62}6364Expected<RenameOccurrences>65RenameOccurrences::initiate(RefactoringRuleContext &Context,66SourceRange SelectionRange, std::string NewName) {67const NamedDecl *ND =68getNamedDeclAt(Context.getASTContext(), SelectionRange.getBegin());69if (!ND)70return Context.createDiagnosticError(71SelectionRange.getBegin(), diag::err_refactor_selection_no_symbol);72return RenameOccurrences(getCanonicalSymbolDeclaration(ND),73std::move(NewName));74}7576const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }7778Expected<AtomicChanges>79RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {80Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);81if (!Occurrences)82return Occurrences.takeError();83// FIXME: Verify that the new name is valid.84SymbolName Name(NewName);85return createRenameReplacements(86*Occurrences, Context.getASTContext().getSourceManager(), Name);87}8889Expected<QualifiedRenameRule>90QualifiedRenameRule::initiate(RefactoringRuleContext &Context,91std::string OldQualifiedName,92std::string NewQualifiedName) {93const NamedDecl *ND =94getNamedDeclFor(Context.getASTContext(), OldQualifiedName);95if (!ND)96return llvm::make_error<llvm::StringError>("Could not find symbol " +97OldQualifiedName,98llvm::errc::invalid_argument);99return QualifiedRenameRule(ND, std::move(NewQualifiedName));100}101102const RefactoringDescriptor &QualifiedRenameRule::describe() {103static const RefactoringDescriptor Descriptor = {104/*Name=*/"local-qualified-rename",105/*Title=*/"Qualified Rename",106/*Description=*/107R"(Finds and renames qualified symbols in code within a translation unit.108It is used to move/rename a symbol to a new namespace/name:109* Supported symbols: classes, class members, functions, enums, and type alias.110* Renames all symbol occurrences from the old qualified name to the new111qualified name. All symbol references will be correctly qualified; For112symbol definitions, only name will be changed.113For example, rename "A::Foo" to "B::Bar":114Old code:115namespace foo {116class A {};117}118119namespace bar {120void f(foo::A a) {}121}122123New code after rename:124namespace foo {125class B {};126}127128namespace bar {129void f(B b) {}130})"131};132return Descriptor;133}134135Expected<AtomicChanges>136QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {137auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());138assert(!USRs.empty());139return tooling::createRenameAtomicChanges(140USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());141}142143Expected<std::vector<AtomicChange>>144createRenameReplacements(const SymbolOccurrences &Occurrences,145const SourceManager &SM, const SymbolName &NewName) {146// FIXME: A true local rename can use just one AtomicChange.147std::vector<AtomicChange> Changes;148for (const auto &Occurrence : Occurrences) {149ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();150assert(NewName.getNamePieces().size() == Ranges.size() &&151"Mismatching number of ranges and name pieces");152AtomicChange Change(SM, Ranges[0].getBegin());153for (const auto &Range : llvm::enumerate(Ranges)) {154auto Error =155Change.replace(SM, CharSourceRange::getCharRange(Range.value()),156NewName.getNamePieces()[Range.index()]);157if (Error)158return std::move(Error);159}160Changes.push_back(std::move(Change));161}162return std::move(Changes);163}164165/// Takes each atomic change and inserts its replacements into the set of166/// replacements that belong to the appropriate file.167static void convertChangesToFileReplacements(168ArrayRef<AtomicChange> AtomicChanges,169std::map<std::string, tooling::Replacements> *FileToReplaces) {170for (const auto &AtomicChange : AtomicChanges) {171for (const auto &Replace : AtomicChange.getReplacements()) {172llvm::Error Err =173(*FileToReplaces)[std::string(Replace.getFilePath())].add(Replace);174if (Err) {175llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "176<< llvm::toString(std::move(Err)) << "\n";177}178}179}180}181182class RenamingASTConsumer : public ASTConsumer {183public:184RenamingASTConsumer(185const std::vector<std::string> &NewNames,186const std::vector<std::string> &PrevNames,187const std::vector<std::vector<std::string>> &USRList,188std::map<std::string, tooling::Replacements> &FileToReplaces,189bool PrintLocations)190: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),191FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}192193void HandleTranslationUnit(ASTContext &Context) override {194for (unsigned I = 0; I < NewNames.size(); ++I) {195// If the previous name was not found, ignore this rename request.196if (PrevNames[I].empty())197continue;198199HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);200}201}202203void HandleOneRename(ASTContext &Context, const std::string &NewName,204const std::string &PrevName,205const std::vector<std::string> &USRs) {206const SourceManager &SourceMgr = Context.getSourceManager();207208SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(209USRs, PrevName, Context.getTranslationUnitDecl());210if (PrintLocations) {211for (const auto &Occurrence : Occurrences) {212FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),213SourceMgr);214errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)215<< ":" << FullLoc.getSpellingLineNumber() << ":"216<< FullLoc.getSpellingColumnNumber() << "\n";217}218}219// FIXME: Support multi-piece names.220// FIXME: better error handling (propagate error out).221SymbolName NewNameRef(NewName);222Expected<std::vector<AtomicChange>> Change =223createRenameReplacements(Occurrences, SourceMgr, NewNameRef);224if (!Change) {225llvm::errs() << "Failed to create renaming replacements for '" << PrevName226<< "'! " << llvm::toString(Change.takeError()) << "\n";227return;228}229convertChangesToFileReplacements(*Change, &FileToReplaces);230}231232private:233const std::vector<std::string> &NewNames, &PrevNames;234const std::vector<std::vector<std::string>> &USRList;235std::map<std::string, tooling::Replacements> &FileToReplaces;236bool PrintLocations;237};238239// A renamer to rename symbols which are identified by a give USRList to240// new name.241//242// FIXME: Merge with the above RenamingASTConsumer.243class USRSymbolRenamer : public ASTConsumer {244public:245USRSymbolRenamer(const std::vector<std::string> &NewNames,246const std::vector<std::vector<std::string>> &USRList,247std::map<std::string, tooling::Replacements> &FileToReplaces)248: NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {249assert(USRList.size() == NewNames.size());250}251252void HandleTranslationUnit(ASTContext &Context) override {253for (unsigned I = 0; I < NewNames.size(); ++I) {254// FIXME: Apply AtomicChanges directly once the refactoring APIs are255// ready.256auto AtomicChanges = tooling::createRenameAtomicChanges(257USRList[I], NewNames[I], Context.getTranslationUnitDecl());258convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);259}260}261262private:263const std::vector<std::string> &NewNames;264const std::vector<std::vector<std::string>> &USRList;265std::map<std::string, tooling::Replacements> &FileToReplaces;266};267268std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {269return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,270FileToReplaces, PrintLocations);271}272273std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {274return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);275}276277} // end namespace tooling278} // end namespace clang279280281