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(const std::string& input, | |
99 const char* from, | |
100 const char* 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 + strlen(from)); | |
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 PostTaskCallback(Replacements* replacements) : replacements_(replacements) {} | |
120 | |
121 virtual void run(const MatchFinder::MatchResult& result) override; | |
122 | |
123 private: | |
124 Replacements* const replacements_; | |
125 }; | |
126 | |
127 // Refactors base::MessageLoopProxy::current() callers. | |
128 class CurrentProxyCallback : public MatchFinder::MatchCallback { | |
129 public: | |
130 CurrentProxyCallback(Replacements* replacements) | |
131 : replacements_(replacements) {} | |
132 | |
133 virtual void run(const MatchFinder::MatchResult& result) override; | |
134 | |
135 private: | |
136 Replacements* const replacements_; | |
137 }; | |
138 | |
139 // Refactors callers to base::MessageLoop::message_loop_proxy() and | |
140 // base::Thread::message_loop_proxy(). | |
141 class ProxyGetterCallback : public MatchFinder::MatchCallback { | |
142 public: | |
143 ProxyGetterCallback(Replacements* replacements) | |
144 : replacements_(replacements) {} | |
145 | |
146 virtual void run(const MatchFinder::MatchResult& result) override; | |
147 | |
148 private: | |
149 Replacements* const replacements_; | |
150 }; | |
151 | |
152 // Rewrites variables of type MessageLoopProxy*. | |
153 class ProxyVariableCallback : public MatchFinder::MatchCallback { | |
154 public: | |
155 ProxyVariableCallback(Replacements* replacements) | |
156 : replacements_(replacements) {} | |
157 | |
158 virtual void run(const MatchFinder::MatchResult& result) override; | |
159 | |
160 private: | |
161 Replacements* const replacements_; | |
162 }; | |
163 | |
164 // Rewrites variables of type scoped_refptr<MessageLoopProxy>. | |
165 class RefPtrProxyVariableCallback : public MatchFinder::MatchCallback { | |
166 public: | |
167 RefPtrProxyVariableCallback(Replacements* replacements) | |
168 : replacements_(replacements) {} | |
169 | |
170 virtual void run(const MatchFinder::MatchResult& result) override; | |
171 | |
172 private: | |
173 Replacements* const replacements_; | |
174 }; | |
175 | |
176 // Rewrites expressions of type MessageLoopProxy*. | |
177 class ProxyExprCallback : public MatchFinder::MatchCallback { | |
178 public: | |
179 ProxyExprCallback(Replacements* replacements) : replacements_(replacements) {} | |
180 | |
181 virtual void run(const MatchFinder::MatchResult& result) override; | |
182 | |
183 private: | |
184 Replacements* const replacements_; | |
185 }; | |
186 | |
187 // Rewrites generic expressions involving MessageLoopProxy* or | |
188 // scoped_refptr<MessageLoopProxy>. | |
189 class GenericProxyExprCallback : public MatchFinder::MatchCallback { | |
190 public: | |
191 GenericProxyExprCallback(Replacements* replacements) | |
192 : replacements_(replacements) {} | |
193 | |
194 virtual void run(const MatchFinder::MatchResult& result) override; | |
195 | |
196 private: | |
197 Replacements* const replacements_; | |
198 }; | |
199 | |
200 // Rewrites functions that return scoped_refptr<base::MessageLoopProxy>. | |
201 class CustomProxyGetterCallback : public MatchFinder::MatchCallback { | |
202 public: | |
203 CustomProxyGetterCallback(Replacements* replacements) | |
204 : replacements_(replacements) {} | |
205 | |
206 virtual void run(const MatchFinder::MatchResult& result) override; | |
207 | |
208 private: | |
209 Replacements* const replacements_; | |
210 }; | |
211 | |
212 class MessageLoopRefactorer { | |
213 public: | |
214 explicit MessageLoopRefactorer(Replacements* replacements) | |
215 : post_callback_(replacements), | |
216 current_proxy_callback_(replacements), | |
217 proxy_getter_callback_(replacements), | |
218 proxy_variable_callback_(replacements), | |
219 proxy_expr_callback_(replacements), | |
220 generic_proxy_expr_callback_(replacements), | |
221 refptr_proxy_variable_callback_(replacements), | |
222 custom_proxy_getter_callback_(replacements) {} | |
223 | |
224 void SetupMatchers(MatchFinder* match_finder); | |
225 | |
226 private: | |
227 PostTaskCallback post_callback_; | |
228 CurrentProxyCallback current_proxy_callback_; | |
229 ProxyGetterCallback proxy_getter_callback_; | |
230 ProxyVariableCallback proxy_variable_callback_; | |
231 ProxyExprCallback proxy_expr_callback_; | |
232 GenericProxyExprCallback generic_proxy_expr_callback_; | |
233 RefPtrProxyVariableCallback refptr_proxy_variable_callback_; | |
234 CustomProxyGetterCallback custom_proxy_getter_callback_; | |
235 }; | |
236 | |
237 void MessageLoopRefactorer::SetupMatchers(MatchFinder* match_finder) { | |
238 auto is_message_loop = recordDecl(isSameOrDerivedFrom("base::MessageLoop")); | |
239 auto is_thread = recordDecl(isSameOrDerivedFrom("base::Thread")); | |
240 auto is_message_loop_proxy = | |
241 recordDecl(isSameOrDerivedFrom("base::MessageLoopProxy")); | |
242 | |
243 // Matches calls to the Post*Task APIs. | |
244 auto post_matcher = | |
245 memberCallExpr(thisPointerType(is_message_loop), | |
246 callee(methodDecl(anyOf( | |
247 hasName("PostTask"), hasName("PostDelayedTask"), | |
248 hasName("PostNonNestableTask"), | |
249 hasName("PostNonNestableDelayedTask"))))).bind("call"); | |
250 | |
251 // Matches calls to MessageLoopProxy::current(). | |
252 auto current_proxy_matcher = | |
253 callExpr(callee(methodDecl(ofClass(is_message_loop_proxy), | |
254 hasName("current")))).bind("call"); | |
255 | |
256 auto message_loop_proxy_callee = | |
257 callee(methodDecl(hasName("message_loop_proxy"))); | |
258 | |
259 // Matches calls to MessageLoop::message_loop_proxy(). | |
260 auto loop_proxy_getter_matcher = | |
261 memberCallExpr(thisPointerType(is_message_loop), | |
262 message_loop_proxy_callee).bind("call"); | |
263 | |
264 // Matches calls to Thread::message_loop_proxy(). | |
265 auto thread_proxy_getter_matcher = | |
266 memberCallExpr(thisPointerType(is_thread), message_loop_proxy_callee) | |
267 .bind("call"); | |
268 | |
269 // Matches variables and members pointing to a MessageLoopProxy that aren't | |
270 // inside template instantiations (e.g., scoped_retpr<>). | |
271 auto proxy_variable_matcher = | |
272 varDecl(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
273 unless(hasAncestor(decl(anyOf( | |
274 recordDecl(isTemplateInstantiation()), | |
275 functionDecl(isTemplateInstantiation())))))).bind("var"); | |
276 auto proxy_field_matcher = | |
277 fieldDecl( | |
278 hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
279 unless(hasAncestor(decl( | |
280 anyOf(recordDecl(isTemplateInstantiation()), | |
281 functionDecl(isTemplateInstantiation())))))).bind("field"); | |
282 | |
283 // Matches any expressions that evaluate to base::MessageLoopProxy* but | |
284 // aren't result from a member or function call (e.g., | |
285 // MessageLoopProxy::current()). | |
286 auto proxy_expr_matcher = | |
287 expr(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
288 unless(anyOf(hasDescendant(callExpr()), | |
289 hasDescendant(memberCallExpr())))).bind("expr"); | |
290 | |
291 // Matches methods which return or MessageLoopProxy* scoped_refptr<*>. | |
292 auto proxy_method_matcher = | |
293 methodDecl( | |
294 returns(pointsTo(recordDecl(hasName("base::MessageLoopProxy"))))) | |
295 .bind("method"); | |
296 auto refptr_proxy_method_matcher = | |
297 methodDecl(returns(asString("scoped_refptr<base::MessageLoopProxy>"))) | |
298 .bind("method"); | |
299 | |
300 // Matches scoped_refptr<*> variable and member declarations. Note that | |
301 // further matching is done in the replacement to only target | |
302 // scoped_refptr<base::MessageLoopProxy>. | |
303 auto refptr_proxy_field_matcher = | |
304 fieldDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("field"); | |
305 auto refptr_proxy_variable_matcher = | |
306 varDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("var"); | |
307 auto refptr_proxy_ref_field_matcher = | |
308 fieldDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) | |
309 .bind("field"); | |
310 auto refptr_proxy_ref_variable_matcher = | |
311 varDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) | |
312 .bind("var"); | |
313 | |
314 // The following three matchers are used to rename variables. They look for | |
315 // expressions, constructor initializers and declaration references (i.e., | |
316 // constructor initializer parameters) of the type base::MessageLoopProxy* or | |
317 // any scoped_refptr<>. The actual matching to | |
318 // scoped_refptr<base::MessageLoopProxy> happens in the callback since I | |
319 // couldn't come up with a reliable AST matcher for doing that here. | |
320 | |
321 // Matches scoped_refptr<> which is being passed as an argument to a | |
322 // function. | |
323 auto proxy_refptr_expr_matcher = | |
324 expr(hasType(recordDecl(hasName("::scoped_refptr"))), | |
325 anyOf(hasAncestor(callExpr()), hasAncestor(memberCallExpr()))) | |
326 .bind("expr"); | |
327 | |
328 auto proxy_refptr_member_expr_matcher = | |
329 memberExpr(hasType(recordDecl(hasName("::scoped_refptr")))) | |
330 .bind("member"); | |
331 | |
332 // Matches any constructor initializer. | |
333 auto ctor_proxy_expr_matcher = constructorDecl( | |
334 forEachConstructorInitializer(ctorInitializer().bind("ctor"))); | |
335 | |
336 // Matches any declaration reference to a MessageLoopProxy* or | |
337 // scoped_refptr<>. | |
338 auto proxy_decl_ref_expr_matcher = | |
339 declRefExpr(anyOf(hasType(recordDecl(hasName("::scoped_refptr"))), | |
340 hasType(pointsTo(recordDecl( | |
341 hasName("base::MessageLoopProxy")))))).bind("ref"); | |
342 | |
343 match_finder->addMatcher(current_proxy_matcher, ¤t_proxy_callback_); | |
344 match_finder->addMatcher(loop_proxy_getter_matcher, &proxy_getter_callback_); | |
345 match_finder->addMatcher(thread_proxy_getter_matcher, | |
346 &proxy_getter_callback_); | |
347 match_finder->addMatcher(post_matcher, &post_callback_); | |
348 match_finder->addMatcher(proxy_variable_matcher, &proxy_variable_callback_); | |
349 match_finder->addMatcher(proxy_field_matcher, &proxy_variable_callback_); | |
350 match_finder->addMatcher(refptr_proxy_field_matcher, | |
351 &refptr_proxy_variable_callback_); | |
352 match_finder->addMatcher(refptr_proxy_variable_matcher, | |
353 &refptr_proxy_variable_callback_); | |
354 match_finder->addMatcher(refptr_proxy_ref_field_matcher, | |
355 &refptr_proxy_variable_callback_); | |
356 match_finder->addMatcher(refptr_proxy_ref_variable_matcher, | |
357 &refptr_proxy_variable_callback_); | |
358 match_finder->addMatcher(proxy_expr_matcher, &proxy_expr_callback_); | |
359 | |
360 match_finder->addMatcher(proxy_refptr_expr_matcher, | |
361 &generic_proxy_expr_callback_); | |
362 match_finder->addMatcher(ctor_proxy_expr_matcher, | |
363 &generic_proxy_expr_callback_); | |
364 match_finder->addMatcher(proxy_decl_ref_expr_matcher, | |
365 &generic_proxy_expr_callback_); | |
366 match_finder->addMatcher(proxy_refptr_member_expr_matcher, | |
367 &generic_proxy_expr_callback_); | |
368 | |
369 match_finder->addMatcher(proxy_method_matcher, | |
370 &custom_proxy_getter_callback_); | |
371 match_finder->addMatcher(refptr_proxy_method_matcher, | |
372 &custom_proxy_getter_callback_); | |
373 } | |
374 | |
375 void PostTaskCallback::run(const MatchFinder::MatchResult& result) { | |
376 const CXXMemberCallExpr* call = | |
377 result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); | |
378 const Expr* obj = call->getImplicitObjectArgument(); | |
379 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); | |
380 | |
381 clang::CharSourceRange range = | |
382 clang::CharSourceRange::getTokenRange(callee->getSourceRange()); | |
383 clang::CharSourceRange obj_range = | |
384 clang::CharSourceRange::getTokenRange(obj->getSourceRange()); | |
385 | |
386 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
387 result.Context->getLangOpts()); | |
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 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
416 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()"; | |
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 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
484 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 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
524 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 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
547 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()) | |
dcheng
2015/05/06 17:31:18
Are you actually generating overlapping edits, or
Sami
2015/05/07 10:37:37
They are identical in the sense that the end resul
dcheng
2015/05/26 20:35:44
I'm really surprised that you need to do this. The
| |
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 |