| Index: tools/clang/value_cleanup/ListValueRewriter.cpp | 
| diff --git a/tools/clang/value_cleanup/ListValueRewriter.cpp b/tools/clang/value_cleanup/ListValueRewriter.cpp | 
| index b5e5260eab1e6210d71425bb85079440e94f6246..deb32411f4287247d332325d383eaf230871bf81 100644 | 
| --- a/tools/clang/value_cleanup/ListValueRewriter.cpp | 
| +++ b/tools/clang/value_cleanup/ListValueRewriter.cpp | 
| @@ -8,6 +8,8 @@ | 
| #include <algorithm> | 
|  | 
| #include "clang/AST/ASTContext.h" | 
| +#include "clang/AST/ParentMap.h" | 
| +#include "clang/AST/RecursiveASTVisitor.h" | 
| #include "clang/ASTMatchers/ASTMatchFinder.h" | 
| #include "clang/ASTMatchers/ASTMatchers.h" | 
| #include "clang/ASTMatchers/ASTMatchersMacros.h" | 
| @@ -23,6 +25,131 @@ using clang::tooling::Replacement; | 
| using clang::tooling::Replacements; | 
| using llvm::StringRef; | 
|  | 
| +namespace { | 
| + | 
| +// Helper class for AppendRawCallback to visit each DeclRefExpr for a given | 
| +// VarDecl. If it finds a DeclRefExpr it can't figure out how to rewrite, the | 
| +// traversal will be terminated early. | 
| +class CollectDeclRefExprVisitor | 
| +    : public clang::RecursiveASTVisitor<CollectDeclRefExprVisitor> { | 
| + public: | 
| +  CollectDeclRefExprVisitor(clang::SourceManager* source_manager, | 
| +                            clang::ASTContext* ast_context, | 
| +                            const clang::VarDecl* decl, | 
| +                            const clang::FunctionDecl* containing_function) | 
| +      : source_manager_(source_manager), | 
| +        ast_context_(ast_context), | 
| +        decl_(decl), | 
| +        is_valid_(decl->hasInit()), | 
| +        map_(containing_function->getBody()) {} | 
| + | 
| +  // RecursiveASTVisitor: | 
| +  bool VisitDeclRefExpr(const clang::DeclRefExpr* expr) { | 
| +    if (expr->getDecl() != decl_) | 
| +      return true; | 
| + | 
| +    const clang::Stmt* stmt = expr; | 
| +    while (stmt) { | 
| +      // TODO(dcheng): Add a const version of getParentIgnoreParenImpCasts. | 
| +      stmt = map_.getParentIgnoreParenImpCasts(const_cast<clang::Stmt*>(stmt)); | 
| + | 
| +      if (clang::isa<clang::MemberExpr>(stmt)) { | 
| +        // Member expressions need no special rewriting since std::unique_ptr | 
| +        // overloads `.' and `->'. | 
| +        return is_valid_; | 
| +      } else if (auto* member_call_expr = | 
| +                     clang::dyn_cast<clang::CXXMemberCallExpr>(stmt)) { | 
| +        return HandleMemberCallExpr(member_call_expr, expr); | 
| +      } else if (auto* binary_op = | 
| +                     clang::dyn_cast<clang::BinaryOperator>(stmt)) { | 
| +        return HandleBinaryOp(binary_op); | 
| +      } else { | 
| +        // Can't handle this so cancel the rewrite. | 
| +        stmt->dump(); | 
| +        return false; | 
| +      } | 
| +    } | 
| + | 
| +    assert(false); | 
| +    return false; | 
| +  } | 
| + | 
| +  const Replacements& replacements() const { return replacements_; } | 
| + | 
| + private: | 
| +  bool HandleMemberCallExpr(const clang::CXXMemberCallExpr* member_call_expr, | 
| +                            const clang::DeclRefExpr* decl_ref_expr) { | 
| +    // If this isn't a ListValue::Append() call, cancel the rewrite: it | 
| +    // will require manual inspection to determine if it's an ownership | 
| +    // transferring call or not. | 
| +    auto* method_decl = member_call_expr->getMethodDecl(); | 
| +    if (method_decl->getQualifiedNameAsString() != "base::ListValue::Append") | 
| +      return false; | 
| +    // Use-after-move is also a fatal error. | 
| +    if (!is_valid_) | 
| +      return false; | 
| + | 
| +    is_valid_ = false; | 
| + | 
| +    // Surround the DeclRefExpr with std::move(). | 
| +    replacements_.emplace(*source_manager_, decl_ref_expr->getLocStart(), 0, | 
| +                          "std::move("); | 
| + | 
| +    clang::SourceLocation end = clang::Lexer::getLocForEndOfToken( | 
| +        decl_ref_expr->getLocEnd(), 0, *source_manager_, | 
| +        ast_context_->getLangOpts()); | 
| +    replacements_.emplace(*source_manager_, end, 0, ")"); | 
| +    return true; | 
| +  } | 
| + | 
| +  bool HandleBinaryOp(const clang::BinaryOperator* op) { | 
| +    if (op->isRelationalOp() || op->isEqualityOp() || op->isLogicalOp()) { | 
| +      // Supported binary operations for which no rewrites need to be done. | 
| +      return is_valid_; | 
| +    } | 
| +    if (!op->isAssignmentOp()) { | 
| +      // Pointer arithmetic or something else clever. Just cancel the rewrite. | 
| +      return false; | 
| +    } | 
| +    if (op->isCompoundAssignmentOp()) { | 
| +      // +=, -=, etc. Give up and cancel the rewrite. | 
| +      return false; | 
| +    } | 
| + | 
| +    const clang::Expr* rhs = op->getRHS()->IgnoreParenImpCasts(); | 
| +    const clang::CXXNewExpr* new_expr = clang::dyn_cast<clang::CXXNewExpr>(rhs); | 
| +    if (!new_expr) { | 
| +      // The variable isn't being assigned the result of a new operation. Just | 
| +      // cancel the rewrite. | 
| +      return false; | 
| +    } | 
| + | 
| +    is_valid_ = true; | 
| + | 
| +    // Rewrite the assignment operation to use std::unique_ptr::reset(). | 
| +    clang::CharSourceRange range = clang::CharSourceRange::getCharRange( | 
| +        op->getOperatorLoc(), op->getRHS()->getLocStart()); | 
| +    replacements_.emplace(*source_manager_, range, ".reset("); | 
| + | 
| +    clang::SourceLocation expr_end = clang::Lexer::getLocForEndOfToken( | 
| +        op->getLocEnd(), 0, *source_manager_, ast_context_->getLangOpts()); | 
| +    replacements_.emplace(*source_manager_, expr_end, 0, ")"); | 
| +    return true; | 
| +  } | 
| + | 
| +  clang::SourceManager* const source_manager_; | 
| +  clang::ASTContext* const ast_context_; | 
| +  const clang::VarDecl* const decl_; | 
| +  // Tracks the state of |decl_| during the traversal. |decl_| becomes valid | 
| +  // upon initialization/assignment and becomes invalid when passed as an | 
| +  // argument to base::ListValue::Append(base::Value*). | 
| +  bool is_valid_; | 
| +  clang::ParentMap map_; | 
| +  Replacements replacements_; | 
| +}; | 
| + | 
| +}  // namespace | 
| + | 
| ListValueRewriter::AppendCallback::AppendCallback(Replacements* replacements) | 
| : replacements_(replacements) {} | 
|  | 
| @@ -135,17 +262,90 @@ void ListValueRewriter::AppendReleasedUniquePtrCallback::run( | 
| replacements_->emplace(*result.SourceManager, insertion_range, "std::move("); | 
| } | 
|  | 
| +ListValueRewriter::AppendRawPtrCallback::AppendRawPtrCallback( | 
| +    Replacements* replacements) | 
| +    : replacements_(replacements) {} | 
| + | 
| +void ListValueRewriter::AppendRawPtrCallback::run( | 
| +    const MatchFinder::MatchResult& result) { | 
| +  auto* var_decl = result.Nodes.getNodeAs<clang::VarDecl>("varDecl"); | 
| +  // As an optimization, skip processing if it's already been visited, since | 
| +  // this match callback walks the entire function body. | 
| +  if (visited_.find(var_decl) != visited_.end()) | 
| +    return; | 
| +  visited_.insert(var_decl); | 
| +  auto* function_context = var_decl->getParentFunctionOrMethod(); | 
| +  assert(function_context && "local var not in function context?!"); | 
| +  auto* function_decl = clang::cast<clang::FunctionDecl>(function_context); | 
| + | 
| +  auto* type_source_info = var_decl->getTypeSourceInfo(); | 
| +  assert(type_source_info && "no type source info for VarDecl?!"); | 
| +  // Don't bother trying to handle qualifiers. | 
| +  clang::QualType qual_type = var_decl->getType(); | 
| +  if (qual_type.hasQualifiers()) { | 
| +    return; | 
| +  } | 
| + | 
| +  CollectDeclRefExprVisitor visitor(result.SourceManager, result.Context, | 
| +                                    var_decl, function_decl); | 
| +  if (!visitor.TraverseStmt(function_decl->getBody())) | 
| +    return; | 
| + | 
| +  // Rewrite the variable type to use std::unique_ptr. | 
| +  clang::CharSourceRange type_range = clang::CharSourceRange::getTokenRange( | 
| +      type_source_info->getTypeLoc().getSourceRange()); | 
| +  std::string replacement_type = "std::unique_ptr<"; | 
| +  while (true) { | 
| +    const clang::Type* type = qual_type.getTypePtr(); | 
| +    if (auto* auto_type = type->getAs<clang::AutoType>()) { | 
| +      if (!auto_type->isDeduced()) { | 
| +        // If an AutoType isn't deduced, the rewriter can't do anything. | 
| +        return; | 
| +      } | 
| +      qual_type = auto_type->getDeducedType(); | 
| +    } else if (auto* pointer_type = type->getAs<clang::PointerType>()) { | 
| +      qual_type = pointer_type->getPointeeType(); | 
| +    } else { | 
| +      break; | 
| +    } | 
| +  } | 
| +  replacement_type += qual_type.getAsString(); | 
| +  replacement_type += ">"; | 
| +  replacements_->emplace(*result.SourceManager, type_range, replacement_type); | 
| + | 
| +  // Initialized with `=' | 
| +  if (var_decl->hasInit() && | 
| +      var_decl->getInitStyle() == clang::VarDecl::CInit) { | 
| +    clang::SourceLocation name_end = clang::Lexer::getLocForEndOfToken( | 
| +        var_decl->getLocation(), 0, *result.SourceManager, | 
| +        result.Context->getLangOpts()); | 
| +    clang::CharSourceRange range = clang::CharSourceRange::getCharRange( | 
| +        name_end, var_decl->getInit()->getLocStart()); | 
| +    replacements_->emplace(*result.SourceManager, range, "("); | 
| + | 
| +    clang::SourceLocation init_end = clang::Lexer::getLocForEndOfToken( | 
| +        var_decl->getInit()->getLocEnd(), 0, *result.SourceManager, | 
| +        result.Context->getLangOpts()); | 
| +    replacements_->emplace(*result.SourceManager, init_end, 0, ")"); | 
| +  } | 
| + | 
| +  // Also append the collected replacements from visiting the DeclRefExprs. | 
| +  replacements_->insert(visitor.replacements().begin(), | 
| +                        visitor.replacements().end()); | 
| +} | 
| + | 
| ListValueRewriter::ListValueRewriter(Replacements* replacements) | 
| : append_boolean_callback_(replacements), | 
| append_integer_callback_(replacements), | 
| append_double_callback_(replacements), | 
| append_string_callback_(replacements), | 
| -      append_released_unique_ptr_callback_(replacements) {} | 
| +      append_released_unique_ptr_callback_(replacements), | 
| +      append_raw_ptr_callback_(replacements) {} | 
|  | 
| void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) { | 
| -  auto is_list_append = | 
| -      allOf(callee(cxxMethodDecl(hasName("::base::ListValue::Append"))), | 
| -            argumentCountIs(1)); | 
| +  auto is_list_append = cxxMemberCallExpr( | 
| +      callee(cxxMethodDecl(hasName("::base::ListValue::Append"))), | 
| +      argumentCountIs(1)); | 
|  | 
| // base::ListValue::Append(new base::FundamentalValue(bool)) | 
| //     => base::ListValue::AppendBoolean() | 
| @@ -241,4 +441,38 @@ void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) { | 
| is_unique_ptr_release, | 
| on(id("objectExpr", expr()))))))), | 
| &append_released_unique_ptr_callback_); | 
| + | 
| +  // Simple versions of the following pattern. Note the callback itself does | 
| +  // much of the filtering (to detect use-after-move, things that aren't | 
| +  // assigned the result of a new expression, etc). | 
| +  // | 
| +  // base::ListValue* this_list = new base::ListValue; | 
| +  // this_list->AppendInteger(1); | 
| +  // that_list->Append(this_list); | 
| +  // | 
| +  // will be rewritten to | 
| +  // | 
| +  // std::unique_ptr<base::ListValue> this_list(new base::ListValue); | 
| +  // this_list->AppendInteger(1); | 
| +  // that_list->Append(std::move(this_list); | 
| +  match_finder->addMatcher( | 
| +      cxxMemberCallExpr( | 
| +          is_list_append, | 
| +          hasArgument( | 
| +              0, | 
| +              ignoringParenImpCasts(id( | 
| +                  "declRefExpr", | 
| +                  declRefExpr(to(id( | 
| +                      "varDecl", | 
| +                      varDecl( | 
| +                          hasLocalStorage(), | 
| +                          anyOf(hasInitializer( | 
| +                                    // Note this won't match C++11 uniform | 
| +                                    // initialization syntax, since the | 
| +                                    // CXXNewExpr is wrapped in an | 
| +                                    // InitListExpr in that case. | 
| +                                    ignoringParenImpCasts(cxxNewExpr())), | 
| +                                unless(hasInitializer(expr()))), | 
| +                          unless(parmVarDecl()))))))))), | 
| +      &append_raw_ptr_callback_); | 
| } | 
|  |