Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Unified Diff: tools/clang/BindMigrate/OldCallbackTransform.cpp

Issue 7886056: Clang plugin that rewrites PostTask(_, NewRunnableMethod(...)) to PostTask(_, base::Bind(...)); (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix lifetime issue where BindMigrateAction is deleted before the ASTConsumer is finished. Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/clang/BindMigrate/OldCallbackTransform.h ('k') | tools/clang/BindMigrate/PostTaskTransform.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/clang/BindMigrate/OldCallbackTransform.cpp
diff --git a/tools/clang/BindMigrate/OldCallbackTransform.cpp b/tools/clang/BindMigrate/OldCallbackTransform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..92eac98f9770953049519c7ebf25925148f8213f
--- /dev/null
+++ b/tools/clang/BindMigrate/OldCallbackTransform.cpp
@@ -0,0 +1,417 @@
+// Copyright (c) 2011 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.
+
+#include "OldCallbackTransform.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Rewrite/Rewriter.h"
+
+#include "DiagnosticEmitter.h"
+
+using namespace std;
+
+namespace {
+
+std::string RemoveSpaceBeforePtrOrRefInType(const std::string &orig) {
+ if (orig.size() < 2) {
+ return orig;
+ }
+
+ string out;
+ std::string::const_iterator it = orig.begin();
+ std::string::const_iterator end = orig.end();
+ char prev_char = *(it++);
+ for (; it != end; ++it) {
+ // Since it's a type, we know it's not going to be a binary expression, so
+ // we can just look for the single chars and deicde if we should skip the
+ // last char.
+ if (*it != '*' && *it != '&') {
+ out.append(1, prev_char);
+ }
+
+ prev_char = *it;
+ }
+
+ out.append(1, prev_char);
+ return out;
+}
+
+} // namespace
+
+namespace clang {
+
+OldCallbackTransform::OldCallbackTransform(ASTContext* context,
+ Rewriter* rewriter,
+ DiagnosticEmitter* emitter)
+ : context_(context), rewriter_(rewriter), error_emitter_(emitter) {
+}
+
+bool OldCallbackTransform::WouldTransformStmt(Stmt* statement) {
+ return IsOldCallbackGenerator(statement) || IsOldCallbackRun(statement);
+}
+
+bool OldCallbackTransform::WouldTransformDecl(Decl* decl) {
+ bool dont_care = false;
+ if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
+ return IsOldCallbackType(td->getUnderlyingType(), &dont_care);
+ } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
+ return IsOldCallbackType(vd->getType(), &dont_care);
+ }
+
+ return false;
+}
+
+void OldCallbackTransform::TransformStmt(Stmt *statement) {
+ // Catch all NewCallback() calls.
+ RewriteIfOldCallbackGenerator(statement);
+
+ // Catch all CallbackN::Run() calls.
+ RewriteIfOldCallbackRun(statement);
+}
+
+void OldCallbackTransform::TransformDecl(Decl *decl) {
+ // Catch all typedefs and variables/parameter declarations.
+ if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
+ RewriteTypedefIfOldCallback(td);
+ } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
+ RewriteVarIfOldCallback(vd);
+ }
+}
+
+void OldCallbackTransform::RewriteIfOldCallbackGenerator(Stmt* statement) {
+ if (!IsOldCallbackGenerator(statement)) {
+ return;
+ }
+
+ // Both callback variants only work with methods. So we always s
+ // NewCallback* -> base::Bind
+ CallExpr* ce = dyn_cast<CallExpr>(statement);
+ if (rewriter_->ReplaceText(ce->getCallee()->getSourceRange(),
+ "base::Bind")) {
+ error_emitter_->EmitError(statement->getLocStart(),
+ "Unable to rewrite to base::Bind()");
+ }
+
+ Expr* arg1 = ce->getArgs()[0];
+ Expr* arg2 = ce->getArgs()[1];
+
+ if (rewriter_->ReplaceStmt(arg1, arg2)) {
+ error_emitter_->EmitError(statement->getLocStart(),
+ "Unable to swap the first argument");
+ }
+
+ std::string unretained_arg("base::Unretained(");
+ unretained_arg += rewriter_->ConvertToString(arg1);
+ unretained_arg += ")";
+ if (rewriter_->ReplaceText(arg2->getSourceRange(), unretained_arg)) {
+ error_emitter_->EmitError(statement->getLocStart(),
+ "Unable to rewrite the second argument");
+ }
+}
+
+void OldCallbackTransform::RewriteIfOldCallbackRun(Stmt* statement) {
+ if (!IsOldCallbackRun(statement)) {
+ return;
+ }
+
+ // Grab the statement and do "s/->Run(/.Run(/".
+ SourceManager& source_manager = context_->getSourceManager();
+ std::pair<FileID,unsigned> start = source_manager.getDecomposedLoc(
+ statement->getLocStart());
+ std::pair<FileID,unsigned> end = source_manager.getDecomposedLoc(
+ statement->getLocEnd());
+
+ StringRef buffer = source_manager.getBufferData(start.first);
+ static const char* kOldRun = "->Run(";
+ size_t where = buffer.find(kOldRun, start.second);
+
+
+ if (where > end.second) {
+ // There's no ".Run(" in this statement. Lucky us. Exit early.
+ return;
+ }
+
+ SourceLocation replace_start =
+ source_manager.getLocForStartOfFile(start.first)
+ .getFileLocWithOffset(where);
+ std::pair<FileID,unsigned> rstart_b = source_manager.getDecomposedLoc(
+ replace_start);
+
+ if (rewriter_->ReplaceText(replace_start, strlen(kOldRun), ".Run(")) {
+ error_emitter_->EmitWarning(replace_start,
+ "Unable to rewrite ->Run() -> .Run()");
+ }
+}
+
+void OldCallbackTransform::RewriteTypedefIfOldCallback(TypedefDecl* td) {
+ // Skip anything from callback_old.h. That'd be silly.
+ // TODO(ajwong): do this.
+
+ bool is_callback_with_return = false;
+ QualType underlying_type = td->getUnderlyingType();
+ if (!IsOldCallbackType(underlying_type, &is_callback_with_return)) {
+ return;
+ }
+
+ string new_typedef = "typedef ";
+
+ if (!CallbackFromType(underlying_type, td->getLocStart(),
+ is_callback_with_return, &new_typedef)) {
+ // Error emited by CallbackFromType function.
+ return;
+ }
+
+ if (underlying_type->isReferenceType()) {
+ error_emitter_->EmitWarning(
+ td->getLocStart(),
+ "Template to a reference type? That's strange. Skipping.");
+ return;
+ }
+
+ // Add identifier
+ new_typedef += " ";
+ new_typedef += td->getIdentifier()->getName();
+ new_typedef += ";";
+
+ // ?!?!?!?!! WTF DOES THIS RETURN TRUE ON ERROR?!
+ if (rewriter_->ReplaceText(td->getSourceRange(), new_typedef)) {
+ error_emitter_->EmitError(td->getLocStart(), "Unable to rewrite typedef.");
+ }
+}
+
+void OldCallbackTransform::RewriteVarIfOldCallback(VarDecl* vd) {
+ bool is_callback_with_return = false;
+ if (!IsOldCallbackType(vd->getType(), &is_callback_with_return)) {
+ return;
+ }
+
+ std::string new_decl;
+ if (!CallbackFromType(vd->getType(), vd->getLocStart(),
+ is_callback_with_return, &new_decl)) {
+ // Error emited by CallbackFromType function.
+ return;
+ }
+
+ new_decl += " ";
+ new_decl += vd->getIdentifier()->getName();
+ if (rewriter_->ReplaceText(vd->getSourceRange(), new_decl)) {
+ error_emitter_->EmitError(vd->getLocStart(), "Unable to rewrite var.");
+ }
+}
+
+bool OldCallbackTransform::CallbackFromType(QualType type,
+ SourceLocation loc,
+ bool is_callback_with_return,
+ std::string* output) {
+ string buffer;
+
+ // Rewrite the whole declaration including const, and volatile. Start
+ // constructing here. Make sure to not propagate pointers though.
+ if (type.isConstQualified()) buffer += "const ";
+ if (type.isRestrictQualified()) buffer += "restrict ";
+ if (type.isVolatileQualified()) buffer += "volatile ";
+
+ if (is_callback_with_return) {
+ if (!CallbackFromTypeWithReturn(type, loc, &buffer)) {
+ return false;
+ }
+ } else {
+ if (!CallbackFromVoidReturnType(type, loc, &buffer)) {
+ return false;
+ }
+ }
+
+ if (type->isReferenceType()) buffer += "&";
+
+ output->append(buffer);
+ return true;
+}
+
+bool OldCallbackTransform::CallbackFromVoidReturnType(QualType type,
+ SourceLocation loc,
+ std::string* output) {
+ // Grab record.
+ ClassTemplateSpecializationDecl* callback_decl;
+ if (type->isPointerType()) {
+ callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+ type->getPointeeType()->getAsCXXRecordDecl());
+ } else {
+ callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+ type.getNonReferenceType()->getAsCXXRecordDecl());
+ }
+
+ // This wasn't a specialization? Huh?
+ if (!callback_decl) {
+ error_emitter_->EmitWarning(
+ loc,
+ "Skipping Callback* Identifier doesn't refer to class or template.");
+ return false;
+ }
+
+ // Expect only one argument in the runner. That argument will be a Tuple type.
+ const TemplateArgumentList& template_args = callback_decl->getTemplateArgs();
+ if (template_args.size() != 1) {
+ error_emitter_->EmitWarning(
+ loc,
+ "Skipping Callback*. "
+ "Expected to find TupleN<> in Runner specialization.");
+ return false;
+ }
+
+ CXXRecordDecl* runner_specialization =
+ template_args[0].getAsType()->getAsCXXRecordDecl();
+
+ if (!runner_specialization ||
+ runner_specialization->getNameAsString().find("Tuple") != 0) {
+ error_emitter_->EmitWarning(
+ loc,
+ "Skipping Callback*. "
+ "Expected to find TupleN<> in Runner specialization.");
+ return false;
+ }
+
+ ClassTemplateSpecializationDecl* tuple_decl =
+ dyn_cast<ClassTemplateSpecializationDecl>(runner_specialization);
+ if (!tuple_decl &&
+ runner_specialization->getNameAsString() != "Tuple0") {
+ error_emitter_->EmitWarning(loc, "Skipping Callback*. Expected Tuple0.");
+ return false;
+ }
+
+ if (!tuple_decl) {
+ *output += "base::Closure";
+ } else {
+ ConvertTupleType(tuple_decl->getTemplateArgs(), output);
+ }
+
+ return true;
+}
+
+bool OldCallbackTransform::CallbackFromTypeWithReturn(QualType type,
+ SourceLocation loc,
+ std::string* output) {
+ // We know we're a CallbackFromTypeWithReturn<>::Type value now so just dig
+ // out the info.
+ CXXRecordDecl* callback_decl;
+ if (type->isPointerType()) {
+ callback_decl = type->getPointeeType()->getAsCXXRecordDecl();
+ } else {
+ callback_decl = type.getNonReferenceType()->getAsCXXRecordDecl();
+ }
+
+ ClassTemplateSpecializationDecl* specialization_decl =
+ dyn_cast<ClassTemplateSpecializationDecl>(callback_decl->getParent());
+
+ string buffer;
+ llvm::raw_string_ostream output_os(buffer);
+ output_os << "base::Callback<";
+ specialization_decl->getTemplateArgs()[0].print(context_->PrintingPolicy,
+ output_os);
+ output_os << "(void)>";
+
+ output_os.flush();
+ output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
+
+ return true;
+}
+
+bool OldCallbackTransform::IsOldCallbackType(QualType type,
+ bool* is_callback_with_return) {
+
+ // First off, is this thing class, or a poitner to a class?
+ QualType bare_type = type.getNonReferenceType();
+ if (bare_type->isPointerType()) {
+ bare_type = bare_type->getPointeeType();
+ }
+ const CXXRecordDecl* type_decl = bare_type->getCXXRecordDeclForPointerType();
+ if (!type_decl) type_decl = bare_type->getAsCXXRecordDecl();
+ if (!type_decl) return false;
+ type_decl = type_decl->getCanonicalDecl();
+
+ // Yay. A class. How quiant. Okay "class," what's your name?
+ string name = type_decl->getNameAsString();
+
+ // A CallbackRunner huh? Well, aren't you special?
+ if (name == "CallbackRunner") return true;
+
+ // We don't normally accept Types, but when we do, we accept
+ // CallbackWithReturnValue::Types.
+ if (name == "Type") {
+ if (const CXXRecordDecl* encloding_class =
+ dyn_cast_or_null<CXXRecordDecl>(type_decl->getParent())) {
+ if (encloding_class->getNameAsString() == "CallbackWithReturnValue") {
+ *is_callback_with_return = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool OldCallbackTransform::IsOldCallbackGenerator(Stmt* statement) {
+ CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
+ if (!ce) return false;
+
+ FunctionDecl *fd = ce->getDirectCallee();
+ if (!fd) return false;
+
+ std::string function_name = fd->getNameInfo().getName().getAsString();
+ if ("NewCallback" != function_name &&
+ "NewCallbackWithReturnValue" != function_name) {
+ return false;
+ }
+
+ if (ce->getNumArgs() < 2) {
+ error_emitter_->EmitWarning(
+ statement->getLocStart(),
+ "NewCallback.* with less than 2 args?! Inconceivable!");
+ return false;
+ }
+
+ return true;
+}
+
+bool OldCallbackTransform::IsOldCallbackRun(Stmt* statement) {
+ bool is_callback_with_return = false;
+ CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
+ if (!ce) return false;
+
+ CXXMethodDecl *md = dyn_cast_or_null<CXXMethodDecl>(ce->getDirectCallee());
+ if (!md) return false;
+
+ std::string function_name = md->getNameInfo().getName().getAsString();
+ if ("Run" != function_name) return false;
+
+ if (!IsOldCallbackType(
+ md->getParent()->getTypeForDecl()->getCanonicalTypeUnqualified(),
+ &is_callback_with_return)) {
+ return false;
+ }
+
+ return true;
+}
+
+void OldCallbackTransform::ConvertTupleType(
+ const TemplateArgumentList& template_args,
+ std::string* output) {
+ string buffer;
+ llvm::raw_string_ostream output_os(buffer);
+ output_os << "base::Callback<void(";
+ for (size_t i = 0; i < template_args.size(); ++i) {
+ template_args[i].print(context_->PrintingPolicy, output_os);
+ if (i != (template_args.size() - 1)) {
+ output_os << ", ";
+ }
+ }
+ output_os << ")>";
+
+ output_os.flush();
+ output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
+}
+
+} // namespace clang
« no previous file with comments | « tools/clang/BindMigrate/OldCallbackTransform.h ('k') | tools/clang/BindMigrate/PostTaskTransform.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698