10b57cec5SDimitry Andric //===--- RefactoringCallbacks.cpp - Structural query framework ------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // 100b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 110b57cec5SDimitry Andric #include "clang/Tooling/RefactoringCallbacks.h" 120b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 130b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h" 140b57cec5SDimitry Andric #include "clang/Lex/Lexer.h" 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric using llvm::StringError; 170b57cec5SDimitry Andric using llvm::make_error; 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric namespace clang { 200b57cec5SDimitry Andric namespace tooling { 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric RefactoringCallback::RefactoringCallback() {} 230b57cec5SDimitry Andric tooling::Replacements &RefactoringCallback::getReplacements() { 240b57cec5SDimitry Andric return Replace; 250b57cec5SDimitry Andric } 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric ASTMatchRefactorer::ASTMatchRefactorer( 280b57cec5SDimitry Andric std::map<std::string, Replacements> &FileToReplaces) 290b57cec5SDimitry Andric : FileToReplaces(FileToReplaces) {} 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric void ASTMatchRefactorer::addDynamicMatcher( 320b57cec5SDimitry Andric const ast_matchers::internal::DynTypedMatcher &Matcher, 330b57cec5SDimitry Andric RefactoringCallback *Callback) { 340b57cec5SDimitry Andric MatchFinder.addDynamicMatcher(Matcher, Callback); 350b57cec5SDimitry Andric Callbacks.push_back(Callback); 360b57cec5SDimitry Andric } 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric class RefactoringASTConsumer : public ASTConsumer { 390b57cec5SDimitry Andric public: 400b57cec5SDimitry Andric explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring) 410b57cec5SDimitry Andric : Refactoring(Refactoring) {} 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 440b57cec5SDimitry Andric // The ASTMatchRefactorer is re-used between translation units. 450b57cec5SDimitry Andric // Clear the matchers so that each Replacement is only emitted once. 460b57cec5SDimitry Andric for (const auto &Callback : Refactoring.Callbacks) { 470b57cec5SDimitry Andric Callback->getReplacements().clear(); 480b57cec5SDimitry Andric } 490b57cec5SDimitry Andric Refactoring.MatchFinder.matchAST(Context); 500b57cec5SDimitry Andric for (const auto &Callback : Refactoring.Callbacks) { 510b57cec5SDimitry Andric for (const auto &Replacement : Callback->getReplacements()) { 520b57cec5SDimitry Andric llvm::Error Err = 530b57cec5SDimitry Andric Refactoring.FileToReplaces[Replacement.getFilePath()].add( 540b57cec5SDimitry Andric Replacement); 550b57cec5SDimitry Andric if (Err) { 560b57cec5SDimitry Andric llvm::errs() << "Skipping replacement " << Replacement.toString() 570b57cec5SDimitry Andric << " due to this error:\n" 580b57cec5SDimitry Andric << toString(std::move(Err)) << "\n"; 590b57cec5SDimitry Andric } 600b57cec5SDimitry Andric } 610b57cec5SDimitry Andric } 620b57cec5SDimitry Andric } 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric private: 650b57cec5SDimitry Andric ASTMatchRefactorer &Refactoring; 660b57cec5SDimitry Andric }; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() { 69*a7dea167SDimitry Andric return std::make_unique<RefactoringASTConsumer>(*this); 700b57cec5SDimitry Andric } 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From, 730b57cec5SDimitry Andric StringRef Text) { 740b57cec5SDimitry Andric return tooling::Replacement( 750b57cec5SDimitry Andric Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text); 760b57cec5SDimitry Andric } 770b57cec5SDimitry Andric static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From, 780b57cec5SDimitry Andric const Stmt &To) { 790b57cec5SDimitry Andric return replaceStmtWithText( 800b57cec5SDimitry Andric Sources, From, 810b57cec5SDimitry Andric Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()), 820b57cec5SDimitry Andric Sources, LangOptions())); 830b57cec5SDimitry Andric } 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText) 860b57cec5SDimitry Andric : FromId(FromId), ToText(ToText) {} 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric void ReplaceStmtWithText::run( 890b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 900b57cec5SDimitry Andric if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) { 910b57cec5SDimitry Andric auto Err = Replace.add(tooling::Replacement( 920b57cec5SDimitry Andric *Result.SourceManager, 930b57cec5SDimitry Andric CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText)); 940b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 950b57cec5SDimitry Andric // release version. 960b57cec5SDimitry Andric if (Err) { 970b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 980b57cec5SDimitry Andric assert(false); 990b57cec5SDimitry Andric } 1000b57cec5SDimitry Andric } 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId) 1040b57cec5SDimitry Andric : FromId(FromId), ToId(ToId) {} 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric void ReplaceStmtWithStmt::run( 1070b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 1080b57cec5SDimitry Andric const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId); 1090b57cec5SDimitry Andric const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId); 1100b57cec5SDimitry Andric if (FromMatch && ToMatch) { 1110b57cec5SDimitry Andric auto Err = Replace.add( 1120b57cec5SDimitry Andric replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch)); 1130b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 1140b57cec5SDimitry Andric // release version. 1150b57cec5SDimitry Andric if (Err) { 1160b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 1170b57cec5SDimitry Andric assert(false); 1180b57cec5SDimitry Andric } 1190b57cec5SDimitry Andric } 1200b57cec5SDimitry Andric } 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id, 1230b57cec5SDimitry Andric bool PickTrueBranch) 1240b57cec5SDimitry Andric : Id(Id), PickTrueBranch(PickTrueBranch) {} 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric void ReplaceIfStmtWithItsBody::run( 1270b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 1280b57cec5SDimitry Andric if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) { 1290b57cec5SDimitry Andric const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse(); 1300b57cec5SDimitry Andric if (Body) { 1310b57cec5SDimitry Andric auto Err = 1320b57cec5SDimitry Andric Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body)); 1330b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 1340b57cec5SDimitry Andric // release version. 1350b57cec5SDimitry Andric if (Err) { 1360b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 1370b57cec5SDimitry Andric assert(false); 1380b57cec5SDimitry Andric } 1390b57cec5SDimitry Andric } else if (!PickTrueBranch) { 1400b57cec5SDimitry Andric // If we want to use the 'else'-branch, but it doesn't exist, delete 1410b57cec5SDimitry Andric // the whole 'if'. 1420b57cec5SDimitry Andric auto Err = 1430b57cec5SDimitry Andric Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, "")); 1440b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 1450b57cec5SDimitry Andric // release version. 1460b57cec5SDimitry Andric if (Err) { 1470b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 1480b57cec5SDimitry Andric assert(false); 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric } 1510b57cec5SDimitry Andric } 1520b57cec5SDimitry Andric } 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric ReplaceNodeWithTemplate::ReplaceNodeWithTemplate( 1550b57cec5SDimitry Andric llvm::StringRef FromId, std::vector<TemplateElement> Template) 1560b57cec5SDimitry Andric : FromId(FromId), Template(std::move(Template)) {} 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>> 1590b57cec5SDimitry Andric ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) { 1600b57cec5SDimitry Andric std::vector<TemplateElement> ParsedTemplate; 1610b57cec5SDimitry Andric for (size_t Index = 0; Index < ToTemplate.size();) { 1620b57cec5SDimitry Andric if (ToTemplate[Index] == '$') { 1630b57cec5SDimitry Andric if (ToTemplate.substr(Index, 2) == "$$") { 1640b57cec5SDimitry Andric Index += 2; 1650b57cec5SDimitry Andric ParsedTemplate.push_back( 1660b57cec5SDimitry Andric TemplateElement{TemplateElement::Literal, "$"}); 1670b57cec5SDimitry Andric } else if (ToTemplate.substr(Index, 2) == "${") { 1680b57cec5SDimitry Andric size_t EndOfIdentifier = ToTemplate.find("}", Index); 1690b57cec5SDimitry Andric if (EndOfIdentifier == std::string::npos) { 1700b57cec5SDimitry Andric return make_error<StringError>( 1710b57cec5SDimitry Andric "Unterminated ${...} in replacement template near " + 1720b57cec5SDimitry Andric ToTemplate.substr(Index), 1730b57cec5SDimitry Andric llvm::inconvertibleErrorCode()); 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric std::string SourceNodeName = 1760b57cec5SDimitry Andric ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2); 1770b57cec5SDimitry Andric ParsedTemplate.push_back( 1780b57cec5SDimitry Andric TemplateElement{TemplateElement::Identifier, SourceNodeName}); 1790b57cec5SDimitry Andric Index = EndOfIdentifier + 1; 1800b57cec5SDimitry Andric } else { 1810b57cec5SDimitry Andric return make_error<StringError>( 1820b57cec5SDimitry Andric "Invalid $ in replacement template near " + 1830b57cec5SDimitry Andric ToTemplate.substr(Index), 1840b57cec5SDimitry Andric llvm::inconvertibleErrorCode()); 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric } else { 1870b57cec5SDimitry Andric size_t NextIndex = ToTemplate.find('$', Index + 1); 1880b57cec5SDimitry Andric ParsedTemplate.push_back( 1890b57cec5SDimitry Andric TemplateElement{TemplateElement::Literal, 1900b57cec5SDimitry Andric ToTemplate.substr(Index, NextIndex - Index)}); 1910b57cec5SDimitry Andric Index = NextIndex; 1920b57cec5SDimitry Andric } 1930b57cec5SDimitry Andric } 1940b57cec5SDimitry Andric return std::unique_ptr<ReplaceNodeWithTemplate>( 1950b57cec5SDimitry Andric new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate))); 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric void ReplaceNodeWithTemplate::run( 1990b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 2000b57cec5SDimitry Andric const auto &NodeMap = Result.Nodes.getMap(); 2010b57cec5SDimitry Andric 2020b57cec5SDimitry Andric std::string ToText; 2030b57cec5SDimitry Andric for (const auto &Element : Template) { 2040b57cec5SDimitry Andric switch (Element.Type) { 2050b57cec5SDimitry Andric case TemplateElement::Literal: 2060b57cec5SDimitry Andric ToText += Element.Value; 2070b57cec5SDimitry Andric break; 2080b57cec5SDimitry Andric case TemplateElement::Identifier: { 2090b57cec5SDimitry Andric auto NodeIter = NodeMap.find(Element.Value); 2100b57cec5SDimitry Andric if (NodeIter == NodeMap.end()) { 2110b57cec5SDimitry Andric llvm::errs() << "Node " << Element.Value 2120b57cec5SDimitry Andric << " used in replacement template not bound in Matcher \n"; 2130b57cec5SDimitry Andric llvm::report_fatal_error("Unbound node in replacement template."); 2140b57cec5SDimitry Andric } 2150b57cec5SDimitry Andric CharSourceRange Source = 2160b57cec5SDimitry Andric CharSourceRange::getTokenRange(NodeIter->second.getSourceRange()); 2170b57cec5SDimitry Andric ToText += Lexer::getSourceText(Source, *Result.SourceManager, 2180b57cec5SDimitry Andric Result.Context->getLangOpts()); 2190b57cec5SDimitry Andric break; 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric } 2230b57cec5SDimitry Andric if (NodeMap.count(FromId) == 0) { 2240b57cec5SDimitry Andric llvm::errs() << "Node to be replaced " << FromId 2250b57cec5SDimitry Andric << " not bound in query.\n"; 2260b57cec5SDimitry Andric llvm::report_fatal_error("FromId node not bound in MatchResult"); 2270b57cec5SDimitry Andric } 2280b57cec5SDimitry Andric auto Replacement = 2290b57cec5SDimitry Andric tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText, 2300b57cec5SDimitry Andric Result.Context->getLangOpts()); 2310b57cec5SDimitry Andric llvm::Error Err = Replace.add(Replacement); 2320b57cec5SDimitry Andric if (Err) { 2330b57cec5SDimitry Andric llvm::errs() << "Query and replace failed in " << Replacement.getFilePath() 2340b57cec5SDimitry Andric << "! " << llvm::toString(std::move(Err)) << "\n"; 2350b57cec5SDimitry Andric llvm::report_fatal_error("Replacement failed"); 2360b57cec5SDimitry Andric } 2370b57cec5SDimitry Andric } 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric } // end namespace tooling 2400b57cec5SDimitry Andric } // end namespace clang 241