xref: /llvm-project/clang/lib/Tooling/RefactoringCallbacks.cpp (revision adcd02683856c30ba6f349279509acecd90063df)
11975e034SDaniel Jasper //===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
21975e034SDaniel Jasper //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61975e034SDaniel Jasper //
71975e034SDaniel Jasper //===----------------------------------------------------------------------===//
81975e034SDaniel Jasper //
91975e034SDaniel Jasper //
101975e034SDaniel Jasper //===----------------------------------------------------------------------===//
111975e034SDaniel Jasper #include "clang/Tooling/RefactoringCallbacks.h"
12fc9213e0SEric Liu #include "clang/ASTMatchers/ASTMatchFinder.h"
13fc9213e0SEric Liu #include "clang/Basic/SourceLocation.h"
14fc9213e0SEric Liu #include "clang/Lex/Lexer.h"
15fc9213e0SEric Liu 
16fc9213e0SEric Liu using llvm::StringError;
17fc9213e0SEric Liu using llvm::make_error;
181975e034SDaniel Jasper 
191975e034SDaniel Jasper namespace clang {
206389dd14SDaniel Jasper namespace tooling {
211975e034SDaniel Jasper 
RefactoringCallback()221975e034SDaniel Jasper RefactoringCallback::RefactoringCallback() {}
getReplacements()231975e034SDaniel Jasper tooling::Replacements &RefactoringCallback::getReplacements() {
241975e034SDaniel Jasper   return Replace;
251975e034SDaniel Jasper }
261975e034SDaniel Jasper 
ASTMatchRefactorer(std::map<std::string,Replacements> & FileToReplaces)27fc9213e0SEric Liu ASTMatchRefactorer::ASTMatchRefactorer(
28fc9213e0SEric Liu     std::map<std::string, Replacements> &FileToReplaces)
29fc9213e0SEric Liu     : FileToReplaces(FileToReplaces) {}
30fc9213e0SEric Liu 
addDynamicMatcher(const ast_matchers::internal::DynTypedMatcher & Matcher,RefactoringCallback * Callback)31fc9213e0SEric Liu void ASTMatchRefactorer::addDynamicMatcher(
32fc9213e0SEric Liu     const ast_matchers::internal::DynTypedMatcher &Matcher,
33fc9213e0SEric Liu     RefactoringCallback *Callback) {
34fc9213e0SEric Liu   MatchFinder.addDynamicMatcher(Matcher, Callback);
35fc9213e0SEric Liu   Callbacks.push_back(Callback);
361975e034SDaniel Jasper }
37fc9213e0SEric Liu 
38fc9213e0SEric Liu class RefactoringASTConsumer : public ASTConsumer {
39fc9213e0SEric Liu public:
RefactoringASTConsumer(ASTMatchRefactorer & Refactoring)406e0bc3fcSAlexander Shaposhnikov   explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
41fc9213e0SEric Liu       : Refactoring(Refactoring) {}
42fc9213e0SEric Liu 
HandleTranslationUnit(ASTContext & Context)43fc9213e0SEric Liu   void HandleTranslationUnit(ASTContext &Context) override {
44fc9213e0SEric Liu     // The ASTMatchRefactorer is re-used between translation units.
45fc9213e0SEric Liu     // Clear the matchers so that each Replacement is only emitted once.
46fc9213e0SEric Liu     for (const auto &Callback : Refactoring.Callbacks) {
47fc9213e0SEric Liu       Callback->getReplacements().clear();
48fc9213e0SEric Liu     }
49fc9213e0SEric Liu     Refactoring.MatchFinder.matchAST(Context);
50fc9213e0SEric Liu     for (const auto &Callback : Refactoring.Callbacks) {
51fc9213e0SEric Liu       for (const auto &Replacement : Callback->getReplacements()) {
52fc9213e0SEric Liu         llvm::Error Err =
53*adcd0268SBenjamin Kramer             Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
54*adcd0268SBenjamin Kramer                 .add(Replacement);
55fc9213e0SEric Liu         if (Err) {
56fc9213e0SEric Liu           llvm::errs() << "Skipping replacement " << Replacement.toString()
57fc9213e0SEric Liu                        << " due to this error:\n"
58fc9213e0SEric Liu                        << toString(std::move(Err)) << "\n";
59fc9213e0SEric Liu         }
60fc9213e0SEric Liu       }
61fc9213e0SEric Liu     }
62fc9213e0SEric Liu   }
63fc9213e0SEric Liu 
64fc9213e0SEric Liu private:
65fc9213e0SEric Liu   ASTMatchRefactorer &Refactoring;
66fc9213e0SEric Liu };
67fc9213e0SEric Liu 
newASTConsumer()68fc9213e0SEric Liu std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
692b3d49b6SJonas Devlieghere   return std::make_unique<RefactoringASTConsumer>(*this);
70fc9213e0SEric Liu }
71fc9213e0SEric Liu 
replaceStmtWithText(SourceManager & Sources,const Stmt & From,StringRef Text)72fc9213e0SEric Liu static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
73fc9213e0SEric Liu                                        StringRef Text) {
74fc9213e0SEric Liu   return tooling::Replacement(
75fc9213e0SEric Liu       Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
76fc9213e0SEric Liu }
replaceStmtWithStmt(SourceManager & Sources,const Stmt & From,const Stmt & To)77fc9213e0SEric Liu static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
781975e034SDaniel Jasper                                        const Stmt &To) {
79fc9213e0SEric Liu   return replaceStmtWithText(
80fc9213e0SEric Liu       Sources, From,
81fc9213e0SEric Liu       Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
821975e034SDaniel Jasper                            Sources, LangOptions()));
831975e034SDaniel Jasper }
841975e034SDaniel Jasper 
ReplaceStmtWithText(StringRef FromId,StringRef ToText)851975e034SDaniel Jasper ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
86*adcd0268SBenjamin Kramer     : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
871975e034SDaniel Jasper 
run(const ast_matchers::MatchFinder::MatchResult & Result)886389dd14SDaniel Jasper void ReplaceStmtWithText::run(
896389dd14SDaniel Jasper     const ast_matchers::MatchFinder::MatchResult &Result) {
907cdc705bSAlexander Kornienko   if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
9140ef2fb3SEric Liu     auto Err = Replace.add(tooling::Replacement(
921975e034SDaniel Jasper         *Result.SourceManager,
9340ef2fb3SEric Liu         CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
9440ef2fb3SEric Liu     // FIXME: better error handling. For now, just print error message in the
9540ef2fb3SEric Liu     // release version.
961ec383c7SPiotr Padlewski     if (Err) {
9740ef2fb3SEric Liu       llvm::errs() << llvm::toString(std::move(Err)) << "\n";
981ec383c7SPiotr Padlewski       assert(false);
991ec383c7SPiotr Padlewski     }
1001975e034SDaniel Jasper   }
1011975e034SDaniel Jasper }
1021975e034SDaniel Jasper 
ReplaceStmtWithStmt(StringRef FromId,StringRef ToId)1031975e034SDaniel Jasper ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
104*adcd0268SBenjamin Kramer     : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
1051975e034SDaniel Jasper 
run(const ast_matchers::MatchFinder::MatchResult & Result)1066389dd14SDaniel Jasper void ReplaceStmtWithStmt::run(
1076389dd14SDaniel Jasper     const ast_matchers::MatchFinder::MatchResult &Result) {
1087cdc705bSAlexander Kornienko   const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
1097cdc705bSAlexander Kornienko   const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
11040ef2fb3SEric Liu   if (FromMatch && ToMatch) {
11140ef2fb3SEric Liu     auto Err = Replace.add(
11240ef2fb3SEric Liu         replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
11340ef2fb3SEric Liu     // FIXME: better error handling. For now, just print error message in the
11440ef2fb3SEric Liu     // release version.
1151ec383c7SPiotr Padlewski     if (Err) {
11640ef2fb3SEric Liu       llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1171ec383c7SPiotr Padlewski       assert(false);
1181ec383c7SPiotr Padlewski     }
11940ef2fb3SEric Liu   }
1201975e034SDaniel Jasper }
1211975e034SDaniel Jasper 
ReplaceIfStmtWithItsBody(StringRef Id,bool PickTrueBranch)1221975e034SDaniel Jasper ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
1231975e034SDaniel Jasper                                                    bool PickTrueBranch)
124*adcd0268SBenjamin Kramer     : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
1251975e034SDaniel Jasper 
run(const ast_matchers::MatchFinder::MatchResult & Result)1266389dd14SDaniel Jasper void ReplaceIfStmtWithItsBody::run(
1276389dd14SDaniel Jasper     const ast_matchers::MatchFinder::MatchResult &Result) {
1287cdc705bSAlexander Kornienko   if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
1291975e034SDaniel Jasper     const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
1301975e034SDaniel Jasper     if (Body) {
13140ef2fb3SEric Liu       auto Err =
13240ef2fb3SEric Liu           Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
13340ef2fb3SEric Liu       // FIXME: better error handling. For now, just print error message in the
13440ef2fb3SEric Liu       // release version.
1351ec383c7SPiotr Padlewski       if (Err) {
13640ef2fb3SEric Liu         llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1371ec383c7SPiotr Padlewski         assert(false);
1381ec383c7SPiotr Padlewski       }
1391975e034SDaniel Jasper     } else if (!PickTrueBranch) {
1401975e034SDaniel Jasper       // If we want to use the 'else'-branch, but it doesn't exist, delete
1411975e034SDaniel Jasper       // the whole 'if'.
14240ef2fb3SEric Liu       auto Err =
14340ef2fb3SEric Liu           Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
14440ef2fb3SEric Liu       // FIXME: better error handling. For now, just print error message in the
14540ef2fb3SEric Liu       // release version.
1461ec383c7SPiotr Padlewski       if (Err) {
14740ef2fb3SEric Liu         llvm::errs() << llvm::toString(std::move(Err)) << "\n";
1481ec383c7SPiotr Padlewski         assert(false);
1491ec383c7SPiotr Padlewski       }
1501975e034SDaniel Jasper     }
1511975e034SDaniel Jasper   }
1521975e034SDaniel Jasper }
1531975e034SDaniel Jasper 
ReplaceNodeWithTemplate(llvm::StringRef FromId,std::vector<TemplateElement> Template)154fc9213e0SEric Liu ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
1556e0bc3fcSAlexander Shaposhnikov     llvm::StringRef FromId, std::vector<TemplateElement> Template)
156*adcd0268SBenjamin Kramer     : FromId(std::string(FromId)), Template(std::move(Template)) {}
157fc9213e0SEric Liu 
158fc9213e0SEric Liu llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
create(StringRef FromId,StringRef ToTemplate)159fc9213e0SEric Liu ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
160fc9213e0SEric Liu   std::vector<TemplateElement> ParsedTemplate;
161fc9213e0SEric Liu   for (size_t Index = 0; Index < ToTemplate.size();) {
162fc9213e0SEric Liu     if (ToTemplate[Index] == '$') {
163fc9213e0SEric Liu       if (ToTemplate.substr(Index, 2) == "$$") {
164fc9213e0SEric Liu         Index += 2;
165fc9213e0SEric Liu         ParsedTemplate.push_back(
166fc9213e0SEric Liu             TemplateElement{TemplateElement::Literal, "$"});
167fc9213e0SEric Liu       } else if (ToTemplate.substr(Index, 2) == "${") {
168fc9213e0SEric Liu         size_t EndOfIdentifier = ToTemplate.find("}", Index);
169fc9213e0SEric Liu         if (EndOfIdentifier == std::string::npos) {
170fc9213e0SEric Liu           return make_error<StringError>(
171fc9213e0SEric Liu               "Unterminated ${...} in replacement template near " +
172fc9213e0SEric Liu                   ToTemplate.substr(Index),
1731ced7e12SNAKAMURA Takumi               llvm::inconvertibleErrorCode());
174fc9213e0SEric Liu         }
175*adcd0268SBenjamin Kramer         std::string SourceNodeName = std::string(
176*adcd0268SBenjamin Kramer             ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));
177fc9213e0SEric Liu         ParsedTemplate.push_back(
178fc9213e0SEric Liu             TemplateElement{TemplateElement::Identifier, SourceNodeName});
179fc9213e0SEric Liu         Index = EndOfIdentifier + 1;
180fc9213e0SEric Liu       } else {
181fc9213e0SEric Liu         return make_error<StringError>(
182fc9213e0SEric Liu             "Invalid $ in replacement template near " +
183fc9213e0SEric Liu                 ToTemplate.substr(Index),
1841ced7e12SNAKAMURA Takumi             llvm::inconvertibleErrorCode());
185fc9213e0SEric Liu       }
186fc9213e0SEric Liu     } else {
187fc9213e0SEric Liu       size_t NextIndex = ToTemplate.find('$', Index + 1);
188*adcd0268SBenjamin Kramer       ParsedTemplate.push_back(TemplateElement{
189*adcd0268SBenjamin Kramer           TemplateElement::Literal,
190*adcd0268SBenjamin Kramer           std::string(ToTemplate.substr(Index, NextIndex - Index))});
191fc9213e0SEric Liu       Index = NextIndex;
192fc9213e0SEric Liu     }
193fc9213e0SEric Liu   }
194fc9213e0SEric Liu   return std::unique_ptr<ReplaceNodeWithTemplate>(
195fc9213e0SEric Liu       new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
196fc9213e0SEric Liu }
197fc9213e0SEric Liu 
run(const ast_matchers::MatchFinder::MatchResult & Result)198fc9213e0SEric Liu void ReplaceNodeWithTemplate::run(
199fc9213e0SEric Liu     const ast_matchers::MatchFinder::MatchResult &Result) {
200fc9213e0SEric Liu   const auto &NodeMap = Result.Nodes.getMap();
201fc9213e0SEric Liu 
202fc9213e0SEric Liu   std::string ToText;
203fc9213e0SEric Liu   for (const auto &Element : Template) {
204fc9213e0SEric Liu     switch (Element.Type) {
205fc9213e0SEric Liu     case TemplateElement::Literal:
206fc9213e0SEric Liu       ToText += Element.Value;
207fc9213e0SEric Liu       break;
208fc9213e0SEric Liu     case TemplateElement::Identifier: {
209fc9213e0SEric Liu       auto NodeIter = NodeMap.find(Element.Value);
210fc9213e0SEric Liu       if (NodeIter == NodeMap.end()) {
211fc9213e0SEric Liu         llvm::errs() << "Node " << Element.Value
212fc9213e0SEric Liu                      << " used in replacement template not bound in Matcher \n";
213fc9213e0SEric Liu         llvm::report_fatal_error("Unbound node in replacement template.");
214fc9213e0SEric Liu       }
215fc9213e0SEric Liu       CharSourceRange Source =
216fc9213e0SEric Liu           CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
217fc9213e0SEric Liu       ToText += Lexer::getSourceText(Source, *Result.SourceManager,
218fc9213e0SEric Liu                                      Result.Context->getLangOpts());
219fc9213e0SEric Liu       break;
220fc9213e0SEric Liu     }
221fc9213e0SEric Liu     }
222fc9213e0SEric Liu   }
223fc9213e0SEric Liu   if (NodeMap.count(FromId) == 0) {
224fc9213e0SEric Liu     llvm::errs() << "Node to be replaced " << FromId
225fc9213e0SEric Liu                  << " not bound in query.\n";
226fc9213e0SEric Liu     llvm::report_fatal_error("FromId node not bound in MatchResult");
227fc9213e0SEric Liu   }
228fc9213e0SEric Liu   auto Replacement =
229fc9213e0SEric Liu       tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
230fc9213e0SEric Liu                            Result.Context->getLangOpts());
231fc9213e0SEric Liu   llvm::Error Err = Replace.add(Replacement);
232fc9213e0SEric Liu   if (Err) {
233fc9213e0SEric Liu     llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
234fc9213e0SEric Liu                  << "! " << llvm::toString(std::move(Err)) << "\n";
235fc9213e0SEric Liu     llvm::report_fatal_error("Replacement failed");
236fc9213e0SEric Liu   }
237fc9213e0SEric Liu }
238fc9213e0SEric Liu 
2396389dd14SDaniel Jasper } // end namespace tooling
2401975e034SDaniel Jasper } // end namespace clang
241