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()"; |
| 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 |