Chromium Code Reviews| 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 |