OLD | NEW |
---|---|
(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*() | |
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 // Additionally, the tool renames variables of type MessageLoopProxy* or | |
27 // scoped_refptr<MessageLoopProxy> as follows: | |
28 // - *message_loop_proxy* => *task_runner* | |
29 // - *message_loop* => *task_runner* | |
30 // - *loop_proxy* => *task_runner* | |
31 // - *proxy* => *task_runner* | |
32 // - *loop* => *task_runner* | |
33 // | |
34 | |
35 #include <memory> | |
36 #include "clang/AST/ExprCXX.h" | |
37 #include "clang/ASTMatchers/ASTMatchers.h" | |
38 #include "clang/ASTMatchers/ASTMatchFinder.h" | |
39 #include "clang/Basic/SourceManager.h" | |
40 #include "clang/Frontend/FrontendActions.h" | |
41 #include "clang/Lex/Lexer.h" | |
42 #include "clang/Tooling/CommonOptionsParser.h" | |
43 #include "clang/Tooling/Refactoring.h" | |
44 #include "clang/Tooling/Tooling.h" | |
45 #include "llvm/Support/CommandLine.h" | |
46 | |
47 using clang::ast_matchers::MatchFinder; | |
48 using clang::ast_matchers::anyOf; | |
49 using clang::ast_matchers::asString; | |
50 using clang::ast_matchers::callExpr; | |
51 using clang::ast_matchers::callee; | |
52 using clang::ast_matchers::constructExpr; | |
53 using clang::ast_matchers::ctorInitializer; | |
54 using clang::ast_matchers::decl; | |
55 using clang::ast_matchers::declRefExpr; | |
56 using clang::ast_matchers::expr; | |
57 using clang::ast_matchers::fieldDecl; | |
58 using clang::ast_matchers::functionDecl; | |
59 using clang::ast_matchers::hasAncestor; | |
60 using clang::ast_matchers::hasAnyTemplateArgument; | |
61 using clang::ast_matchers::hasDescendant; | |
62 using clang::ast_matchers::hasName; | |
63 using clang::ast_matchers::hasType; | |
64 using clang::ast_matchers::isSameOrDerivedFrom; | |
65 using clang::ast_matchers::isTemplateInstantiation; | |
66 using clang::ast_matchers::memberCallExpr; | |
67 using clang::ast_matchers::memberExpr; | |
68 using clang::ast_matchers::methodDecl; | |
69 using clang::ast_matchers::ofClass; | |
70 using clang::ast_matchers::pointsTo; | |
71 using clang::ast_matchers::recordDecl; | |
72 using clang::ast_matchers::references; | |
73 using clang::ast_matchers::refersToType; | |
74 using clang::ast_matchers::returns; | |
75 using clang::ast_matchers::templateSpecializationType; | |
76 using clang::ast_matchers::thisPointerType; | |
77 using clang::ast_matchers::unless; | |
78 using clang::ast_matchers::varDecl; | |
79 using clang::ast_matchers::constructorDecl; | |
80 using clang::ast_matchers::forEachConstructorInitializer; | |
81 | |
82 using clang::tooling::CommonOptionsParser; | |
83 using clang::tooling::Replacement; | |
84 using clang::tooling::Replacements; | |
85 | |
86 using clang::CXXMethodDecl; | |
87 using clang::CXXMemberCallExpr; | |
88 using clang::CXXCtorInitializer; | |
89 using clang::CallExpr; | |
90 using clang::Expr; | |
91 using clang::DeclRefExpr; | |
92 using clang::FieldDecl; | |
93 using clang::MemberExpr; | |
94 using clang::VarDecl; | |
95 | |
96 namespace { | |
97 | |
98 std::string ReplaceFirst(llvm::StringRef input, | |
99 llvm::StringRef from, | |
100 llvm::StringRef to) { | |
101 size_t pos = input.find(from); | |
102 if (pos == std::string::npos) | |
103 return input; | |
104 return (input.substr(0, pos) + to + input.substr(pos + from.size())).str(); | |
105 } | |
106 | |
107 std::string RenameMessageLoopProxyVariable(std::string name) { | |
108 name = ReplaceFirst(name, "message_loop_proxy", "task_runner"); | |
109 name = ReplaceFirst(name, "message_loop", "task_runner"); | |
110 name = ReplaceFirst(name, "loop_proxy", "task_runner"); | |
111 name = ReplaceFirst(name, "proxy", "task_runner"); | |
112 name = ReplaceFirst(name, "loop", "task_runner"); | |
113 return name; | |
114 } | |
115 | |
116 // Handles conversion of the Post*Task APIs. | |
117 class PostTaskCallback : public MatchFinder::MatchCallback { | |
118 public: | |
119 explicit PostTaskCallback(Replacements* replacements) | |
120 : replacements_(replacements) {} | |
121 | |
122 void run(const MatchFinder::MatchResult& result) override; | |
123 | |
124 private: | |
125 Replacements* const replacements_; | |
126 }; | |
127 | |
128 // Refactors base::MessageLoopProxy::current() callers. | |
129 class CurrentProxyCallback : public MatchFinder::MatchCallback { | |
130 public: | |
131 explicit CurrentProxyCallback(Replacements* replacements) | |
132 : replacements_(replacements) {} | |
133 | |
134 void run(const MatchFinder::MatchResult& result) override; | |
135 | |
136 private: | |
137 Replacements* const replacements_; | |
138 }; | |
139 | |
140 // Refactors callers to base::MessageLoop::message_loop_proxy() and | |
141 // base::Thread::message_loop_proxy(). | |
142 class ProxyGetterCallback : public MatchFinder::MatchCallback { | |
143 public: | |
144 explicit ProxyGetterCallback(Replacements* replacements) | |
145 : replacements_(replacements) {} | |
146 | |
147 void run(const MatchFinder::MatchResult& result) override; | |
148 | |
149 private: | |
150 Replacements* const replacements_; | |
151 }; | |
152 | |
153 // Rewrites variables of type MessageLoopProxy*. | |
154 class ProxyVariableCallback : public MatchFinder::MatchCallback { | |
155 public: | |
156 explicit ProxyVariableCallback(Replacements* replacements) | |
157 : replacements_(replacements) {} | |
158 | |
159 void run(const MatchFinder::MatchResult& result) override; | |
160 | |
161 private: | |
162 Replacements* const replacements_; | |
163 }; | |
164 | |
165 // Rewrites variables of type scoped_refptr<MessageLoopProxy>. | |
166 class RefPtrProxyVariableCallback : public MatchFinder::MatchCallback { | |
167 public: | |
168 explicit RefPtrProxyVariableCallback(Replacements* replacements) | |
169 : replacements_(replacements) {} | |
170 | |
171 void run(const MatchFinder::MatchResult& result) override; | |
172 | |
173 private: | |
174 Replacements* const replacements_; | |
175 }; | |
176 | |
177 // Rewrites expressions of type MessageLoopProxy*. | |
178 class ProxyExprCallback : public MatchFinder::MatchCallback { | |
179 public: | |
180 explicit ProxyExprCallback(Replacements* replacements) | |
181 : replacements_(replacements) {} | |
182 | |
183 void run(const MatchFinder::MatchResult& result) override; | |
184 | |
185 private: | |
186 Replacements* const replacements_; | |
187 }; | |
188 | |
189 // Rewrites generic expressions involving MessageLoopProxy* or | |
190 // scoped_refptr<MessageLoopProxy>. | |
191 class GenericProxyExprCallback : public MatchFinder::MatchCallback { | |
192 public: | |
193 explicit GenericProxyExprCallback(Replacements* replacements) | |
194 : replacements_(replacements) {} | |
195 | |
196 void run(const MatchFinder::MatchResult& result) override; | |
197 | |
198 private: | |
199 Replacements* const replacements_; | |
200 }; | |
201 | |
202 // Rewrites functions that return scoped_refptr<base::MessageLoopProxy>. | |
203 class CustomProxyGetterCallback : public MatchFinder::MatchCallback { | |
204 public: | |
205 explicit CustomProxyGetterCallback(Replacements* replacements) | |
206 : replacements_(replacements) {} | |
207 | |
208 void run(const MatchFinder::MatchResult& result) override; | |
209 | |
210 private: | |
211 Replacements* const replacements_; | |
212 }; | |
213 | |
214 class MessageLoopRefactorer { | |
215 public: | |
216 explicit MessageLoopRefactorer(Replacements* replacements) | |
217 : post_callback_(replacements), | |
218 current_proxy_callback_(replacements), | |
219 proxy_getter_callback_(replacements), | |
220 proxy_variable_callback_(replacements), | |
221 proxy_expr_callback_(replacements), | |
222 generic_proxy_expr_callback_(replacements), | |
223 refptr_proxy_variable_callback_(replacements), | |
224 custom_proxy_getter_callback_(replacements) {} | |
225 | |
226 void SetupMatchers(MatchFinder* match_finder); | |
227 | |
228 private: | |
229 PostTaskCallback post_callback_; | |
230 CurrentProxyCallback current_proxy_callback_; | |
231 ProxyGetterCallback proxy_getter_callback_; | |
232 ProxyVariableCallback proxy_variable_callback_; | |
233 ProxyExprCallback proxy_expr_callback_; | |
234 GenericProxyExprCallback generic_proxy_expr_callback_; | |
235 RefPtrProxyVariableCallback refptr_proxy_variable_callback_; | |
236 CustomProxyGetterCallback custom_proxy_getter_callback_; | |
237 }; | |
238 | |
239 void MessageLoopRefactorer::SetupMatchers(MatchFinder* match_finder) { | |
240 auto is_message_loop = recordDecl(isSameOrDerivedFrom("base::MessageLoop")); | |
241 auto is_thread = recordDecl(isSameOrDerivedFrom("base::Thread")); | |
242 auto is_message_loop_proxy = | |
243 recordDecl(isSameOrDerivedFrom("base::MessageLoopProxy")); | |
244 | |
245 // Matches calls to the Post*Task APIs. | |
246 auto post_matcher = | |
247 memberCallExpr(thisPointerType(is_message_loop), | |
248 callee(methodDecl(anyOf( | |
249 hasName("PostTask"), hasName("PostDelayedTask"), | |
250 hasName("PostNonNestableTask"), | |
251 hasName("PostNonNestableDelayedTask"))))).bind("call"); | |
252 | |
253 // Matches calls to MessageLoopProxy::current(). | |
254 auto current_proxy_matcher = | |
255 callExpr(callee(methodDecl(ofClass(is_message_loop_proxy), | |
256 hasName("current")))).bind("call"); | |
257 | |
258 auto message_loop_proxy_callee = | |
259 callee(methodDecl(hasName("message_loop_proxy"))); | |
260 | |
261 // Matches calls to MessageLoop::message_loop_proxy(). | |
262 auto loop_proxy_getter_matcher = | |
263 memberCallExpr(thisPointerType(is_message_loop), | |
264 message_loop_proxy_callee).bind("call"); | |
265 | |
266 // Matches calls to Thread::message_loop_proxy(). | |
267 auto thread_proxy_getter_matcher = | |
268 memberCallExpr(thisPointerType(is_thread), message_loop_proxy_callee) | |
269 .bind("call"); | |
270 | |
271 // Matches variables and members pointing to a MessageLoopProxy that aren't | |
272 // inside template instantiations (e.g., scoped_retpr<>). | |
273 auto proxy_variable_matcher = | |
274 varDecl(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
275 unless(hasAncestor(decl(anyOf( | |
276 recordDecl(isTemplateInstantiation()), | |
277 functionDecl(isTemplateInstantiation())))))).bind("var"); | |
278 auto proxy_field_matcher = | |
279 fieldDecl( | |
280 hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
281 unless(hasAncestor(decl( | |
282 anyOf(recordDecl(isTemplateInstantiation()), | |
283 functionDecl(isTemplateInstantiation())))))).bind("field"); | |
284 | |
285 // Matches any expressions that evaluate to base::MessageLoopProxy* but | |
286 // aren't result from a member or function call (e.g., | |
287 // MessageLoopProxy::current()). | |
288 auto proxy_expr_matcher = | |
289 expr(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
290 unless(anyOf(hasDescendant(callExpr()), | |
291 hasDescendant(memberCallExpr())))).bind("expr"); | |
292 | |
293 // Matches methods which return or MessageLoopProxy* scoped_refptr<*>. | |
294 auto proxy_method_matcher = | |
295 methodDecl( | |
296 returns(pointsTo(recordDecl(hasName("base::MessageLoopProxy"))))) | |
297 .bind("method"); | |
298 auto refptr_proxy_method_matcher = | |
299 methodDecl(returns(asString("scoped_refptr<base::MessageLoopProxy>"))) | |
300 .bind("method"); | |
301 | |
302 // Matches scoped_refptr<*> variable and member declarations. Note that | |
303 // further matching is done in the replacement to only target | |
304 // scoped_refptr<base::MessageLoopProxy>. | |
305 auto refptr_proxy_field_matcher = | |
306 fieldDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("field"); | |
307 auto refptr_proxy_variable_matcher = | |
308 varDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("var"); | |
309 auto refptr_proxy_ref_field_matcher = | |
310 fieldDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) | |
311 .bind("field"); | |
312 auto refptr_proxy_ref_variable_matcher = | |
313 varDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) | |
314 .bind("var"); | |
315 | |
316 // The following three matchers are used to rename variables. They look for | |
317 // expressions, constructor initializers and declaration references (i.e., | |
318 // constructor initializer parameters) of the type base::MessageLoopProxy* or | |
319 // any scoped_refptr<>. The actual matching to | |
320 // scoped_refptr<base::MessageLoopProxy> happens in the callback since I | |
321 // couldn't come up with a reliable AST matcher for doing that here. | |
322 | |
323 // Matches scoped_refptr<> which is being passed as an argument to a | |
324 // function. | |
325 auto proxy_refptr_expr_matcher = | |
326 expr(hasType(recordDecl(hasName("::scoped_refptr"))), | |
327 anyOf(hasAncestor(callExpr()), hasAncestor(memberCallExpr()))) | |
328 .bind("expr"); | |
329 | |
330 auto proxy_refptr_member_expr_matcher = | |
331 memberExpr(hasType(recordDecl(hasName("::scoped_refptr")))) | |
332 .bind("member"); | |
333 | |
334 // Matches any constructor initializer. | |
335 auto ctor_proxy_expr_matcher = constructorDecl( | |
336 forEachConstructorInitializer(ctorInitializer().bind("ctor"))); | |
337 | |
338 // Matches any declaration reference to a MessageLoopProxy* or | |
339 // scoped_refptr<>. | |
340 auto proxy_decl_ref_expr_matcher = | |
341 declRefExpr(anyOf(hasType(recordDecl(hasName("::scoped_refptr"))), | |
342 hasType(pointsTo(recordDecl( | |
343 hasName("base::MessageLoopProxy")))))).bind("ref"); | |
344 | |
345 match_finder->addMatcher(current_proxy_matcher, ¤t_proxy_callback_); | |
346 match_finder->addMatcher(loop_proxy_getter_matcher, &proxy_getter_callback_); | |
347 match_finder->addMatcher(thread_proxy_getter_matcher, | |
348 &proxy_getter_callback_); | |
349 match_finder->addMatcher(post_matcher, &post_callback_); | |
350 match_finder->addMatcher(proxy_variable_matcher, &proxy_variable_callback_); | |
351 match_finder->addMatcher(proxy_field_matcher, &proxy_variable_callback_); | |
352 match_finder->addMatcher(refptr_proxy_field_matcher, | |
353 &refptr_proxy_variable_callback_); | |
354 match_finder->addMatcher(refptr_proxy_variable_matcher, | |
355 &refptr_proxy_variable_callback_); | |
356 match_finder->addMatcher(refptr_proxy_ref_field_matcher, | |
357 &refptr_proxy_variable_callback_); | |
358 match_finder->addMatcher(refptr_proxy_ref_variable_matcher, | |
359 &refptr_proxy_variable_callback_); | |
360 match_finder->addMatcher(proxy_expr_matcher, &proxy_expr_callback_); | |
361 | |
362 match_finder->addMatcher(proxy_refptr_expr_matcher, | |
363 &generic_proxy_expr_callback_); | |
364 match_finder->addMatcher(ctor_proxy_expr_matcher, | |
365 &generic_proxy_expr_callback_); | |
366 match_finder->addMatcher(proxy_decl_ref_expr_matcher, | |
367 &generic_proxy_expr_callback_); | |
368 match_finder->addMatcher(proxy_refptr_member_expr_matcher, | |
369 &generic_proxy_expr_callback_); | |
370 | |
371 match_finder->addMatcher(proxy_method_matcher, | |
372 &custom_proxy_getter_callback_); | |
373 match_finder->addMatcher(refptr_proxy_method_matcher, | |
374 &custom_proxy_getter_callback_); | |
375 } | |
376 | |
377 void PostTaskCallback::run(const MatchFinder::MatchResult& result) { | |
378 const CXXMemberCallExpr* call = | |
379 result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); | |
380 const Expr* obj = call->getImplicitObjectArgument(); | |
381 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); | |
382 | |
383 clang::CharSourceRange range = | |
384 clang::CharSourceRange::getTokenRange(callee->getSourceRange()); | |
385 clang::CharSourceRange obj_range = | |
386 clang::CharSourceRange::getTokenRange(obj->getSourceRange()); | |
387 | |
388 std::string obj_text = clang::Lexer::getSourceText( | |
389 obj_range, *result.SourceManager, result.Context->getLangOpts()); | |
390 std::string method = call->getMethodDecl()->getNameInfo().getAsString(); | |
391 | |
392 // Rewrite the call to go through the task runner. | |
393 bool is_arrow = callee->isArrow(); | |
394 std::string replacement = | |
395 obj_text + (is_arrow ? "->" : ".") + "task_runner()->" + method; | |
396 | |
397 // Rewrite MessageLoop::current() as ThreadTaskRunnerHandle::Get(). | |
398 if (obj_text.find("MessageLoop::current") != obj_text.npos || | |
399 obj_text.find("MessageLoopForIO::current") != obj_text.npos || | |
400 obj_text.find("MessageLoopForUI::current") != obj_text.npos) { | |
401 std::string ns = ""; | |
402 if (obj_text.find("base::") == 0) { | |
403 ns = "base::"; | |
404 } | |
405 replacement = ns + "ThreadTaskRunnerHandle::Get()->" + method; | |
406 } | |
407 | |
408 replacements_->insert(Replacement(*result.SourceManager, range, replacement)); | |
409 } | |
410 | |
411 void CurrentProxyCallback::run(const MatchFinder::MatchResult& result) { | |
412 const CallExpr* call = result.Nodes.getNodeAs<CallExpr>("call"); | |
413 clang::CharSourceRange range = | |
414 clang::CharSourceRange::getTokenRange(call->getSourceRange()); | |
415 llvm::StringRef text = clang::Lexer::getSourceText( | |
416 range, *result.SourceManager, result.Context->getLangOpts()); | |
417 | |
418 std::string ns = ""; | |
419 if (text.find("base::") == 0) { | |
420 ns = "base::"; | |
421 } | |
422 std::string replacement = ns + "ThreadTaskRunnerHandle::Get()"; | |
dcheng
2015/05/26 20:35:44
My main objection to landing this patch as-is is t
| |
423 replacements_->insert(Replacement(*result.SourceManager, range, replacement)); | |
424 } | |
425 | |
426 void ProxyGetterCallback::run(const MatchFinder::MatchResult& result) { | |
427 const CXXMemberCallExpr* call = | |
428 result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); | |
429 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); | |
430 clang::CharSourceRange range = | |
431 clang::CharSourceRange::getTokenRange(callee->getSourceRange()); | |
432 | |
433 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
434 result.Context->getLangOpts()); | |
435 | |
436 text = ReplaceFirst(text, "message_loop_proxy", "task_runner"); | |
437 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
438 } | |
439 | |
440 void ProxyVariableCallback::run(const MatchFinder::MatchResult& result) { | |
441 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); | |
442 const FieldDecl* field = result.Nodes.getNodeAs<FieldDecl>("field"); | |
443 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
444 var ? var->getSourceRange() : field->getSourceRange()); | |
445 | |
446 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
447 result.Context->getLangOpts()); | |
448 | |
449 // Change the type of the pointee. | |
450 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); | |
451 | |
452 // Rename the pointer variable too. | |
453 text = RenameMessageLoopProxyVariable(text); | |
454 | |
455 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
456 } | |
457 | |
458 void RefPtrProxyVariableCallback::run(const MatchFinder::MatchResult& result) { | |
459 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); | |
460 const FieldDecl* field = result.Nodes.getNodeAs<FieldDecl>("field"); | |
461 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
462 var ? var->getSourceRange() : field->getSourceRange()); | |
463 | |
464 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
465 result.Context->getLangOpts()); | |
466 if (text.find("MessageLoopProxy") == text.npos) | |
467 return; | |
468 | |
469 // Change the type of the pointee. | |
470 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); | |
471 | |
472 // Rename the refptr variable too. | |
473 text = RenameMessageLoopProxyVariable(text); | |
474 | |
475 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
476 } | |
477 | |
478 void ProxyExprCallback::run(const MatchFinder::MatchResult& result) { | |
479 const Expr* expr = result.Nodes.getNodeAs<Expr>("expr"); | |
480 clang::CharSourceRange range = | |
481 clang::CharSourceRange::getTokenRange(expr->getSourceRange()); | |
482 | |
483 llvm::StringRef text = clang::Lexer::getSourceText( | |
484 range, *result.SourceManager, result.Context->getLangOpts()); | |
485 | |
486 std::string replacement = RenameMessageLoopProxyVariable(text); | |
487 if (replacement != text) { | |
488 replacements_->insert( | |
489 Replacement(*result.SourceManager, range, replacement)); | |
490 } | |
491 } | |
492 | |
493 void GenericProxyExprCallback::run(const MatchFinder::MatchResult& result) { | |
494 const Expr* expr = result.Nodes.getNodeAs<Expr>("expr"); | |
495 const CXXCtorInitializer* ctor = | |
496 result.Nodes.getNodeAs<CXXCtorInitializer>("ctor"); | |
497 const DeclRefExpr* ref = result.Nodes.getNodeAs<DeclRefExpr>("ref"); | |
498 const MemberExpr* member = result.Nodes.getNodeAs<MemberExpr>("member"); | |
499 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
500 expr ? expr->getSourceRange() : ref ? ref->getSourceRange() | |
501 : member ? member->getSourceRange() | |
502 : ctor->getSourceRange()); | |
503 | |
504 std::string type; | |
505 if (expr) { | |
506 type = expr->getType().getAsString(); | |
507 } else if (ref) { | |
508 type = ref->getType().getAsString(); | |
509 } else if (member) { | |
510 type = member->getType().getAsString(); | |
511 } else { | |
512 // Ignore base class constructors. | |
513 if (!ctor->getMember()) | |
514 return; | |
515 type = ctor->getMember()->getType().getAsString(); | |
516 } | |
517 | |
518 // Ignore unrelated scoped_refptr<>s. | |
519 if (type.find("scoped_refptr<base::MessageLoopProxy>") == type.npos && | |
520 type.find("base::MessageLoopProxy *") == type.npos) | |
521 return; | |
522 | |
523 llvm::StringRef text = clang::Lexer::getSourceText( | |
524 range, *result.SourceManager, result.Context->getLangOpts()); | |
525 std::string replacement = RenameMessageLoopProxyVariable(text); | |
526 | |
527 // For field initializers, also try renaming the initializing value. | |
528 if (ctor) { | |
529 replacement = RenameMessageLoopProxyVariable(replacement); | |
530 // Hack: rename any static getter because the renaming of the field will | |
531 // conflict the edit that replaces the getter. | |
532 replacement = ReplaceFirst(replacement, "base::MessageLoopProxy::current()", | |
533 "base::ThreadTaskRunnerHandle::Get()"); | |
534 } | |
535 | |
536 if (replacement != text) { | |
537 replacements_->insert( | |
538 Replacement(*result.SourceManager, range, replacement)); | |
539 } | |
540 } | |
541 | |
542 void CustomProxyGetterCallback::run(const MatchFinder::MatchResult& result) { | |
543 const CXXMethodDecl* method = result.Nodes.getNodeAs<CXXMethodDecl>("method"); | |
544 clang::CharSourceRange range = | |
545 clang::CharSourceRange::getTokenRange(method->getReturnTypeSourceRange()); | |
546 llvm::StringRef text = clang::Lexer::getSourceText( | |
547 range, *result.SourceManager, result.Context->getLangOpts()); | |
548 | |
549 std::string replacement = | |
550 ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); | |
551 if (replacement != text) { | |
552 replacements_->insert( | |
553 Replacement(*result.SourceManager, range, replacement)); | |
554 } | |
555 } | |
556 | |
557 } // namespace | |
558 | |
559 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | |
560 | |
561 int main(int argc, const char* argv[]) { | |
562 llvm::cl::OptionCategory category("MessageLoop refactoring tool"); | |
563 CommonOptionsParser options(argc, argv, category); | |
564 clang::tooling::ClangTool tool(options.getCompilations(), | |
565 options.getSourcePathList()); | |
566 | |
567 Replacements replacements; | |
568 MessageLoopRefactorer refactorer(&replacements); | |
569 MatchFinder match_finder; | |
570 refactorer.SetupMatchers(&match_finder); | |
571 | |
572 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | |
573 clang::tooling::newFrontendActionFactory(&match_finder); | |
574 int result = tool.run(frontend_factory.get()); | |
575 if (result != 0) | |
576 return result; | |
577 | |
578 // Each replacement line should have the following format: | |
579 // r:<file path>:<offset>:<length>:<replacement text> | |
580 // Only the <replacement text> field can contain embedded ":" characters. | |
581 // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML | |
582 // serialization and then use clang-apply-replacements, but that would require | |
583 // copying and pasting a larger amount of boilerplate for all Chrome clang | |
584 // tools. | |
585 llvm::outs() << "==== BEGIN EDITS ====\n"; | |
586 unsigned prev_end = 0; | |
587 for (const auto& r : replacements) { | |
588 // Discard overlapping edits. | |
589 if (prev_end > r.getOffset()) | |
590 continue; | |
591 prev_end = r.getOffset() + r.getLength(); | |
592 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() | |
593 << ":::" << r.getLength() << ":::" << r.getReplacementText() | |
594 << "\n"; | |
595 } | |
596 llvm::outs() << "==== END EDITS ====\n"; | |
597 | |
598 return 0; | |
599 } | |
OLD | NEW |