| 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;
|
| +}
|
|
|