xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/RefactoringCallbacks.cpp (revision e25152834cdf3b353892835a4f3b157e066a8ed4)
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 
RefactoringCallback()220b57cec5SDimitry Andric RefactoringCallback::RefactoringCallback() {}
getReplacements()230b57cec5SDimitry Andric tooling::Replacements &RefactoringCallback::getReplacements() {
240b57cec5SDimitry Andric   return Replace;
250b57cec5SDimitry Andric }
260b57cec5SDimitry Andric 
ASTMatchRefactorer(std::map<std::string,Replacements> & FileToReplaces)270b57cec5SDimitry Andric ASTMatchRefactorer::ASTMatchRefactorer(
280b57cec5SDimitry Andric     std::map<std::string, Replacements> &FileToReplaces)
290b57cec5SDimitry Andric     : FileToReplaces(FileToReplaces) {}
300b57cec5SDimitry Andric 
addDynamicMatcher(const ast_matchers::internal::DynTypedMatcher & Matcher,RefactoringCallback * Callback)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:
RefactoringASTConsumer(ASTMatchRefactorer & Refactoring)400b57cec5SDimitry Andric   explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
410b57cec5SDimitry Andric       : Refactoring(Refactoring) {}
420b57cec5SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)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 =
53*5ffd83dbSDimitry Andric             Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
54*5ffd83dbSDimitry Andric                 .add(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 
newASTConsumer()680b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
69a7dea167SDimitry Andric   return std::make_unique<RefactoringASTConsumer>(*this);
700b57cec5SDimitry Andric }
710b57cec5SDimitry Andric 
replaceStmtWithText(SourceManager & Sources,const Stmt & From,StringRef Text)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 }
replaceStmtWithStmt(SourceManager & Sources,const Stmt & From,const Stmt & To)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 
ReplaceStmtWithText(StringRef FromId,StringRef ToText)850b57cec5SDimitry Andric ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
86*5ffd83dbSDimitry Andric     : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
870b57cec5SDimitry Andric 
run(const ast_matchers::MatchFinder::MatchResult & Result)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 
ReplaceStmtWithStmt(StringRef FromId,StringRef ToId)1030b57cec5SDimitry Andric ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
104*5ffd83dbSDimitry Andric     : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
1050b57cec5SDimitry Andric 
run(const ast_matchers::MatchFinder::MatchResult & Result)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 
ReplaceIfStmtWithItsBody(StringRef Id,bool PickTrueBranch)1220b57cec5SDimitry Andric ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
1230b57cec5SDimitry Andric                                                    bool PickTrueBranch)
124*5ffd83dbSDimitry Andric     : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
1250b57cec5SDimitry Andric 
run(const ast_matchers::MatchFinder::MatchResult & Result)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 
ReplaceNodeWithTemplate(llvm::StringRef FromId,std::vector<TemplateElement> Template)1540b57cec5SDimitry Andric ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
1550b57cec5SDimitry Andric     llvm::StringRef FromId, std::vector<TemplateElement> Template)
156*5ffd83dbSDimitry Andric     : FromId(std::string(FromId)), Template(std::move(Template)) {}
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
create(StringRef FromId,StringRef ToTemplate)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         }
175*5ffd83dbSDimitry Andric         std::string SourceNodeName = std::string(
176*5ffd83dbSDimitry 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);
188*5ffd83dbSDimitry Andric       ParsedTemplate.push_back(TemplateElement{
189*5ffd83dbSDimitry Andric           TemplateElement::Literal,
190*5ffd83dbSDimitry Andric           std::string(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 
run(const ast_matchers::MatchFinder::MatchResult & Result)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