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 |