blob: e3fc91afeb595675b19981bc136fd3df364443eb [file] [log] [blame]
Daniel Jasper7e222822012-07-16 09:18:17 +00001//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Daniel Jasper7e222822012-07-16 09:18:17 +00006//
7//===----------------------------------------------------------------------===//
8//
9//
10//===----------------------------------------------------------------------===//
Daniel Jasper1975e032012-07-17 08:03:01 +000011#include "clang/Tooling/RefactoringCallbacks.h"
Eric Liufc9213e2017-05-10 07:48:45 +000012#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Basic/SourceLocation.h"
14#include "clang/Lex/Lexer.h"
15
16using llvm::StringError;
17using llvm::make_error;
Daniel Jasper7e222822012-07-16 09:18:17 +000018
19namespace clang {
Daniel Jasper6389dd12012-07-17 08:37:03 +000020namespace tooling {
Daniel Jasper7e222822012-07-16 09:18:17 +000021
22RefactoringCallback::RefactoringCallback() {}
23tooling::Replacements &RefactoringCallback::getReplacements() {
24 return Replace;
25}
26
Eric Liufc9213e2017-05-10 07:48:45 +000027ASTMatchRefactorer::ASTMatchRefactorer(
28 std::map<std::string, Replacements> &FileToReplaces)
29 : FileToReplaces(FileToReplaces) {}
30
31void ASTMatchRefactorer::addDynamicMatcher(
32 const ast_matchers::internal::DynTypedMatcher &Matcher,
33 RefactoringCallback *Callback) {
34 MatchFinder.addDynamicMatcher(Matcher, Callback);
35 Callbacks.push_back(Callback);
Daniel Jasper7e222822012-07-16 09:18:17 +000036}
Eric Liufc9213e2017-05-10 07:48:45 +000037
38class RefactoringASTConsumer : public ASTConsumer {
39public:
Alexander Shaposhnikov6e0bc3f2017-05-12 00:16:56 +000040 explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
Eric Liufc9213e2017-05-10 07:48:45 +000041 : Refactoring(Refactoring) {}
42
43 void HandleTranslationUnit(ASTContext &Context) override {
44 // The ASTMatchRefactorer is re-used between translation units.
45 // Clear the matchers so that each Replacement is only emitted once.
46 for (const auto &Callback : Refactoring.Callbacks) {
47 Callback->getReplacements().clear();
48 }
49 Refactoring.MatchFinder.matchAST(Context);
50 for (const auto &Callback : Refactoring.Callbacks) {
51 for (const auto &Replacement : Callback->getReplacements()) {
52 llvm::Error Err =
Benjamin Krameradcd0262020-01-28 20:23:46 +010053 Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
54 .add(Replacement);
Eric Liufc9213e2017-05-10 07:48:45 +000055 if (Err) {
56 llvm::errs() << "Skipping replacement " << Replacement.toString()
57 << " due to this error:\n"
58 << toString(std::move(Err)) << "\n";
59 }
60 }
61 }
62 }
63
64private:
65 ASTMatchRefactorer &Refactoring;
66};
67
68std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
Jonas Devlieghere2b3d49b2019-08-14 23:04:18 +000069 return std::make_unique<RefactoringASTConsumer>(*this);
Eric Liufc9213e2017-05-10 07:48:45 +000070}
71
72static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
73 StringRef Text) {
74 return tooling::Replacement(
75 Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
76}
77static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
Daniel Jasper6389dd12012-07-17 08:37:03 +000078 const Stmt &To) {
Eric Liufc9213e2017-05-10 07:48:45 +000079 return replaceStmtWithText(
80 Sources, From,
81 Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
82 Sources, LangOptions()));
Daniel Jasper7e222822012-07-16 09:18:17 +000083}
84
85ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
Benjamin Krameradcd0262020-01-28 20:23:46 +010086 : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
Daniel Jasper7e222822012-07-16 09:18:17 +000087
Daniel Jasper6389dd12012-07-17 08:37:03 +000088void ReplaceStmtWithText::run(
89 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +000090 if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
Eric Liu40ef2fb2016-08-01 10:16:37 +000091 auto Err = Replace.add(tooling::Replacement(
Daniel Jasper7e222822012-07-16 09:18:17 +000092 *Result.SourceManager,
Eric Liu40ef2fb2016-08-01 10:16:37 +000093 CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
94 // FIXME: better error handling. For now, just print error message in the
95 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +000096 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +000097 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +000098 assert(false);
99 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000100 }
101}
102
103ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
Benjamin Krameradcd0262020-01-28 20:23:46 +0100104 : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
Daniel Jasper7e222822012-07-16 09:18:17 +0000105
Daniel Jasper6389dd12012-07-17 08:37:03 +0000106void ReplaceStmtWithStmt::run(
107 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +0000108 const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
109 const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
Eric Liu40ef2fb2016-08-01 10:16:37 +0000110 if (FromMatch && ToMatch) {
111 auto Err = Replace.add(
112 replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
113 // FIXME: better error handling. For now, just print error message in the
114 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000115 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000116 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000117 assert(false);
118 }
Eric Liu40ef2fb2016-08-01 10:16:37 +0000119 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000120}
121
122ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
123 bool PickTrueBranch)
Benjamin Krameradcd0262020-01-28 20:23:46 +0100124 : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
Daniel Jasper7e222822012-07-16 09:18:17 +0000125
Daniel Jasper6389dd12012-07-17 08:37:03 +0000126void ReplaceIfStmtWithItsBody::run(
127 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +0000128 if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
Daniel Jasper7e222822012-07-16 09:18:17 +0000129 const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
130 if (Body) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000131 auto Err =
132 Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
133 // FIXME: better error handling. For now, just print error message in the
134 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000135 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000136 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000137 assert(false);
138 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000139 } else if (!PickTrueBranch) {
140 // If we want to use the 'else'-branch, but it doesn't exist, delete
141 // the whole 'if'.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000142 auto Err =
143 Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
144 // FIXME: better error handling. For now, just print error message in the
145 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000146 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000147 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000148 assert(false);
149 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000150 }
151 }
152}
153
Eric Liufc9213e2017-05-10 07:48:45 +0000154ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
Alexander Shaposhnikov6e0bc3f2017-05-12 00:16:56 +0000155 llvm::StringRef FromId, std::vector<TemplateElement> Template)
Benjamin Krameradcd0262020-01-28 20:23:46 +0100156 : FromId(std::string(FromId)), Template(std::move(Template)) {}
Eric Liufc9213e2017-05-10 07:48:45 +0000157
158llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
159ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
160 std::vector<TemplateElement> ParsedTemplate;
161 for (size_t Index = 0; Index < ToTemplate.size();) {
162 if (ToTemplate[Index] == '$') {
163 if (ToTemplate.substr(Index, 2) == "$$") {
164 Index += 2;
165 ParsedTemplate.push_back(
166 TemplateElement{TemplateElement::Literal, "$"});
167 } else if (ToTemplate.substr(Index, 2) == "${") {
168 size_t EndOfIdentifier = ToTemplate.find("}", Index);
169 if (EndOfIdentifier == std::string::npos) {
170 return make_error<StringError>(
171 "Unterminated ${...} in replacement template near " +
172 ToTemplate.substr(Index),
NAKAMURA Takumi1ced7e12017-05-10 22:30:44 +0000173 llvm::inconvertibleErrorCode());
Eric Liufc9213e2017-05-10 07:48:45 +0000174 }
Benjamin Krameradcd0262020-01-28 20:23:46 +0100175 std::string SourceNodeName = std::string(
176 ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));
Eric Liufc9213e2017-05-10 07:48:45 +0000177 ParsedTemplate.push_back(
178 TemplateElement{TemplateElement::Identifier, SourceNodeName});
179 Index = EndOfIdentifier + 1;
180 } else {
181 return make_error<StringError>(
182 "Invalid $ in replacement template near " +
183 ToTemplate.substr(Index),
NAKAMURA Takumi1ced7e12017-05-10 22:30:44 +0000184 llvm::inconvertibleErrorCode());
Eric Liufc9213e2017-05-10 07:48:45 +0000185 }
186 } else {
187 size_t NextIndex = ToTemplate.find('$', Index + 1);
Benjamin Krameradcd0262020-01-28 20:23:46 +0100188 ParsedTemplate.push_back(TemplateElement{
189 TemplateElement::Literal,
190 std::string(ToTemplate.substr(Index, NextIndex - Index))});
Eric Liufc9213e2017-05-10 07:48:45 +0000191 Index = NextIndex;
192 }
193 }
194 return std::unique_ptr<ReplaceNodeWithTemplate>(
195 new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
196}
197
198void ReplaceNodeWithTemplate::run(
199 const ast_matchers::MatchFinder::MatchResult &Result) {
200 const auto &NodeMap = Result.Nodes.getMap();
201
202 std::string ToText;
203 for (const auto &Element : Template) {
204 switch (Element.Type) {
205 case TemplateElement::Literal:
206 ToText += Element.Value;
207 break;
208 case TemplateElement::Identifier: {
209 auto NodeIter = NodeMap.find(Element.Value);
210 if (NodeIter == NodeMap.end()) {
211 llvm::errs() << "Node " << Element.Value
212 << " used in replacement template not bound in Matcher \n";
213 llvm::report_fatal_error("Unbound node in replacement template.");
214 }
215 CharSourceRange Source =
216 CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
217 ToText += Lexer::getSourceText(Source, *Result.SourceManager,
218 Result.Context->getLangOpts());
219 break;
220 }
221 }
222 }
223 if (NodeMap.count(FromId) == 0) {
224 llvm::errs() << "Node to be replaced " << FromId
225 << " not bound in query.\n";
226 llvm::report_fatal_error("FromId node not bound in MatchResult");
227 }
228 auto Replacement =
229 tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
230 Result.Context->getLangOpts());
231 llvm::Error Err = Replace.add(Replacement);
232 if (Err) {
233 llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
234 << "! " << llvm::toString(std::move(Err)) << "\n";
235 llvm::report_fatal_error("Replacement failed");
236 }
237}
238
Daniel Jasper6389dd12012-07-17 08:37:03 +0000239} // end namespace tooling
Daniel Jasper7e222822012-07-16 09:18:17 +0000240} // end namespace clang