xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Tooling/RefactoringCallbacks.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg //
107330f729Sjoerg //===----------------------------------------------------------------------===//
117330f729Sjoerg #include "clang/Tooling/RefactoringCallbacks.h"
127330f729Sjoerg #include "clang/ASTMatchers/ASTMatchFinder.h"
137330f729Sjoerg #include "clang/Basic/SourceLocation.h"
147330f729Sjoerg #include "clang/Lex/Lexer.h"
157330f729Sjoerg 
167330f729Sjoerg using llvm::StringError;
177330f729Sjoerg using llvm::make_error;
187330f729Sjoerg 
197330f729Sjoerg namespace clang {
207330f729Sjoerg namespace tooling {
217330f729Sjoerg 
RefactoringCallback()227330f729Sjoerg RefactoringCallback::RefactoringCallback() {}
getReplacements()237330f729Sjoerg tooling::Replacements &RefactoringCallback::getReplacements() {
247330f729Sjoerg   return Replace;
257330f729Sjoerg }
267330f729Sjoerg 
ASTMatchRefactorer(std::map<std::string,Replacements> & FileToReplaces)277330f729Sjoerg ASTMatchRefactorer::ASTMatchRefactorer(
287330f729Sjoerg     std::map<std::string, Replacements> &FileToReplaces)
297330f729Sjoerg     : FileToReplaces(FileToReplaces) {}
307330f729Sjoerg 
addDynamicMatcher(const ast_matchers::internal::DynTypedMatcher & Matcher,RefactoringCallback * Callback)317330f729Sjoerg void ASTMatchRefactorer::addDynamicMatcher(
327330f729Sjoerg     const ast_matchers::internal::DynTypedMatcher &Matcher,
337330f729Sjoerg     RefactoringCallback *Callback) {
347330f729Sjoerg   MatchFinder.addDynamicMatcher(Matcher, Callback);
357330f729Sjoerg   Callbacks.push_back(Callback);
367330f729Sjoerg }
377330f729Sjoerg 
387330f729Sjoerg class RefactoringASTConsumer : public ASTConsumer {
397330f729Sjoerg public:
RefactoringASTConsumer(ASTMatchRefactorer & Refactoring)407330f729Sjoerg   explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
417330f729Sjoerg       : Refactoring(Refactoring) {}
427330f729Sjoerg 
HandleTranslationUnit(ASTContext & Context)437330f729Sjoerg   void HandleTranslationUnit(ASTContext &Context) override {
447330f729Sjoerg     // The ASTMatchRefactorer is re-used between translation units.
457330f729Sjoerg     // Clear the matchers so that each Replacement is only emitted once.
467330f729Sjoerg     for (const auto &Callback : Refactoring.Callbacks) {
477330f729Sjoerg       Callback->getReplacements().clear();
487330f729Sjoerg     }
497330f729Sjoerg     Refactoring.MatchFinder.matchAST(Context);
507330f729Sjoerg     for (const auto &Callback : Refactoring.Callbacks) {
517330f729Sjoerg       for (const auto &Replacement : Callback->getReplacements()) {
527330f729Sjoerg         llvm::Error Err =
53*e038c9c4Sjoerg             Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
54*e038c9c4Sjoerg                 .add(Replacement);
557330f729Sjoerg         if (Err) {
567330f729Sjoerg           llvm::errs() << "Skipping replacement " << Replacement.toString()
577330f729Sjoerg                        << " due to this error:\n"
587330f729Sjoerg                        << toString(std::move(Err)) << "\n";
597330f729Sjoerg         }
607330f729Sjoerg       }
617330f729Sjoerg     }
627330f729Sjoerg   }
637330f729Sjoerg 
647330f729Sjoerg private:
657330f729Sjoerg   ASTMatchRefactorer &Refactoring;
667330f729Sjoerg };
677330f729Sjoerg 
newASTConsumer()687330f729Sjoerg std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
697330f729Sjoerg   return std::make_unique<RefactoringASTConsumer>(*this);
707330f729Sjoerg }
717330f729Sjoerg 
replaceStmtWithText(SourceManager & Sources,const Stmt & From,StringRef Text)727330f729Sjoerg static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
737330f729Sjoerg                                        StringRef Text) {
747330f729Sjoerg   return tooling::Replacement(
757330f729Sjoerg       Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
767330f729Sjoerg }
replaceStmtWithStmt(SourceManager & Sources,const Stmt & From,const Stmt & To)777330f729Sjoerg static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
787330f729Sjoerg                                        const Stmt &To) {
797330f729Sjoerg   return replaceStmtWithText(
807330f729Sjoerg       Sources, From,
817330f729Sjoerg       Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
827330f729Sjoerg                            Sources, LangOptions()));
837330f729Sjoerg }
847330f729Sjoerg 
ReplaceStmtWithText(StringRef FromId,StringRef ToText)857330f729Sjoerg ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
86*e038c9c4Sjoerg     : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
877330f729Sjoerg 
run(const ast_matchers::MatchFinder::MatchResult & Result)887330f729Sjoerg void ReplaceStmtWithText::run(
897330f729Sjoerg     const ast_matchers::MatchFinder::MatchResult &Result) {
907330f729Sjoerg   if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
917330f729Sjoerg     auto Err = Replace.add(tooling::Replacement(
927330f729Sjoerg         *Result.SourceManager,
937330f729Sjoerg         CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
947330f729Sjoerg     // FIXME: better error handling. For now, just print error message in the
957330f729Sjoerg     // release version.
967330f729Sjoerg     if (Err) {
977330f729Sjoerg       llvm::errs() << llvm::toString(std::move(Err)) << "\n";
987330f729Sjoerg       assert(false);
997330f729Sjoerg     }
1007330f729Sjoerg   }
1017330f729Sjoerg }
1027330f729Sjoerg 
ReplaceStmtWithStmt(StringRef FromId,StringRef ToId)1037330f729Sjoerg ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
104*e038c9c4Sjoerg     : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
1057330f729Sjoerg 
run(const ast_matchers::MatchFinder::MatchResult & Result)1067330f729Sjoerg void ReplaceStmtWithStmt::run(
1077330f729Sjoerg     const ast_matchers::MatchFinder::MatchResult &Result) {
1087330f729Sjoerg   const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
1097330f729Sjoerg   const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
1107330f729Sjoerg   if (FromMatch && ToMatch) {
1117330f729Sjoerg     auto Err = Replace.add(
1127330f729Sjoerg         replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
1137330f729Sjoerg     // FIXME: better error handling. For now, just print error message in the
1147330f729Sjoerg     // release version.
1157330f729Sjoerg     if (Err) {
1167330f729Sjoerg       llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1177330f729Sjoerg       assert(false);
1187330f729Sjoerg     }
1197330f729Sjoerg   }
1207330f729Sjoerg }
1217330f729Sjoerg 
ReplaceIfStmtWithItsBody(StringRef Id,bool PickTrueBranch)1227330f729Sjoerg ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
1237330f729Sjoerg                                                    bool PickTrueBranch)
124*e038c9c4Sjoerg     : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
1257330f729Sjoerg 
run(const ast_matchers::MatchFinder::MatchResult & Result)1267330f729Sjoerg void ReplaceIfStmtWithItsBody::run(
1277330f729Sjoerg     const ast_matchers::MatchFinder::MatchResult &Result) {
1287330f729Sjoerg   if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
1297330f729Sjoerg     const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
1307330f729Sjoerg     if (Body) {
1317330f729Sjoerg       auto Err =
1327330f729Sjoerg           Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
1337330f729Sjoerg       // FIXME: better error handling. For now, just print error message in the
1347330f729Sjoerg       // release version.
1357330f729Sjoerg       if (Err) {
1367330f729Sjoerg         llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1377330f729Sjoerg         assert(false);
1387330f729Sjoerg       }
1397330f729Sjoerg     } else if (!PickTrueBranch) {
1407330f729Sjoerg       // If we want to use the 'else'-branch, but it doesn't exist, delete
1417330f729Sjoerg       // the whole 'if'.
1427330f729Sjoerg       auto Err =
1437330f729Sjoerg           Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
1447330f729Sjoerg       // FIXME: better error handling. For now, just print error message in the
1457330f729Sjoerg       // release version.
1467330f729Sjoerg       if (Err) {
1477330f729Sjoerg         llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1487330f729Sjoerg         assert(false);
1497330f729Sjoerg       }
1507330f729Sjoerg     }
1517330f729Sjoerg   }
1527330f729Sjoerg }
1537330f729Sjoerg 
ReplaceNodeWithTemplate(llvm::StringRef FromId,std::vector<TemplateElement> Template)1547330f729Sjoerg ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
1557330f729Sjoerg     llvm::StringRef FromId, std::vector<TemplateElement> Template)
156*e038c9c4Sjoerg     : FromId(std::string(FromId)), Template(std::move(Template)) {}
1577330f729Sjoerg 
1587330f729Sjoerg llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
create(StringRef FromId,StringRef ToTemplate)1597330f729Sjoerg ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
1607330f729Sjoerg   std::vector<TemplateElement> ParsedTemplate;
1617330f729Sjoerg   for (size_t Index = 0; Index < ToTemplate.size();) {
1627330f729Sjoerg     if (ToTemplate[Index] == '$') {
1637330f729Sjoerg       if (ToTemplate.substr(Index, 2) == "$$") {
1647330f729Sjoerg         Index += 2;
1657330f729Sjoerg         ParsedTemplate.push_back(
1667330f729Sjoerg             TemplateElement{TemplateElement::Literal, "$"});
1677330f729Sjoerg       } else if (ToTemplate.substr(Index, 2) == "${") {
1687330f729Sjoerg         size_t EndOfIdentifier = ToTemplate.find("}", Index);
1697330f729Sjoerg         if (EndOfIdentifier == std::string::npos) {
1707330f729Sjoerg           return make_error<StringError>(
1717330f729Sjoerg               "Unterminated ${...} in replacement template near " +
1727330f729Sjoerg                   ToTemplate.substr(Index),
1737330f729Sjoerg               llvm::inconvertibleErrorCode());
1747330f729Sjoerg         }
175*e038c9c4Sjoerg         std::string SourceNodeName = std::string(
176*e038c9c4Sjoerg             ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));
1777330f729Sjoerg         ParsedTemplate.push_back(
1787330f729Sjoerg             TemplateElement{TemplateElement::Identifier, SourceNodeName});
1797330f729Sjoerg         Index = EndOfIdentifier + 1;
1807330f729Sjoerg       } else {
1817330f729Sjoerg         return make_error<StringError>(
1827330f729Sjoerg             "Invalid $ in replacement template near " +
1837330f729Sjoerg                 ToTemplate.substr(Index),
1847330f729Sjoerg             llvm::inconvertibleErrorCode());
1857330f729Sjoerg       }
1867330f729Sjoerg     } else {
1877330f729Sjoerg       size_t NextIndex = ToTemplate.find('$', Index + 1);
188*e038c9c4Sjoerg       ParsedTemplate.push_back(TemplateElement{
189*e038c9c4Sjoerg           TemplateElement::Literal,
190*e038c9c4Sjoerg           std::string(ToTemplate.substr(Index, NextIndex - Index))});
1917330f729Sjoerg       Index = NextIndex;
1927330f729Sjoerg     }
1937330f729Sjoerg   }
1947330f729Sjoerg   return std::unique_ptr<ReplaceNodeWithTemplate>(
1957330f729Sjoerg       new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
1967330f729Sjoerg }
1977330f729Sjoerg 
run(const ast_matchers::MatchFinder::MatchResult & Result)1987330f729Sjoerg void ReplaceNodeWithTemplate::run(
1997330f729Sjoerg     const ast_matchers::MatchFinder::MatchResult &Result) {
2007330f729Sjoerg   const auto &NodeMap = Result.Nodes.getMap();
2017330f729Sjoerg 
2027330f729Sjoerg   std::string ToText;
2037330f729Sjoerg   for (const auto &Element : Template) {
2047330f729Sjoerg     switch (Element.Type) {
2057330f729Sjoerg     case TemplateElement::Literal:
2067330f729Sjoerg       ToText += Element.Value;
2077330f729Sjoerg       break;
2087330f729Sjoerg     case TemplateElement::Identifier: {
2097330f729Sjoerg       auto NodeIter = NodeMap.find(Element.Value);
2107330f729Sjoerg       if (NodeIter == NodeMap.end()) {
2117330f729Sjoerg         llvm::errs() << "Node " << Element.Value
2127330f729Sjoerg                      << " used in replacement template not bound in Matcher \n";
2137330f729Sjoerg         llvm::report_fatal_error("Unbound node in replacement template.");
2147330f729Sjoerg       }
2157330f729Sjoerg       CharSourceRange Source =
2167330f729Sjoerg           CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
2177330f729Sjoerg       ToText += Lexer::getSourceText(Source, *Result.SourceManager,
2187330f729Sjoerg                                      Result.Context->getLangOpts());
2197330f729Sjoerg       break;
2207330f729Sjoerg     }
2217330f729Sjoerg     }
2227330f729Sjoerg   }
2237330f729Sjoerg   if (NodeMap.count(FromId) == 0) {
2247330f729Sjoerg     llvm::errs() << "Node to be replaced " << FromId
2257330f729Sjoerg                  << " not bound in query.\n";
2267330f729Sjoerg     llvm::report_fatal_error("FromId node not bound in MatchResult");
2277330f729Sjoerg   }
2287330f729Sjoerg   auto Replacement =
2297330f729Sjoerg       tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
2307330f729Sjoerg                            Result.Context->getLangOpts());
2317330f729Sjoerg   llvm::Error Err = Replace.add(Replacement);
2327330f729Sjoerg   if (Err) {
2337330f729Sjoerg     llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
2347330f729Sjoerg                  << "! " << llvm::toString(std::move(Err)) << "\n";
2357330f729Sjoerg     llvm::report_fatal_error("Replacement failed");
2367330f729Sjoerg   }
2377330f729Sjoerg }
2387330f729Sjoerg 
2397330f729Sjoerg } // end namespace tooling
2407330f729Sjoerg } // end namespace clang
241