Path: blob/main/contrib/llvm-project/clang/lib/Tooling/RefactoringCallbacks.cpp
35233 views
//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//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//9//===----------------------------------------------------------------------===//10#include "clang/Tooling/RefactoringCallbacks.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Basic/SourceLocation.h"13#include "clang/Lex/Lexer.h"1415using llvm::StringError;16using llvm::make_error;1718namespace clang {19namespace tooling {2021RefactoringCallback::RefactoringCallback() {}22tooling::Replacements &RefactoringCallback::getReplacements() {23return Replace;24}2526ASTMatchRefactorer::ASTMatchRefactorer(27std::map<std::string, Replacements> &FileToReplaces)28: FileToReplaces(FileToReplaces) {}2930void ASTMatchRefactorer::addDynamicMatcher(31const ast_matchers::internal::DynTypedMatcher &Matcher,32RefactoringCallback *Callback) {33MatchFinder.addDynamicMatcher(Matcher, Callback);34Callbacks.push_back(Callback);35}3637class RefactoringASTConsumer : public ASTConsumer {38public:39explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)40: Refactoring(Refactoring) {}4142void HandleTranslationUnit(ASTContext &Context) override {43// The ASTMatchRefactorer is re-used between translation units.44// Clear the matchers so that each Replacement is only emitted once.45for (const auto &Callback : Refactoring.Callbacks) {46Callback->getReplacements().clear();47}48Refactoring.MatchFinder.matchAST(Context);49for (const auto &Callback : Refactoring.Callbacks) {50for (const auto &Replacement : Callback->getReplacements()) {51llvm::Error Err =52Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]53.add(Replacement);54if (Err) {55llvm::errs() << "Skipping replacement " << Replacement.toString()56<< " due to this error:\n"57<< toString(std::move(Err)) << "\n";58}59}60}61}6263private:64ASTMatchRefactorer &Refactoring;65};6667std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {68return std::make_unique<RefactoringASTConsumer>(*this);69}7071static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,72StringRef Text) {73return tooling::Replacement(74Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);75}76static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,77const Stmt &To) {78return replaceStmtWithText(79Sources, From,80Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),81Sources, LangOptions()));82}8384ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)85: FromId(std::string(FromId)), ToText(std::string(ToText)) {}8687void ReplaceStmtWithText::run(88const ast_matchers::MatchFinder::MatchResult &Result) {89if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {90auto Err = Replace.add(tooling::Replacement(91*Result.SourceManager,92CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));93// FIXME: better error handling. For now, just print error message in the94// release version.95if (Err) {96llvm::errs() << llvm::toString(std::move(Err)) << "\n";97assert(false);98}99}100}101102ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)103: FromId(std::string(FromId)), ToId(std::string(ToId)) {}104105void ReplaceStmtWithStmt::run(106const ast_matchers::MatchFinder::MatchResult &Result) {107const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);108const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);109if (FromMatch && ToMatch) {110auto Err = Replace.add(111replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));112// FIXME: better error handling. For now, just print error message in the113// release version.114if (Err) {115llvm::errs() << llvm::toString(std::move(Err)) << "\n";116assert(false);117}118}119}120121ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,122bool PickTrueBranch)123: Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}124125void ReplaceIfStmtWithItsBody::run(126const ast_matchers::MatchFinder::MatchResult &Result) {127if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {128const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();129if (Body) {130auto Err =131Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));132// FIXME: better error handling. For now, just print error message in the133// release version.134if (Err) {135llvm::errs() << llvm::toString(std::move(Err)) << "\n";136assert(false);137}138} else if (!PickTrueBranch) {139// If we want to use the 'else'-branch, but it doesn't exist, delete140// the whole 'if'.141auto Err =142Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));143// FIXME: better error handling. For now, just print error message in the144// release version.145if (Err) {146llvm::errs() << llvm::toString(std::move(Err)) << "\n";147assert(false);148}149}150}151}152153ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(154llvm::StringRef FromId, std::vector<TemplateElement> Template)155: FromId(std::string(FromId)), Template(std::move(Template)) {}156157llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>158ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {159std::vector<TemplateElement> ParsedTemplate;160for (size_t Index = 0; Index < ToTemplate.size();) {161if (ToTemplate[Index] == '$') {162if (ToTemplate.substr(Index, 2) == "$$") {163Index += 2;164ParsedTemplate.push_back(165TemplateElement{TemplateElement::Literal, "$"});166} else if (ToTemplate.substr(Index, 2) == "${") {167size_t EndOfIdentifier = ToTemplate.find("}", Index);168if (EndOfIdentifier == std::string::npos) {169return make_error<StringError>(170"Unterminated ${...} in replacement template near " +171ToTemplate.substr(Index),172llvm::inconvertibleErrorCode());173}174std::string SourceNodeName = std::string(175ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));176ParsedTemplate.push_back(177TemplateElement{TemplateElement::Identifier, SourceNodeName});178Index = EndOfIdentifier + 1;179} else {180return make_error<StringError>(181"Invalid $ in replacement template near " +182ToTemplate.substr(Index),183llvm::inconvertibleErrorCode());184}185} else {186size_t NextIndex = ToTemplate.find('$', Index + 1);187ParsedTemplate.push_back(TemplateElement{188TemplateElement::Literal,189std::string(ToTemplate.substr(Index, NextIndex - Index))});190Index = NextIndex;191}192}193return std::unique_ptr<ReplaceNodeWithTemplate>(194new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));195}196197void ReplaceNodeWithTemplate::run(198const ast_matchers::MatchFinder::MatchResult &Result) {199const auto &NodeMap = Result.Nodes.getMap();200201std::string ToText;202for (const auto &Element : Template) {203switch (Element.Type) {204case TemplateElement::Literal:205ToText += Element.Value;206break;207case TemplateElement::Identifier: {208auto NodeIter = NodeMap.find(Element.Value);209if (NodeIter == NodeMap.end()) {210llvm::errs() << "Node " << Element.Value211<< " used in replacement template not bound in Matcher \n";212llvm::report_fatal_error("Unbound node in replacement template.");213}214CharSourceRange Source =215CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());216ToText += Lexer::getSourceText(Source, *Result.SourceManager,217Result.Context->getLangOpts());218break;219}220}221}222if (NodeMap.count(FromId) == 0) {223llvm::errs() << "Node to be replaced " << FromId224<< " not bound in query.\n";225llvm::report_fatal_error("FromId node not bound in MatchResult");226}227auto Replacement =228tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,229Result.Context->getLangOpts());230llvm::Error Err = Replace.add(Replacement);231if (Err) {232llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()233<< "! " << llvm::toString(std::move(Err)) << "\n";234llvm::report_fatal_error("Replacement failed");235}236}237238} // end namespace tooling239} // end namespace clang240241242