Index: tools/clang/refactor_message_loop/RefactorMessageLoop.cpp |
diff --git a/tools/clang/refactor_message_loop/RefactorMessageLoop.cpp b/tools/clang/refactor_message_loop/RefactorMessageLoop.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2c6b19dc5af1b6c370d1071cf39e651516ec56c2 |
--- /dev/null |
+++ b/tools/clang/refactor_message_loop/RefactorMessageLoop.cpp |
@@ -0,0 +1,599 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+// |
+// This tool performs the following transformations on base::MessageLoop and |
+// related APIs: |
+// |
+// MessageLoop: |
+// - base::MessageLoop::PostTask* |
+// => base::MessageLoop::task_runner()->PostTask*() |
+// |
+// Thread: |
+// - base::Thread::message_loop_proxy() |
+// => task_runner() |
+// |
+// MessageLoopProxy: |
+// - base::MessageLoopProxy |
+// => base::SingleThreadTaskRunner |
+// - scoped_refptr<base::MessageLoopProxy> |
+// => scoped_refptr<base::SingleThreadTaskRunner> |
+// - base::MessageLoopProxy::current() |
+// => base::ThreadTaskRunnerHandle::Get() |
+// - base::MessageLoop::message_loop_proxy() |
+// => base::MessageLoop::task_runner() (done) |
+// |
+// Additionally, the tool renames variables of type MessageLoopProxy* or |
+// scoped_refptr<MessageLoopProxy> as follows: |
+// - *message_loop_proxy* => *task_runner* |
+// - *message_loop* => *task_runner* |
+// - *loop_proxy* => *task_runner* |
+// - *proxy* => *task_runner* |
+// - *loop* => *task_runner* |
+// |
+ |
+#include <memory> |
+#include "clang/AST/ExprCXX.h" |
+#include "clang/ASTMatchers/ASTMatchers.h" |
+#include "clang/ASTMatchers/ASTMatchFinder.h" |
+#include "clang/Basic/SourceManager.h" |
+#include "clang/Frontend/FrontendActions.h" |
+#include "clang/Lex/Lexer.h" |
+#include "clang/Tooling/CommonOptionsParser.h" |
+#include "clang/Tooling/Refactoring.h" |
+#include "clang/Tooling/Tooling.h" |
+#include "llvm/Support/CommandLine.h" |
+ |
+using clang::ast_matchers::MatchFinder; |
+using clang::ast_matchers::anyOf; |
+using clang::ast_matchers::asString; |
+using clang::ast_matchers::callExpr; |
+using clang::ast_matchers::callee; |
+using clang::ast_matchers::constructExpr; |
+using clang::ast_matchers::ctorInitializer; |
+using clang::ast_matchers::decl; |
+using clang::ast_matchers::declRefExpr; |
+using clang::ast_matchers::expr; |
+using clang::ast_matchers::fieldDecl; |
+using clang::ast_matchers::functionDecl; |
+using clang::ast_matchers::hasAncestor; |
+using clang::ast_matchers::hasAnyTemplateArgument; |
+using clang::ast_matchers::hasDescendant; |
+using clang::ast_matchers::hasName; |
+using clang::ast_matchers::hasType; |
+using clang::ast_matchers::isSameOrDerivedFrom; |
+using clang::ast_matchers::isTemplateInstantiation; |
+using clang::ast_matchers::memberCallExpr; |
+using clang::ast_matchers::memberExpr; |
+using clang::ast_matchers::methodDecl; |
+using clang::ast_matchers::ofClass; |
+using clang::ast_matchers::pointsTo; |
+using clang::ast_matchers::recordDecl; |
+using clang::ast_matchers::references; |
+using clang::ast_matchers::refersToType; |
+using clang::ast_matchers::returns; |
+using clang::ast_matchers::templateSpecializationType; |
+using clang::ast_matchers::thisPointerType; |
+using clang::ast_matchers::unless; |
+using clang::ast_matchers::varDecl; |
+using clang::ast_matchers::constructorDecl; |
+using clang::ast_matchers::forEachConstructorInitializer; |
+ |
+using clang::tooling::CommonOptionsParser; |
+using clang::tooling::Replacement; |
+using clang::tooling::Replacements; |
+ |
+using clang::CXXMethodDecl; |
+using clang::CXXMemberCallExpr; |
+using clang::CXXCtorInitializer; |
+using clang::CallExpr; |
+using clang::Expr; |
+using clang::DeclRefExpr; |
+using clang::FieldDecl; |
+using clang::MemberExpr; |
+using clang::VarDecl; |
+ |
+namespace { |
+ |
+std::string ReplaceFirst(llvm::StringRef input, |
+ llvm::StringRef from, |
+ llvm::StringRef to) { |
+ size_t pos = input.find(from); |
+ if (pos == std::string::npos) |
+ return input; |
+ return (input.substr(0, pos) + to + input.substr(pos + from.size())).str(); |
+} |
+ |
+std::string RenameMessageLoopProxyVariable(std::string name) { |
+ name = ReplaceFirst(name, "message_loop_proxy", "task_runner"); |
+ name = ReplaceFirst(name, "message_loop", "task_runner"); |
+ name = ReplaceFirst(name, "loop_proxy", "task_runner"); |
+ name = ReplaceFirst(name, "proxy", "task_runner"); |
+ name = ReplaceFirst(name, "loop", "task_runner"); |
+ return name; |
+} |
+ |
+// Handles conversion of the Post*Task APIs. |
+class PostTaskCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit PostTaskCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Refactors base::MessageLoopProxy::current() callers. |
+class CurrentProxyCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit CurrentProxyCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Refactors callers to base::MessageLoop::message_loop_proxy() and |
+// base::Thread::message_loop_proxy(). |
+class ProxyGetterCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit ProxyGetterCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Rewrites variables of type MessageLoopProxy*. |
+class ProxyVariableCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit ProxyVariableCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Rewrites variables of type scoped_refptr<MessageLoopProxy>. |
+class RefPtrProxyVariableCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit RefPtrProxyVariableCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Rewrites expressions of type MessageLoopProxy*. |
+class ProxyExprCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit ProxyExprCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Rewrites generic expressions involving MessageLoopProxy* or |
+// scoped_refptr<MessageLoopProxy>. |
+class GenericProxyExprCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit GenericProxyExprCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+// Rewrites functions that return scoped_refptr<base::MessageLoopProxy>. |
+class CustomProxyGetterCallback : public MatchFinder::MatchCallback { |
+ public: |
+ explicit CustomProxyGetterCallback(Replacements* replacements) |
+ : replacements_(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override; |
+ |
+ private: |
+ Replacements* const replacements_; |
+}; |
+ |
+class MessageLoopRefactorer { |
+ public: |
+ explicit MessageLoopRefactorer(Replacements* replacements) |
+ : post_callback_(replacements), |
+ current_proxy_callback_(replacements), |
+ proxy_getter_callback_(replacements), |
+ proxy_variable_callback_(replacements), |
+ proxy_expr_callback_(replacements), |
+ generic_proxy_expr_callback_(replacements), |
+ refptr_proxy_variable_callback_(replacements), |
+ custom_proxy_getter_callback_(replacements) {} |
+ |
+ void SetupMatchers(MatchFinder* match_finder); |
+ |
+ private: |
+ PostTaskCallback post_callback_; |
+ CurrentProxyCallback current_proxy_callback_; |
+ ProxyGetterCallback proxy_getter_callback_; |
+ ProxyVariableCallback proxy_variable_callback_; |
+ ProxyExprCallback proxy_expr_callback_; |
+ GenericProxyExprCallback generic_proxy_expr_callback_; |
+ RefPtrProxyVariableCallback refptr_proxy_variable_callback_; |
+ CustomProxyGetterCallback custom_proxy_getter_callback_; |
+}; |
+ |
+void MessageLoopRefactorer::SetupMatchers(MatchFinder* match_finder) { |
+ auto is_message_loop = recordDecl(isSameOrDerivedFrom("base::MessageLoop")); |
+ auto is_thread = recordDecl(isSameOrDerivedFrom("base::Thread")); |
+ auto is_message_loop_proxy = |
+ recordDecl(isSameOrDerivedFrom("base::MessageLoopProxy")); |
+ |
+ // Matches calls to the Post*Task APIs. |
+ auto post_matcher = |
+ memberCallExpr(thisPointerType(is_message_loop), |
+ callee(methodDecl(anyOf( |
+ hasName("PostTask"), hasName("PostDelayedTask"), |
+ hasName("PostNonNestableTask"), |
+ hasName("PostNonNestableDelayedTask"))))).bind("call"); |
+ |
+ // Matches calls to MessageLoopProxy::current(). |
+ auto current_proxy_matcher = |
+ callExpr(callee(methodDecl(ofClass(is_message_loop_proxy), |
+ hasName("current")))).bind("call"); |
+ |
+ auto message_loop_proxy_callee = |
+ callee(methodDecl(hasName("message_loop_proxy"))); |
+ |
+ // Matches calls to MessageLoop::message_loop_proxy(). |
+ auto loop_proxy_getter_matcher = |
+ memberCallExpr(thisPointerType(is_message_loop), |
+ message_loop_proxy_callee).bind("call"); |
+ |
+ // Matches calls to Thread::message_loop_proxy(). |
+ auto thread_proxy_getter_matcher = |
+ memberCallExpr(thisPointerType(is_thread), message_loop_proxy_callee) |
+ .bind("call"); |
+ |
+ // Matches variables and members pointing to a MessageLoopProxy that aren't |
+ // inside template instantiations (e.g., scoped_retpr<>). |
+ auto proxy_variable_matcher = |
+ varDecl(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), |
+ unless(hasAncestor(decl(anyOf( |
+ recordDecl(isTemplateInstantiation()), |
+ functionDecl(isTemplateInstantiation())))))).bind("var"); |
+ auto proxy_field_matcher = |
+ fieldDecl( |
+ hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), |
+ unless(hasAncestor(decl( |
+ anyOf(recordDecl(isTemplateInstantiation()), |
+ functionDecl(isTemplateInstantiation())))))).bind("field"); |
+ |
+ // Matches any expressions that evaluate to base::MessageLoopProxy* but |
+ // aren't result from a member or function call (e.g., |
+ // MessageLoopProxy::current()). |
+ auto proxy_expr_matcher = |
+ expr(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), |
+ unless(anyOf(hasDescendant(callExpr()), |
+ hasDescendant(memberCallExpr())))).bind("expr"); |
+ |
+ // Matches methods which return or MessageLoopProxy* scoped_refptr<*>. |
+ auto proxy_method_matcher = |
+ methodDecl( |
+ returns(pointsTo(recordDecl(hasName("base::MessageLoopProxy"))))) |
+ .bind("method"); |
+ auto refptr_proxy_method_matcher = |
+ methodDecl(returns(asString("scoped_refptr<base::MessageLoopProxy>"))) |
+ .bind("method"); |
+ |
+ // Matches scoped_refptr<*> variable and member declarations. Note that |
+ // further matching is done in the replacement to only target |
+ // scoped_refptr<base::MessageLoopProxy>. |
+ auto refptr_proxy_field_matcher = |
+ fieldDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("field"); |
+ auto refptr_proxy_variable_matcher = |
+ varDecl(hasType(recordDecl(hasName("::scoped_refptr")))).bind("var"); |
+ auto refptr_proxy_ref_field_matcher = |
+ fieldDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) |
+ .bind("field"); |
+ auto refptr_proxy_ref_variable_matcher = |
+ varDecl(hasType(references(recordDecl(hasName("::scoped_refptr"))))) |
+ .bind("var"); |
+ |
+ // The following three matchers are used to rename variables. They look for |
+ // expressions, constructor initializers and declaration references (i.e., |
+ // constructor initializer parameters) of the type base::MessageLoopProxy* or |
+ // any scoped_refptr<>. The actual matching to |
+ // scoped_refptr<base::MessageLoopProxy> happens in the callback since I |
+ // couldn't come up with a reliable AST matcher for doing that here. |
+ |
+ // Matches scoped_refptr<> which is being passed as an argument to a |
+ // function. |
+ auto proxy_refptr_expr_matcher = |
+ expr(hasType(recordDecl(hasName("::scoped_refptr"))), |
+ anyOf(hasAncestor(callExpr()), hasAncestor(memberCallExpr()))) |
+ .bind("expr"); |
+ |
+ auto proxy_refptr_member_expr_matcher = |
+ memberExpr(hasType(recordDecl(hasName("::scoped_refptr")))) |
+ .bind("member"); |
+ |
+ // Matches any constructor initializer. |
+ auto ctor_proxy_expr_matcher = constructorDecl( |
+ forEachConstructorInitializer(ctorInitializer().bind("ctor"))); |
+ |
+ // Matches any declaration reference to a MessageLoopProxy* or |
+ // scoped_refptr<>. |
+ auto proxy_decl_ref_expr_matcher = |
+ declRefExpr(anyOf(hasType(recordDecl(hasName("::scoped_refptr"))), |
+ hasType(pointsTo(recordDecl( |
+ hasName("base::MessageLoopProxy")))))).bind("ref"); |
+ |
+ match_finder->addMatcher(current_proxy_matcher, ¤t_proxy_callback_); |
+ match_finder->addMatcher(loop_proxy_getter_matcher, &proxy_getter_callback_); |
+ match_finder->addMatcher(thread_proxy_getter_matcher, |
+ &proxy_getter_callback_); |
+ match_finder->addMatcher(post_matcher, &post_callback_); |
+ match_finder->addMatcher(proxy_variable_matcher, &proxy_variable_callback_); |
+ match_finder->addMatcher(proxy_field_matcher, &proxy_variable_callback_); |
+ match_finder->addMatcher(refptr_proxy_field_matcher, |
+ &refptr_proxy_variable_callback_); |
+ match_finder->addMatcher(refptr_proxy_variable_matcher, |
+ &refptr_proxy_variable_callback_); |
+ match_finder->addMatcher(refptr_proxy_ref_field_matcher, |
+ &refptr_proxy_variable_callback_); |
+ match_finder->addMatcher(refptr_proxy_ref_variable_matcher, |
+ &refptr_proxy_variable_callback_); |
+ match_finder->addMatcher(proxy_expr_matcher, &proxy_expr_callback_); |
+ |
+ match_finder->addMatcher(proxy_refptr_expr_matcher, |
+ &generic_proxy_expr_callback_); |
+ match_finder->addMatcher(ctor_proxy_expr_matcher, |
+ &generic_proxy_expr_callback_); |
+ match_finder->addMatcher(proxy_decl_ref_expr_matcher, |
+ &generic_proxy_expr_callback_); |
+ match_finder->addMatcher(proxy_refptr_member_expr_matcher, |
+ &generic_proxy_expr_callback_); |
+ |
+ match_finder->addMatcher(proxy_method_matcher, |
+ &custom_proxy_getter_callback_); |
+ match_finder->addMatcher(refptr_proxy_method_matcher, |
+ &custom_proxy_getter_callback_); |
+} |
+ |
+void PostTaskCallback::run(const MatchFinder::MatchResult& result) { |
+ const CXXMemberCallExpr* call = |
+ result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); |
+ const Expr* obj = call->getImplicitObjectArgument(); |
+ const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); |
+ |
+ clang::CharSourceRange range = |
+ clang::CharSourceRange::getTokenRange(callee->getSourceRange()); |
+ clang::CharSourceRange obj_range = |
+ clang::CharSourceRange::getTokenRange(obj->getSourceRange()); |
+ |
+ std::string obj_text = clang::Lexer::getSourceText( |
+ obj_range, *result.SourceManager, result.Context->getLangOpts()); |
+ std::string method = call->getMethodDecl()->getNameInfo().getAsString(); |
+ |
+ // Rewrite the call to go through the task runner. |
+ bool is_arrow = callee->isArrow(); |
+ std::string replacement = |
+ obj_text + (is_arrow ? "->" : ".") + "task_runner()->" + method; |
+ |
+ // Rewrite MessageLoop::current() as ThreadTaskRunnerHandle::Get(). |
+ if (obj_text.find("MessageLoop::current") != obj_text.npos || |
+ obj_text.find("MessageLoopForIO::current") != obj_text.npos || |
+ obj_text.find("MessageLoopForUI::current") != obj_text.npos) { |
+ std::string ns = ""; |
+ if (obj_text.find("base::") == 0) { |
+ ns = "base::"; |
+ } |
+ replacement = ns + "ThreadTaskRunnerHandle::Get()->" + method; |
+ } |
+ |
+ replacements_->insert(Replacement(*result.SourceManager, range, replacement)); |
+} |
+ |
+void CurrentProxyCallback::run(const MatchFinder::MatchResult& result) { |
+ const CallExpr* call = result.Nodes.getNodeAs<CallExpr>("call"); |
+ clang::CharSourceRange range = |
+ clang::CharSourceRange::getTokenRange(call->getSourceRange()); |
+ llvm::StringRef text = clang::Lexer::getSourceText( |
+ range, *result.SourceManager, result.Context->getLangOpts()); |
+ |
+ std::string ns = ""; |
+ if (text.find("base::") == 0) { |
+ ns = "base::"; |
+ } |
+ std::string replacement = ns + "ThreadTaskRunnerHandle::Get()"; |
+ replacements_->insert(Replacement(*result.SourceManager, range, replacement)); |
+} |
+ |
+void ProxyGetterCallback::run(const MatchFinder::MatchResult& result) { |
+ const CXXMemberCallExpr* call = |
+ result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); |
+ const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); |
+ clang::CharSourceRange range = |
+ clang::CharSourceRange::getTokenRange(callee->getSourceRange()); |
+ |
+ std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, |
+ result.Context->getLangOpts()); |
+ |
+ text = ReplaceFirst(text, "message_loop_proxy", "task_runner"); |
+ replacements_->insert(Replacement(*result.SourceManager, range, text)); |
+} |
+ |
+void ProxyVariableCallback::run(const MatchFinder::MatchResult& result) { |
+ const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); |
+ const FieldDecl* field = result.Nodes.getNodeAs<FieldDecl>("field"); |
+ clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( |
+ var ? var->getSourceRange() : field->getSourceRange()); |
+ |
+ std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, |
+ result.Context->getLangOpts()); |
+ |
+ // Change the type of the pointee. |
+ text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); |
+ |
+ // Rename the pointer variable too. |
+ text = RenameMessageLoopProxyVariable(text); |
+ |
+ replacements_->insert(Replacement(*result.SourceManager, range, text)); |
+} |
+ |
+void RefPtrProxyVariableCallback::run(const MatchFinder::MatchResult& result) { |
+ const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); |
+ const FieldDecl* field = result.Nodes.getNodeAs<FieldDecl>("field"); |
+ clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( |
+ var ? var->getSourceRange() : field->getSourceRange()); |
+ |
+ std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, |
+ result.Context->getLangOpts()); |
+ if (text.find("MessageLoopProxy") == text.npos) |
+ return; |
+ |
+ // Change the type of the pointee. |
+ text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); |
+ |
+ // Rename the refptr variable too. |
+ text = RenameMessageLoopProxyVariable(text); |
+ |
+ replacements_->insert(Replacement(*result.SourceManager, range, text)); |
+} |
+ |
+void ProxyExprCallback::run(const MatchFinder::MatchResult& result) { |
+ const Expr* expr = result.Nodes.getNodeAs<Expr>("expr"); |
+ clang::CharSourceRange range = |
+ clang::CharSourceRange::getTokenRange(expr->getSourceRange()); |
+ |
+ llvm::StringRef text = clang::Lexer::getSourceText( |
+ range, *result.SourceManager, result.Context->getLangOpts()); |
+ |
+ std::string replacement = RenameMessageLoopProxyVariable(text); |
+ if (replacement != text) { |
+ replacements_->insert( |
+ Replacement(*result.SourceManager, range, replacement)); |
+ } |
+} |
+ |
+void GenericProxyExprCallback::run(const MatchFinder::MatchResult& result) { |
+ const Expr* expr = result.Nodes.getNodeAs<Expr>("expr"); |
+ const CXXCtorInitializer* ctor = |
+ result.Nodes.getNodeAs<CXXCtorInitializer>("ctor"); |
+ const DeclRefExpr* ref = result.Nodes.getNodeAs<DeclRefExpr>("ref"); |
+ const MemberExpr* member = result.Nodes.getNodeAs<MemberExpr>("member"); |
+ clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( |
+ expr ? expr->getSourceRange() : ref ? ref->getSourceRange() |
+ : member ? member->getSourceRange() |
+ : ctor->getSourceRange()); |
+ |
+ std::string type; |
+ if (expr) { |
+ type = expr->getType().getAsString(); |
+ } else if (ref) { |
+ type = ref->getType().getAsString(); |
+ } else if (member) { |
+ type = member->getType().getAsString(); |
+ } else { |
+ // Ignore base class constructors. |
+ if (!ctor->getMember()) |
+ return; |
+ type = ctor->getMember()->getType().getAsString(); |
+ } |
+ |
+ // Ignore unrelated scoped_refptr<>s. |
+ if (type.find("scoped_refptr<base::MessageLoopProxy>") == type.npos && |
+ type.find("base::MessageLoopProxy *") == type.npos) |
+ return; |
+ |
+ llvm::StringRef text = clang::Lexer::getSourceText( |
+ range, *result.SourceManager, result.Context->getLangOpts()); |
+ std::string replacement = RenameMessageLoopProxyVariable(text); |
+ |
+ // For field initializers, also try renaming the initializing value. |
+ if (ctor) { |
+ replacement = RenameMessageLoopProxyVariable(replacement); |
+ // Hack: rename any static getter because the renaming of the field will |
+ // conflict the edit that replaces the getter. |
+ replacement = ReplaceFirst(replacement, "base::MessageLoopProxy::current()", |
+ "base::ThreadTaskRunnerHandle::Get()"); |
+ } |
+ |
+ if (replacement != text) { |
+ replacements_->insert( |
+ Replacement(*result.SourceManager, range, replacement)); |
+ } |
+} |
+ |
+void CustomProxyGetterCallback::run(const MatchFinder::MatchResult& result) { |
+ const CXXMethodDecl* method = result.Nodes.getNodeAs<CXXMethodDecl>("method"); |
+ clang::CharSourceRange range = |
+ clang::CharSourceRange::getTokenRange(method->getReturnTypeSourceRange()); |
+ llvm::StringRef text = clang::Lexer::getSourceText( |
+ range, *result.SourceManager, result.Context->getLangOpts()); |
+ |
+ std::string replacement = |
+ ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); |
+ if (replacement != text) { |
+ replacements_->insert( |
+ Replacement(*result.SourceManager, range, replacement)); |
+ } |
+} |
+ |
+} // namespace |
+ |
+static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
+ |
+int main(int argc, const char* argv[]) { |
+ llvm::cl::OptionCategory category("MessageLoop refactoring tool"); |
+ CommonOptionsParser options(argc, argv, category); |
+ clang::tooling::ClangTool tool(options.getCompilations(), |
+ options.getSourcePathList()); |
+ |
+ Replacements replacements; |
+ MessageLoopRefactorer refactorer(&replacements); |
+ MatchFinder match_finder; |
+ refactorer.SetupMatchers(&match_finder); |
+ |
+ std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
+ clang::tooling::newFrontendActionFactory(&match_finder); |
+ int result = tool.run(frontend_factory.get()); |
+ if (result != 0) |
+ return result; |
+ |
+ // Each replacement line should have the following format: |
+ // r:<file path>:<offset>:<length>:<replacement text> |
+ // Only the <replacement text> field can contain embedded ":" characters. |
+ // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML |
+ // serialization and then use clang-apply-replacements, but that would require |
+ // copying and pasting a larger amount of boilerplate for all Chrome clang |
+ // tools. |
+ llvm::outs() << "==== BEGIN EDITS ====\n"; |
+ unsigned prev_end = 0; |
+ for (const auto& r : replacements) { |
+ // Discard overlapping edits. |
+ if (prev_end > r.getOffset()) |
+ continue; |
+ prev_end = r.getOffset() + r.getLength(); |
+ llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() |
+ << ":::" << r.getLength() << ":::" << r.getReplacementText() |
+ << "\n"; |
+ } |
+ llvm::outs() << "==== END EDITS ====\n"; |
+ |
+ return 0; |
+} |