xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/RefactoringCallbacks.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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