1*0b57cec5SDimitry Andric //===--- RefactoringCallbacks.cpp - Structural query framework ------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // 10*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 11*0b57cec5SDimitry Andric #include "clang/Tooling/RefactoringCallbacks.h" 12*0b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 13*0b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h" 14*0b57cec5SDimitry Andric #include "clang/Lex/Lexer.h" 15*0b57cec5SDimitry Andric 16*0b57cec5SDimitry Andric using llvm::StringError; 17*0b57cec5SDimitry Andric using llvm::make_error; 18*0b57cec5SDimitry Andric 19*0b57cec5SDimitry Andric namespace clang { 20*0b57cec5SDimitry Andric namespace tooling { 21*0b57cec5SDimitry Andric 22*0b57cec5SDimitry Andric RefactoringCallback::RefactoringCallback() {} 23*0b57cec5SDimitry Andric tooling::Replacements &RefactoringCallback::getReplacements() { 24*0b57cec5SDimitry Andric return Replace; 25*0b57cec5SDimitry Andric } 26*0b57cec5SDimitry Andric 27*0b57cec5SDimitry Andric ASTMatchRefactorer::ASTMatchRefactorer( 28*0b57cec5SDimitry Andric std::map<std::string, Replacements> &FileToReplaces) 29*0b57cec5SDimitry Andric : FileToReplaces(FileToReplaces) {} 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric void ASTMatchRefactorer::addDynamicMatcher( 32*0b57cec5SDimitry Andric const ast_matchers::internal::DynTypedMatcher &Matcher, 33*0b57cec5SDimitry Andric RefactoringCallback *Callback) { 34*0b57cec5SDimitry Andric MatchFinder.addDynamicMatcher(Matcher, Callback); 35*0b57cec5SDimitry Andric Callbacks.push_back(Callback); 36*0b57cec5SDimitry Andric } 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric class RefactoringASTConsumer : public ASTConsumer { 39*0b57cec5SDimitry Andric public: 40*0b57cec5SDimitry Andric explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring) 41*0b57cec5SDimitry Andric : Refactoring(Refactoring) {} 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 44*0b57cec5SDimitry Andric // The ASTMatchRefactorer is re-used between translation units. 45*0b57cec5SDimitry Andric // Clear the matchers so that each Replacement is only emitted once. 46*0b57cec5SDimitry Andric for (const auto &Callback : Refactoring.Callbacks) { 47*0b57cec5SDimitry Andric Callback->getReplacements().clear(); 48*0b57cec5SDimitry Andric } 49*0b57cec5SDimitry Andric Refactoring.MatchFinder.matchAST(Context); 50*0b57cec5SDimitry Andric for (const auto &Callback : Refactoring.Callbacks) { 51*0b57cec5SDimitry Andric for (const auto &Replacement : Callback->getReplacements()) { 52*0b57cec5SDimitry Andric llvm::Error Err = 53*0b57cec5SDimitry Andric Refactoring.FileToReplaces[Replacement.getFilePath()].add( 54*0b57cec5SDimitry Andric Replacement); 55*0b57cec5SDimitry Andric if (Err) { 56*0b57cec5SDimitry Andric llvm::errs() << "Skipping replacement " << Replacement.toString() 57*0b57cec5SDimitry Andric << " due to this error:\n" 58*0b57cec5SDimitry Andric << toString(std::move(Err)) << "\n"; 59*0b57cec5SDimitry Andric } 60*0b57cec5SDimitry Andric } 61*0b57cec5SDimitry Andric } 62*0b57cec5SDimitry Andric } 63*0b57cec5SDimitry Andric 64*0b57cec5SDimitry Andric private: 65*0b57cec5SDimitry Andric ASTMatchRefactorer &Refactoring; 66*0b57cec5SDimitry Andric }; 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() { 69*0b57cec5SDimitry Andric return llvm::make_unique<RefactoringASTConsumer>(*this); 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From, 73*0b57cec5SDimitry Andric StringRef Text) { 74*0b57cec5SDimitry Andric return tooling::Replacement( 75*0b57cec5SDimitry Andric Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text); 76*0b57cec5SDimitry Andric } 77*0b57cec5SDimitry Andric static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From, 78*0b57cec5SDimitry Andric const Stmt &To) { 79*0b57cec5SDimitry Andric return replaceStmtWithText( 80*0b57cec5SDimitry Andric Sources, From, 81*0b57cec5SDimitry Andric Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()), 82*0b57cec5SDimitry Andric Sources, LangOptions())); 83*0b57cec5SDimitry Andric } 84*0b57cec5SDimitry Andric 85*0b57cec5SDimitry Andric ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText) 86*0b57cec5SDimitry Andric : FromId(FromId), ToText(ToText) {} 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric void ReplaceStmtWithText::run( 89*0b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 90*0b57cec5SDimitry Andric if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) { 91*0b57cec5SDimitry Andric auto Err = Replace.add(tooling::Replacement( 92*0b57cec5SDimitry Andric *Result.SourceManager, 93*0b57cec5SDimitry Andric CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText)); 94*0b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 95*0b57cec5SDimitry Andric // release version. 96*0b57cec5SDimitry Andric if (Err) { 97*0b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 98*0b57cec5SDimitry Andric assert(false); 99*0b57cec5SDimitry Andric } 100*0b57cec5SDimitry Andric } 101*0b57cec5SDimitry Andric } 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId) 104*0b57cec5SDimitry Andric : FromId(FromId), ToId(ToId) {} 105*0b57cec5SDimitry Andric 106*0b57cec5SDimitry Andric void ReplaceStmtWithStmt::run( 107*0b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 108*0b57cec5SDimitry Andric const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId); 109*0b57cec5SDimitry Andric const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId); 110*0b57cec5SDimitry Andric if (FromMatch && ToMatch) { 111*0b57cec5SDimitry Andric auto Err = Replace.add( 112*0b57cec5SDimitry Andric replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch)); 113*0b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 114*0b57cec5SDimitry Andric // release version. 115*0b57cec5SDimitry Andric if (Err) { 116*0b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 117*0b57cec5SDimitry Andric assert(false); 118*0b57cec5SDimitry Andric } 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric } 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id, 123*0b57cec5SDimitry Andric bool PickTrueBranch) 124*0b57cec5SDimitry Andric : Id(Id), PickTrueBranch(PickTrueBranch) {} 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric void ReplaceIfStmtWithItsBody::run( 127*0b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 128*0b57cec5SDimitry Andric if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) { 129*0b57cec5SDimitry Andric const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse(); 130*0b57cec5SDimitry Andric if (Body) { 131*0b57cec5SDimitry Andric auto Err = 132*0b57cec5SDimitry Andric Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body)); 133*0b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 134*0b57cec5SDimitry Andric // release version. 135*0b57cec5SDimitry Andric if (Err) { 136*0b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 137*0b57cec5SDimitry Andric assert(false); 138*0b57cec5SDimitry Andric } 139*0b57cec5SDimitry Andric } else if (!PickTrueBranch) { 140*0b57cec5SDimitry Andric // If we want to use the 'else'-branch, but it doesn't exist, delete 141*0b57cec5SDimitry Andric // the whole 'if'. 142*0b57cec5SDimitry Andric auto Err = 143*0b57cec5SDimitry Andric Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, "")); 144*0b57cec5SDimitry Andric // FIXME: better error handling. For now, just print error message in the 145*0b57cec5SDimitry Andric // release version. 146*0b57cec5SDimitry Andric if (Err) { 147*0b57cec5SDimitry Andric llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 148*0b57cec5SDimitry Andric assert(false); 149*0b57cec5SDimitry Andric } 150*0b57cec5SDimitry Andric } 151*0b57cec5SDimitry Andric } 152*0b57cec5SDimitry Andric } 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric ReplaceNodeWithTemplate::ReplaceNodeWithTemplate( 155*0b57cec5SDimitry Andric llvm::StringRef FromId, std::vector<TemplateElement> Template) 156*0b57cec5SDimitry Andric : FromId(FromId), Template(std::move(Template)) {} 157*0b57cec5SDimitry Andric 158*0b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>> 159*0b57cec5SDimitry Andric ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) { 160*0b57cec5SDimitry Andric std::vector<TemplateElement> ParsedTemplate; 161*0b57cec5SDimitry Andric for (size_t Index = 0; Index < ToTemplate.size();) { 162*0b57cec5SDimitry Andric if (ToTemplate[Index] == '$') { 163*0b57cec5SDimitry Andric if (ToTemplate.substr(Index, 2) == "$$") { 164*0b57cec5SDimitry Andric Index += 2; 165*0b57cec5SDimitry Andric ParsedTemplate.push_back( 166*0b57cec5SDimitry Andric TemplateElement{TemplateElement::Literal, "$"}); 167*0b57cec5SDimitry Andric } else if (ToTemplate.substr(Index, 2) == "${") { 168*0b57cec5SDimitry Andric size_t EndOfIdentifier = ToTemplate.find("}", Index); 169*0b57cec5SDimitry Andric if (EndOfIdentifier == std::string::npos) { 170*0b57cec5SDimitry Andric return make_error<StringError>( 171*0b57cec5SDimitry Andric "Unterminated ${...} in replacement template near " + 172*0b57cec5SDimitry Andric ToTemplate.substr(Index), 173*0b57cec5SDimitry Andric llvm::inconvertibleErrorCode()); 174*0b57cec5SDimitry Andric } 175*0b57cec5SDimitry Andric std::string SourceNodeName = 176*0b57cec5SDimitry Andric ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2); 177*0b57cec5SDimitry Andric ParsedTemplate.push_back( 178*0b57cec5SDimitry Andric TemplateElement{TemplateElement::Identifier, SourceNodeName}); 179*0b57cec5SDimitry Andric Index = EndOfIdentifier + 1; 180*0b57cec5SDimitry Andric } else { 181*0b57cec5SDimitry Andric return make_error<StringError>( 182*0b57cec5SDimitry Andric "Invalid $ in replacement template near " + 183*0b57cec5SDimitry Andric ToTemplate.substr(Index), 184*0b57cec5SDimitry Andric llvm::inconvertibleErrorCode()); 185*0b57cec5SDimitry Andric } 186*0b57cec5SDimitry Andric } else { 187*0b57cec5SDimitry Andric size_t NextIndex = ToTemplate.find('$', Index + 1); 188*0b57cec5SDimitry Andric ParsedTemplate.push_back( 189*0b57cec5SDimitry Andric TemplateElement{TemplateElement::Literal, 190*0b57cec5SDimitry Andric ToTemplate.substr(Index, NextIndex - Index)}); 191*0b57cec5SDimitry Andric Index = NextIndex; 192*0b57cec5SDimitry Andric } 193*0b57cec5SDimitry Andric } 194*0b57cec5SDimitry Andric return std::unique_ptr<ReplaceNodeWithTemplate>( 195*0b57cec5SDimitry Andric new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate))); 196*0b57cec5SDimitry Andric } 197*0b57cec5SDimitry Andric 198*0b57cec5SDimitry Andric void ReplaceNodeWithTemplate::run( 199*0b57cec5SDimitry Andric const ast_matchers::MatchFinder::MatchResult &Result) { 200*0b57cec5SDimitry Andric const auto &NodeMap = Result.Nodes.getMap(); 201*0b57cec5SDimitry Andric 202*0b57cec5SDimitry Andric std::string ToText; 203*0b57cec5SDimitry Andric for (const auto &Element : Template) { 204*0b57cec5SDimitry Andric switch (Element.Type) { 205*0b57cec5SDimitry Andric case TemplateElement::Literal: 206*0b57cec5SDimitry Andric ToText += Element.Value; 207*0b57cec5SDimitry Andric break; 208*0b57cec5SDimitry Andric case TemplateElement::Identifier: { 209*0b57cec5SDimitry Andric auto NodeIter = NodeMap.find(Element.Value); 210*0b57cec5SDimitry Andric if (NodeIter == NodeMap.end()) { 211*0b57cec5SDimitry Andric llvm::errs() << "Node " << Element.Value 212*0b57cec5SDimitry Andric << " used in replacement template not bound in Matcher \n"; 213*0b57cec5SDimitry Andric llvm::report_fatal_error("Unbound node in replacement template."); 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric CharSourceRange Source = 216*0b57cec5SDimitry Andric CharSourceRange::getTokenRange(NodeIter->second.getSourceRange()); 217*0b57cec5SDimitry Andric ToText += Lexer::getSourceText(Source, *Result.SourceManager, 218*0b57cec5SDimitry Andric Result.Context->getLangOpts()); 219*0b57cec5SDimitry Andric break; 220*0b57cec5SDimitry Andric } 221*0b57cec5SDimitry Andric } 222*0b57cec5SDimitry Andric } 223*0b57cec5SDimitry Andric if (NodeMap.count(FromId) == 0) { 224*0b57cec5SDimitry Andric llvm::errs() << "Node to be replaced " << FromId 225*0b57cec5SDimitry Andric << " not bound in query.\n"; 226*0b57cec5SDimitry Andric llvm::report_fatal_error("FromId node not bound in MatchResult"); 227*0b57cec5SDimitry Andric } 228*0b57cec5SDimitry Andric auto Replacement = 229*0b57cec5SDimitry Andric tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText, 230*0b57cec5SDimitry Andric Result.Context->getLangOpts()); 231*0b57cec5SDimitry Andric llvm::Error Err = Replace.add(Replacement); 232*0b57cec5SDimitry Andric if (Err) { 233*0b57cec5SDimitry Andric llvm::errs() << "Query and replace failed in " << Replacement.getFilePath() 234*0b57cec5SDimitry Andric << "! " << llvm::toString(std::move(Err)) << "\n"; 235*0b57cec5SDimitry Andric llvm::report_fatal_error("Replacement failed"); 236*0b57cec5SDimitry Andric } 237*0b57cec5SDimitry Andric } 238*0b57cec5SDimitry Andric 239*0b57cec5SDimitry Andric } // end namespace tooling 240*0b57cec5SDimitry Andric } // end namespace clang 241