blob: ad0a10dab9b55885bb864dc4ea3d42f25698bd3e [file] [log] [blame]
Daniel Jasper7e222822012-07-16 09:18:17 +00001//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//
11//===----------------------------------------------------------------------===//
Daniel Jasper1975e032012-07-17 08:03:01 +000012#include "clang/Tooling/RefactoringCallbacks.h"
Eric Liufc9213e2017-05-10 07:48:45 +000013#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Lex/Lexer.h"
16
17using llvm::StringError;
18using llvm::make_error;
Daniel Jasper7e222822012-07-16 09:18:17 +000019
20namespace clang {
Daniel Jasper6389dd12012-07-17 08:37:03 +000021namespace tooling {
Daniel Jasper7e222822012-07-16 09:18:17 +000022
23RefactoringCallback::RefactoringCallback() {}
24tooling::Replacements &RefactoringCallback::getReplacements() {
25 return Replace;
26}
27
Eric Liufc9213e2017-05-10 07:48:45 +000028ASTMatchRefactorer::ASTMatchRefactorer(
29 std::map<std::string, Replacements> &FileToReplaces)
30 : FileToReplaces(FileToReplaces) {}
31
32void ASTMatchRefactorer::addDynamicMatcher(
33 const ast_matchers::internal::DynTypedMatcher &Matcher,
34 RefactoringCallback *Callback) {
35 MatchFinder.addDynamicMatcher(Matcher, Callback);
36 Callbacks.push_back(Callback);
Daniel Jasper7e222822012-07-16 09:18:17 +000037}
Eric Liufc9213e2017-05-10 07:48:45 +000038
39class RefactoringASTConsumer : public ASTConsumer {
40public:
41 RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
42 : Refactoring(Refactoring) {}
43
44 void HandleTranslationUnit(ASTContext &Context) override {
45 // The ASTMatchRefactorer is re-used between translation units.
46 // Clear the matchers so that each Replacement is only emitted once.
47 for (const auto &Callback : Refactoring.Callbacks) {
48 Callback->getReplacements().clear();
49 }
50 Refactoring.MatchFinder.matchAST(Context);
51 for (const auto &Callback : Refactoring.Callbacks) {
52 for (const auto &Replacement : Callback->getReplacements()) {
53 llvm::Error Err =
54 Refactoring.FileToReplaces[Replacement.getFilePath()].add(
55 Replacement);
56 if (Err) {
57 llvm::errs() << "Skipping replacement " << Replacement.toString()
58 << " due to this error:\n"
59 << toString(std::move(Err)) << "\n";
60 }
61 }
62 }
63 }
64
65private:
66 ASTMatchRefactorer &Refactoring;
67};
68
69std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
70 return llvm::make_unique<RefactoringASTConsumer>(*this);
71}
72
73static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
74 StringRef Text) {
75 return tooling::Replacement(
76 Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
77}
78static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
Daniel Jasper6389dd12012-07-17 08:37:03 +000079 const Stmt &To) {
Eric Liufc9213e2017-05-10 07:48:45 +000080 return replaceStmtWithText(
81 Sources, From,
82 Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
83 Sources, LangOptions()));
Daniel Jasper7e222822012-07-16 09:18:17 +000084}
85
86ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
87 : FromId(FromId), ToText(ToText) {}
88
Daniel Jasper6389dd12012-07-17 08:37:03 +000089void ReplaceStmtWithText::run(
90 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +000091 if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
Eric Liu40ef2fb2016-08-01 10:16:37 +000092 auto Err = Replace.add(tooling::Replacement(
Daniel Jasper7e222822012-07-16 09:18:17 +000093 *Result.SourceManager,
Eric Liu40ef2fb2016-08-01 10:16:37 +000094 CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
95 // FIXME: better error handling. For now, just print error message in the
96 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +000097 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +000098 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +000099 assert(false);
100 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000101 }
102}
103
104ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
105 : FromId(FromId), ToId(ToId) {}
106
Daniel Jasper6389dd12012-07-17 08:37:03 +0000107void ReplaceStmtWithStmt::run(
108 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +0000109 const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
110 const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
Eric Liu40ef2fb2016-08-01 10:16:37 +0000111 if (FromMatch && ToMatch) {
112 auto Err = Replace.add(
113 replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
114 // FIXME: better error handling. For now, just print error message in the
115 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000116 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000117 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000118 assert(false);
119 }
Eric Liu40ef2fb2016-08-01 10:16:37 +0000120 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000121}
122
123ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
124 bool PickTrueBranch)
125 : Id(Id), PickTrueBranch(PickTrueBranch) {}
126
Daniel Jasper6389dd12012-07-17 08:37:03 +0000127void ReplaceIfStmtWithItsBody::run(
128 const ast_matchers::MatchFinder::MatchResult &Result) {
Alexander Kornienko7cdc7052016-12-13 16:19:34 +0000129 if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
Daniel Jasper7e222822012-07-16 09:18:17 +0000130 const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
131 if (Body) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000132 auto Err =
133 Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
134 // FIXME: better error handling. For now, just print error message in the
135 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000136 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000137 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000138 assert(false);
139 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000140 } else if (!PickTrueBranch) {
141 // If we want to use the 'else'-branch, but it doesn't exist, delete
142 // the whole 'if'.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000143 auto Err =
144 Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
145 // FIXME: better error handling. For now, just print error message in the
146 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000147 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000148 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000149 assert(false);
150 }
Daniel Jasper7e222822012-07-16 09:18:17 +0000151 }
152 }
153}
154
Eric Liufc9213e2017-05-10 07:48:45 +0000155ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
156 llvm::StringRef FromId, std::vector<TemplateElement> &&Template)
157 : FromId(FromId), Template(Template) {}
158
159llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
160ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
161 std::vector<TemplateElement> ParsedTemplate;
162 for (size_t Index = 0; Index < ToTemplate.size();) {
163 if (ToTemplate[Index] == '$') {
164 if (ToTemplate.substr(Index, 2) == "$$") {
165 Index += 2;
166 ParsedTemplate.push_back(
167 TemplateElement{TemplateElement::Literal, "$"});
168 } else if (ToTemplate.substr(Index, 2) == "${") {
169 size_t EndOfIdentifier = ToTemplate.find("}", Index);
170 if (EndOfIdentifier == std::string::npos) {
171 return make_error<StringError>(
172 "Unterminated ${...} in replacement template near " +
173 ToTemplate.substr(Index),
NAKAMURA Takumi1ced7e12017-05-10 22:30:44 +0000174 llvm::inconvertibleErrorCode());
Eric Liufc9213e2017-05-10 07:48:45 +0000175 }
176 std::string SourceNodeName =
177 ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2);
178 ParsedTemplate.push_back(
179 TemplateElement{TemplateElement::Identifier, SourceNodeName});
180 Index = EndOfIdentifier + 1;
181 } else {
182 return make_error<StringError>(
183 "Invalid $ in replacement template near " +
184 ToTemplate.substr(Index),
NAKAMURA Takumi1ced7e12017-05-10 22:30:44 +0000185 llvm::inconvertibleErrorCode());
Eric Liufc9213e2017-05-10 07:48:45 +0000186 }
187 } else {
188 size_t NextIndex = ToTemplate.find('$', Index + 1);
189 ParsedTemplate.push_back(
190 TemplateElement{TemplateElement::Literal,
191 ToTemplate.substr(Index, NextIndex - Index)});
192 Index = NextIndex;
193 }
194 }
195 return std::unique_ptr<ReplaceNodeWithTemplate>(
196 new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
197}
198
199void ReplaceNodeWithTemplate::run(
200 const ast_matchers::MatchFinder::MatchResult &Result) {
201 const auto &NodeMap = Result.Nodes.getMap();
202
203 std::string ToText;
204 for (const auto &Element : Template) {
205 switch (Element.Type) {
206 case TemplateElement::Literal:
207 ToText += Element.Value;
208 break;
209 case TemplateElement::Identifier: {
210 auto NodeIter = NodeMap.find(Element.Value);
211 if (NodeIter == NodeMap.end()) {
212 llvm::errs() << "Node " << Element.Value
213 << " used in replacement template not bound in Matcher \n";
214 llvm::report_fatal_error("Unbound node in replacement template.");
215 }
216 CharSourceRange Source =
217 CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
218 ToText += Lexer::getSourceText(Source, *Result.SourceManager,
219 Result.Context->getLangOpts());
220 break;
221 }
222 }
223 }
224 if (NodeMap.count(FromId) == 0) {
225 llvm::errs() << "Node to be replaced " << FromId
226 << " not bound in query.\n";
227 llvm::report_fatal_error("FromId node not bound in MatchResult");
228 }
229 auto Replacement =
230 tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
231 Result.Context->getLangOpts());
232 llvm::Error Err = Replace.add(Replacement);
233 if (Err) {
234 llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
235 << "! " << llvm::toString(std::move(Err)) << "\n";
236 llvm::report_fatal_error("Replacement failed");
237 }
238}
239
Daniel Jasper6389dd12012-07-17 08:37:03 +0000240} // end namespace tooling
Daniel Jasper7e222822012-07-16 09:18:17 +0000241} // end namespace clang