Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1286)

Side by Side Diff: tools/clang/refactor_message_loop/RefactorMessageLoop.cpp

Issue 1010073002: clang: Add a tool for MessageLoop refactoring (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More tests. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This tool performs the following transformations on base::MessageLoop and
6 // related APIs:
7 //
8 // MessageLoop:
9 // - base::MessageLoop::PostTask*
10 // => base::MessageLoop::task_runner()->PostTask*()
dcheng 2015/05/06 17:31:18 Dumb question: why not just update MessageLoop::Po
Sami 2015/05/07 10:37:37 There is some more discussion on the linked design
11 //
12 // Thread:
13 // - base::Thread::message_loop_proxy()
14 // => task_runner()
15 //
16 // MessageLoopProxy:
17 // - base::MessageLoopProxy
18 // => base::SingleThreadTaskRunner
19 // - scoped_refptr<base::MessageLoopProxy>
20 // => scoped_refptr<base::SingleThreadTaskRunner>
21 // - base::MessageLoopProxy::current()
22 // => base::ThreadTaskRunnerHandle::Get()
23 // - base::MessageLoop::message_loop_proxy()
24 // => base::MessageLoop::task_runner() (done)
25 //
26
27 #include <memory>
28 #include "clang/AST/ExprCXX.h"
29 #include "clang/ASTMatchers/ASTMatchers.h"
30 #include "clang/ASTMatchers/ASTMatchFinder.h"
31 #include "clang/Basic/SourceManager.h"
32 #include "clang/Frontend/FrontendActions.h"
33 #include "clang/Lex/Lexer.h"
34 #include "clang/Tooling/CommonOptionsParser.h"
35 #include "clang/Tooling/Refactoring.h"
36 #include "clang/Tooling/Tooling.h"
37 #include "llvm/Support/CommandLine.h"
38
39 using clang::ast_matchers::MatchFinder;
40 using clang::ast_matchers::anyOf;
41 using clang::ast_matchers::asString;
42 using clang::ast_matchers::callExpr;
43 using clang::ast_matchers::callee;
44 using clang::ast_matchers::decl;
45 using clang::ast_matchers::functionDecl;
46 using clang::ast_matchers::hasAncestor;
47 using clang::ast_matchers::hasAnyTemplateArgument;
48 using clang::ast_matchers::hasDescendant;
49 using clang::ast_matchers::hasName;
50 using clang::ast_matchers::hasType;
51 using clang::ast_matchers::isSameOrDerivedFrom;
52 using clang::ast_matchers::isTemplateInstantiation;
53 using clang::ast_matchers::memberCallExpr;
54 using clang::ast_matchers::methodDecl;
55 using clang::ast_matchers::ofClass;
56 using clang::ast_matchers::pointsTo;
57 using clang::ast_matchers::recordDecl;
58 using clang::ast_matchers::refersToType;
59 using clang::ast_matchers::templateSpecializationType;
60 using clang::ast_matchers::thisPointerType;
61 using clang::ast_matchers::unless;
62 using clang::ast_matchers::varDecl;
63
64 using clang::tooling::CommonOptionsParser;
65 using clang::tooling::Replacement;
66 using clang::tooling::Replacements;
67
68 using clang::CXXMemberCallExpr;
69 using clang::CallExpr;
70 using clang::Expr;
71 using clang::MemberExpr;
72 using clang::VarDecl;
73
74 namespace {
75
76 std::string ReplaceFirst(const std::string& input,
dcheng 2015/05/06 17:31:18 Consider using llvm::StringRef where appropriate,
Sami 2015/05/07 10:37:37 Neat, thanks for the tip. Done.
77 const char* from,
78 const char* to) {
79 size_t pos = input.find(from);
80 if (pos == std::string::npos)
81 return input;
82 return input.substr(0, pos) + to + input.substr(pos + strlen(from));
83 }
84
85 // Handles conversion of the Post*Task APIs.
86 class PostTaskCallback : public MatchFinder::MatchCallback {
87 public:
88 PostTaskCallback(Replacements* replacements) : replacements_(replacements) {}
dcheng 2015/05/06 17:31:18 explicit
Sami 2015/05/07 10:37:37 Fixed (here and elsewhere).
89
90 virtual void run(const MatchFinder::MatchResult& result) override;
dcheng 2015/05/06 17:31:17 No virtual.
Sami 2015/05/07 10:37:37 Fixed (here and elsewhere).
91
92 private:
93 Replacements* const replacements_;
94 };
95
96 // Refactors base::MessageLoopProxy::current() callers.
97 class CurrentProxyCallback : public MatchFinder::MatchCallback {
98 public:
99 CurrentProxyCallback(Replacements* replacements)
100 : replacements_(replacements) {}
101
102 virtual void run(const MatchFinder::MatchResult& result) override;
103
104 private:
105 Replacements* const replacements_;
106 };
107
108 // Refactors callers to base::MessageLoop::message_loop_proxy() and
109 // base::Thread::message_loop_proxy().
110 class ProxyGetterCallback : public MatchFinder::MatchCallback {
111 public:
112 ProxyGetterCallback(Replacements* replacements)
113 : replacements_(replacements) {}
114
115 virtual void run(const MatchFinder::MatchResult& result) override;
116
117 private:
118 Replacements* const replacements_;
119 };
120
121 // Rewrites variables of type MessageLoopProxy*.
122 class ProxyVariableCallback : public MatchFinder::MatchCallback {
123 public:
124 ProxyVariableCallback(Replacements* replacements)
125 : replacements_(replacements) {}
126
127 virtual void run(const MatchFinder::MatchResult& result) override;
128
129 private:
130 Replacements* const replacements_;
131 };
132
133 // Rewrites variables of type scoped_refptr<MessageLoopProxy>.
134 class RefPtrProxyVariableCallback : public MatchFinder::MatchCallback {
135 public:
136 RefPtrProxyVariableCallback(Replacements* replacements)
137 : replacements_(replacements) {}
138
139 virtual void run(const MatchFinder::MatchResult& result) override;
140
141 private:
142 Replacements* const replacements_;
143 };
144
145 class MessageLoopRefactorer {
146 public:
147 explicit MessageLoopRefactorer(Replacements* replacements)
148 : post_callback_(replacements),
149 current_proxy_callback_(replacements),
150 proxy_getter_callback_(replacements),
151 proxy_variable_callback_(replacements),
152 refptr_proxy_variable_callback_(replacements) {}
153
154 void SetupMatchers(MatchFinder* match_finder);
155
156 private:
157 PostTaskCallback post_callback_;
158 CurrentProxyCallback current_proxy_callback_;
159 ProxyGetterCallback proxy_getter_callback_;
160 ProxyVariableCallback proxy_variable_callback_;
161 RefPtrProxyVariableCallback refptr_proxy_variable_callback_;
162 };
163
164 void MessageLoopRefactorer::SetupMatchers(MatchFinder* match_finder) {
165 auto is_message_loop = recordDecl(isSameOrDerivedFrom("base::MessageLoop"));
166 auto is_thread = recordDecl(isSameOrDerivedFrom("base::Thread"));
167 auto is_message_loop_proxy =
168 recordDecl(isSameOrDerivedFrom("base::MessageLoopProxy"));
169
170 // Matches calls to the Post*Task APIs.
171 auto post_matcher =
172 memberCallExpr(thisPointerType(is_message_loop),
173 callee(methodDecl(anyOf(
174 hasName("PostTask"), hasName("PostDelayedTask"),
175 hasName("PostNonNestableTask"),
176 hasName("PostNonNestableDelayedTask"))))).bind("call");
177
178 // Matches calls to MessageLoopProxy::current().
179 auto current_proxy_matcher =
180 callExpr(callee(methodDecl(ofClass(is_message_loop_proxy),
181 hasName("current")))).bind("call");
182
183 auto message_loop_proxy_callee =
184 callee(methodDecl(hasName("message_loop_proxy")));
185
186 // Matches calls to MessageLoop::message_loop_proxy().
187 auto loop_proxy_getter_matcher =
188 memberCallExpr(thisPointerType(is_message_loop),
189 message_loop_proxy_callee).bind("call");
190
191 // Matches calls to Thread::message_loop_proxy().
192 auto thread_proxy_getter_matcher =
193 memberCallExpr(thisPointerType(is_thread), message_loop_proxy_callee)
194 .bind("call");
195
196 // Matches variables pointing to a MessageLoopProxy that aren't inside
197 // template instantiations (e.g., scoped_retpr<>).
198 auto proxy_variable_matcher =
199 varDecl(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))),
200 unless(hasAncestor(decl(anyOf(
201 recordDecl(isTemplateInstantiation()),
202 functionDecl(isTemplateInstantiation())))))).bind("var");
203
204 // Matches scoped_refptr<MessageLoopProxy>.
205 auto refptr_proxy_variable_matcher =
206 varDecl(hasType(recordDecl(hasName("::scoped_refptr"))),
207 hasDescendant(templateSpecializationType(hasAnyTemplateArgument(
208 refersToType(asString("class base::MessageLoopProxy"))))))
209 .bind("var");
210
211 match_finder->addMatcher(current_proxy_matcher, &current_proxy_callback_);
212 match_finder->addMatcher(loop_proxy_getter_matcher, &proxy_getter_callback_);
213 match_finder->addMatcher(thread_proxy_getter_matcher,
214 &proxy_getter_callback_);
215 match_finder->addMatcher(post_matcher, &post_callback_);
216 match_finder->addMatcher(proxy_variable_matcher, &proxy_variable_callback_);
217 match_finder->addMatcher(refptr_proxy_variable_matcher,
218 &refptr_proxy_variable_callback_);
219 }
220
221 void PostTaskCallback::run(const MatchFinder::MatchResult& result) {
222 const CXXMemberCallExpr* call =
223 result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
224 const Expr* obj = call->getImplicitObjectArgument();
225 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee());
226
227 clang::CharSourceRange range =
228 clang::CharSourceRange::getTokenRange(callee->getSourceRange());
229 clang::CharSourceRange obj_range =
230 clang::CharSourceRange::getTokenRange(obj->getSourceRange());
231
232 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager,
233 result.Context->getLangOpts());
234 std::string obj_text = clang::Lexer::getSourceText(
235 obj_range, *result.SourceManager, result.Context->getLangOpts());
236 std::string method = call->getMethodDecl()->getNameInfo().getAsString();
237
238 // Rewrite the call to go through the task runner.
239 bool is_arrow = callee->isArrow();
240 std::string replacement =
241 obj_text + (is_arrow ? "->" : ".") + "task_runner()->" + method;
dcheng 2015/05/06 17:31:17 Isn't this always -> in Chrome?
Sami 2015/05/07 10:37:37 That's almost always the case, but some tests do h
242
243 // Rewrite MessageLoop::current() as ThreadTaskRunnerHandle::Get().
dcheng 2015/05/06 17:31:18 Why is this chunk here? I would have expected to s
Sami 2015/05/07 10:37:37 CurrentProxyCallback matches calls to MessageLoopP
244 if (obj_text.find("MessageLoop::current") != obj_text.npos ||
245 obj_text.find("MessageLoopForIO::current") != obj_text.npos ||
246 obj_text.find("MessageLoopForUI::current") != obj_text.npos) {
247 std::string ns = "";
248 if (obj_text.find("base::") == 0) {
249 ns = "base::";
250 }
251 replacement = ns + "ThreadTaskRunnerHandle::Get()->" + method;
252 }
253
254 replacements_->insert(Replacement(*result.SourceManager, range, replacement));
255 }
256
257 void CurrentProxyCallback::run(const MatchFinder::MatchResult& result) {
258 const CallExpr* call = result.Nodes.getNodeAs<CallExpr>("call");
259 clang::CharSourceRange range =
260 clang::CharSourceRange::getTokenRange(call->getSourceRange());
261 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager,
262 result.Context->getLangOpts());
263
264 std::string ns = "";
265 if (text.find("base::") == 0) {
dcheng 2015/05/06 17:31:18 Hmm... I guess I should finish my patch to try to
Sami 2015/05/07 10:37:37 Got a link? Sounds useful :) Although for this pa
266 ns = "base::";
267 }
268 std::string replacement = ns + "ThreadTaskRunnerHandle::Get()";
269 replacements_->insert(Replacement(*result.SourceManager, range, replacement));
270 }
271
272 void ProxyGetterCallback::run(const MatchFinder::MatchResult& result) {
273 const CXXMemberCallExpr* call =
274 result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
275 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee());
276 clang::CharSourceRange range =
277 clang::CharSourceRange::getTokenRange(callee->getSourceRange());
278
279 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager,
280 result.Context->getLangOpts());
281
282 text = ReplaceFirst(text, "message_loop_proxy", "task_runner");
283 replacements_->insert(Replacement(*result.SourceManager, range, text));
284 }
285
286 void ProxyVariableCallback::run(const MatchFinder::MatchResult& result) {
287 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var");
288 clang::CharSourceRange range =
289 clang::CharSourceRange::getTokenRange(var->getSourceRange());
290
291 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager,
292 result.Context->getLangOpts());
293
294 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner");
295 replacements_->insert(Replacement(*result.SourceManager, range, text));
296 }
297
298 void RefPtrProxyVariableCallback::run(const MatchFinder::MatchResult& result) {
299 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var");
300 clang::CharSourceRange range =
301 clang::CharSourceRange::getTokenRange(var->getSourceRange());
302
303 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager,
304 result.Context->getLangOpts());
305
306 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner");
307 replacements_->insert(Replacement(*result.SourceManager, range, text));
308 }
309
310 } // namespace
311
312 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
313
314 int main(int argc, const char* argv[]) {
315 llvm::cl::OptionCategory category("MessageLoop refactoring tool");
316 CommonOptionsParser options(argc, argv, category);
317 clang::tooling::ClangTool tool(options.getCompilations(),
318 options.getSourcePathList());
319
320 Replacements replacements;
321 MessageLoopRefactorer refactorer(&replacements);
322 MatchFinder match_finder;
323 refactorer.SetupMatchers(&match_finder);
324
325 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory =
326 clang::tooling::newFrontendActionFactory(&match_finder);
327 int result = tool.run(frontend_factory.get());
328 if (result != 0)
329 return result;
330
331 // Each replacement line should have the following format:
332 // r:<file path>:<offset>:<length>:<replacement text>
333 // Only the <replacement text> field can contain embedded ":" characters.
334 // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML
335 // serialization and then use clang-apply-replacements, but that would require
336 // copying and pasting a larger amount of boilerplate for all Chrome clang
337 // tools.
338 llvm::outs() << "==== BEGIN EDITS ====\n";
339 for (const auto& r : replacements) {
340 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
341 << ":::" << r.getLength() << ":::" << r.getReplacementText()
342 << "\n";
343 }
344 llvm::outs() << "==== END EDITS ====\n";
345
346 return 0;
347 }
OLDNEW
« no previous file with comments | « tools/clang/refactor_message_loop/CMakeLists.txt ('k') | tools/clang/refactor_message_loop/tests/test-expected.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698