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(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 |