Home | History | Annotate | Download | only in value_cleanup
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ListValueRewriter.h"
      6 
      7 #include <assert.h>
      8 #include <algorithm>
      9 
     10 #include "clang/AST/ASTContext.h"
     11 #include "clang/AST/ParentMap.h"
     12 #include "clang/AST/RecursiveASTVisitor.h"
     13 #include "clang/ASTMatchers/ASTMatchFinder.h"
     14 #include "clang/ASTMatchers/ASTMatchers.h"
     15 #include "clang/ASTMatchers/ASTMatchersMacros.h"
     16 #include "clang/Basic/CharInfo.h"
     17 #include "clang/Basic/SourceManager.h"
     18 #include "clang/Frontend/FrontendActions.h"
     19 #include "clang/Lex/Lexer.h"
     20 #include "clang/Tooling/Refactoring.h"
     21 #include "llvm/ADT/STLExtras.h"
     22 
     23 using namespace clang::ast_matchers;
     24 using clang::tooling::Replacement;
     25 using llvm::StringRef;
     26 
     27 namespace {
     28 
     29 // Helper class for AppendRawCallback to visit each DeclRefExpr for a given
     30 // VarDecl. If it finds a DeclRefExpr it can't figure out how to rewrite, the
     31 // traversal will be terminated early.
     32 class CollectDeclRefExprVisitor
     33     : public clang::RecursiveASTVisitor<CollectDeclRefExprVisitor> {
     34  public:
     35   CollectDeclRefExprVisitor(clang::SourceManager* source_manager,
     36                             clang::ASTContext* ast_context,
     37                             const clang::VarDecl* decl,
     38                             const clang::FunctionDecl* containing_function)
     39       : source_manager_(source_manager),
     40         ast_context_(ast_context),
     41         decl_(decl),
     42         is_valid_(decl->hasInit()),
     43         map_(containing_function->getBody()) {}
     44 
     45   // RecursiveASTVisitor:
     46   bool VisitDeclRefExpr(const clang::DeclRefExpr* expr) {
     47     if (expr->getDecl() != decl_)
     48       return true;
     49 
     50     const clang::Stmt* stmt = expr;
     51     while (stmt) {
     52       // TODO(dcheng): Add a const version of getParentIgnoreParenImpCasts.
     53       stmt = map_.getParentIgnoreParenImpCasts(const_cast<clang::Stmt*>(stmt));
     54 
     55       if (clang::isa<clang::MemberExpr>(stmt)) {
     56         // Member expressions need no special rewriting since std::unique_ptr
     57         // overloads `.' and `->'.
     58         return is_valid_;
     59       } else if (auto* member_call_expr =
     60                      clang::dyn_cast<clang::CXXMemberCallExpr>(stmt)) {
     61         return HandleMemberCallExpr(member_call_expr, expr);
     62       } else if (auto* binary_op =
     63                      clang::dyn_cast<clang::BinaryOperator>(stmt)) {
     64         return HandleBinaryOp(binary_op);
     65       } else {
     66         // Can't handle this so cancel the rewrite.
     67         stmt->dump();
     68         return false;
     69       }
     70     }
     71 
     72     assert(false);
     73     return false;
     74   }
     75 
     76   const std::set<clang::tooling::Replacement>& replacements() const {
     77     return replacements_;
     78   }
     79 
     80  private:
     81   bool HandleMemberCallExpr(const clang::CXXMemberCallExpr* member_call_expr,
     82                             const clang::DeclRefExpr* decl_ref_expr) {
     83     // If this isn't a ListValue::Append() call, cancel the rewrite: it
     84     // will require manual inspection to determine if it's an ownership
     85     // transferring call or not.
     86     auto* method_decl = member_call_expr->getMethodDecl();
     87     if (method_decl->getQualifiedNameAsString() != "base::ListValue::Append")
     88       return false;
     89     // Use-after-move is also a fatal error.
     90     if (!is_valid_)
     91       return false;
     92 
     93     is_valid_ = false;
     94 
     95     // Surround the DeclRefExpr with std::move().
     96     replacements_.emplace(*source_manager_, decl_ref_expr->getLocStart(), 0,
     97                           "std::move(");
     98 
     99     clang::SourceLocation end = clang::Lexer::getLocForEndOfToken(
    100         decl_ref_expr->getLocEnd(), 0, *source_manager_,
    101         ast_context_->getLangOpts());
    102     replacements_.emplace(*source_manager_, end, 0, ")");
    103     return true;
    104   }
    105 
    106   bool HandleBinaryOp(const clang::BinaryOperator* op) {
    107     if (op->isRelationalOp() || op->isEqualityOp() || op->isLogicalOp()) {
    108       // Supported binary operations for which no rewrites need to be done.
    109       return is_valid_;
    110     }
    111     if (!op->isAssignmentOp()) {
    112       // Pointer arithmetic or something else clever. Just cancel the rewrite.
    113       return false;
    114     }
    115     if (op->isCompoundAssignmentOp()) {
    116       // +=, -=, etc. Give up and cancel the rewrite.
    117       return false;
    118     }
    119 
    120     const clang::Expr* rhs = op->getRHS()->IgnoreParenImpCasts();
    121     const clang::CXXNewExpr* new_expr = clang::dyn_cast<clang::CXXNewExpr>(rhs);
    122     if (!new_expr) {
    123       // The variable isn't being assigned the result of a new operation. Just
    124       // cancel the rewrite.
    125       return false;
    126     }
    127 
    128     is_valid_ = true;
    129 
    130     // Rewrite the assignment operation to use std::unique_ptr::reset().
    131     clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
    132         op->getOperatorLoc(), op->getRHS()->getLocStart());
    133     replacements_.emplace(*source_manager_, range, ".reset(");
    134 
    135     clang::SourceLocation expr_end = clang::Lexer::getLocForEndOfToken(
    136         op->getLocEnd(), 0, *source_manager_, ast_context_->getLangOpts());
    137     replacements_.emplace(*source_manager_, expr_end, 0, ")");
    138     return true;
    139   }
    140 
    141   clang::SourceManager* const source_manager_;
    142   clang::ASTContext* const ast_context_;
    143   const clang::VarDecl* const decl_;
    144   // Tracks the state of |decl_| during the traversal. |decl_| becomes valid
    145   // upon initialization/assignment and becomes invalid when passed as an
    146   // argument to base::ListValue::Append(base::Value*).
    147   bool is_valid_;
    148   clang::ParentMap map_;
    149   std::set<clang::tooling::Replacement> replacements_;
    150 };
    151 
    152 }  // namespace
    153 
    154 ListValueRewriter::AppendCallback::AppendCallback(
    155     std::set<clang::tooling::Replacement>* replacements)
    156     : replacements_(replacements) {}
    157 
    158 void ListValueRewriter::AppendCallback::run(
    159     const MatchFinder::MatchResult& result) {
    160   // Delete `new base::*Value(' and `)'.
    161   auto* newExpr = result.Nodes.getNodeAs<clang::CXXNewExpr>("newExpr");
    162   auto* argExpr = result.Nodes.getNodeAs<clang::Expr>("argExpr");
    163 
    164   // Note that for the end loc, we use the expansion loc: the argument might be
    165   // a macro like true and false.
    166   clang::CharSourceRange pre_arg_range = clang::CharSourceRange::getCharRange(
    167       newExpr->getLocStart(),
    168       result.SourceManager->getExpansionLoc(argExpr->getLocStart()));
    169   replacements_->emplace(*result.SourceManager, pre_arg_range, "");
    170 
    171   clang::CharSourceRange post_arg_range =
    172       clang::CharSourceRange::getTokenRange(newExpr->getLocEnd());
    173   replacements_->emplace(*result.SourceManager, post_arg_range, "");
    174 }
    175 
    176 ListValueRewriter::AppendBooleanCallback::AppendBooleanCallback(
    177     std::set<clang::tooling::Replacement>* replacements)
    178     : AppendCallback(replacements) {}
    179 
    180 void ListValueRewriter::AppendBooleanCallback::run(
    181     const MatchFinder::MatchResult& result) {
    182   // Replace 'Append' with 'AppendBoolean'.
    183   auto* callExpr = result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("callExpr");
    184 
    185   clang::CharSourceRange call_range =
    186       clang::CharSourceRange::getTokenRange(callExpr->getExprLoc());
    187   replacements_->emplace(*result.SourceManager, call_range, "AppendBoolean");
    188 
    189   AppendCallback::run(result);
    190 }
    191 
    192 ListValueRewriter::AppendIntegerCallback::AppendIntegerCallback(
    193     std::set<clang::tooling::Replacement>* replacements)
    194     : AppendCallback(replacements) {}
    195 
    196 void ListValueRewriter::AppendIntegerCallback::run(
    197     const MatchFinder::MatchResult& result) {
    198   // Replace 'Append' with 'AppendInteger'.
    199   auto* callExpr = result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("callExpr");
    200 
    201   clang::CharSourceRange call_range =
    202       clang::CharSourceRange::getTokenRange(callExpr->getExprLoc());
    203   replacements_->emplace(*result.SourceManager, call_range, "AppendInteger");
    204 
    205   AppendCallback::run(result);
    206 }
    207 
    208 ListValueRewriter::AppendDoubleCallback::AppendDoubleCallback(
    209     std::set<clang::tooling::Replacement>* replacements)
    210     : AppendCallback(replacements) {}
    211 
    212 void ListValueRewriter::AppendDoubleCallback::run(
    213     const MatchFinder::MatchResult& result) {
    214   // Replace 'Append' with 'AppendDouble'.
    215   auto* callExpr = result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("callExpr");
    216 
    217   clang::CharSourceRange call_range =
    218       clang::CharSourceRange::getTokenRange(callExpr->getExprLoc());
    219   replacements_->emplace(*result.SourceManager, call_range, "AppendDouble");
    220 
    221   AppendCallback::run(result);
    222 }
    223 
    224 ListValueRewriter::AppendStringCallback::AppendStringCallback(
    225     std::set<clang::tooling::Replacement>* replacements)
    226     : AppendCallback(replacements) {}
    227 
    228 void ListValueRewriter::AppendStringCallback::run(
    229     const MatchFinder::MatchResult& result) {
    230   // Replace 'Append' with 'AppendString'.
    231   auto* callExpr = result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("callExpr");
    232 
    233   clang::CharSourceRange call_range =
    234       clang::CharSourceRange::getTokenRange(callExpr->getExprLoc());
    235   replacements_->emplace(*result.SourceManager, call_range, "AppendString");
    236 
    237   AppendCallback::run(result);
    238 }
    239 
    240 ListValueRewriter::AppendReleasedUniquePtrCallback::
    241     AppendReleasedUniquePtrCallback(
    242         std::set<clang::tooling::Replacement>* replacements)
    243     : replacements_(replacements) {}
    244 
    245 void ListValueRewriter::AppendReleasedUniquePtrCallback::run(
    246     const MatchFinder::MatchResult& result) {
    247   auto* object_expr = result.Nodes.getNodeAs<clang::Expr>("objectExpr");
    248   bool arg_is_rvalue = object_expr->Classify(*result.Context).isRValue();
    249 
    250   // Remove .release()
    251   auto* member_call =
    252       result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("memberCall");
    253   auto* member_expr = result.Nodes.getNodeAs<clang::MemberExpr>("memberExpr");
    254   clang::CharSourceRange release_range = clang::CharSourceRange::getTokenRange(
    255       member_expr->getOperatorLoc(), member_call->getLocEnd());
    256   replacements_->emplace(*result.SourceManager, release_range,
    257                          arg_is_rvalue ? "" : ")");
    258 
    259   if (arg_is_rvalue)
    260     return;
    261 
    262   // Insert `std::move(' for non-rvalue expressions.
    263   clang::CharSourceRange insertion_range = clang::CharSourceRange::getCharRange(
    264       object_expr->getLocStart(), object_expr->getLocStart());
    265   replacements_->emplace(*result.SourceManager, insertion_range, "std::move(");
    266 }
    267 
    268 ListValueRewriter::AppendRawPtrCallback::AppendRawPtrCallback(
    269     std::set<clang::tooling::Replacement>* replacements)
    270     : replacements_(replacements) {}
    271 
    272 void ListValueRewriter::AppendRawPtrCallback::run(
    273     const MatchFinder::MatchResult& result) {
    274   auto* var_decl = result.Nodes.getNodeAs<clang::VarDecl>("varDecl");
    275   // As an optimization, skip processing if it's already been visited, since
    276   // this match callback walks the entire function body.
    277   if (visited_.find(var_decl) != visited_.end())
    278     return;
    279   visited_.insert(var_decl);
    280   auto* function_context = var_decl->getParentFunctionOrMethod();
    281   assert(function_context && "local var not in function context?!");
    282   auto* function_decl = clang::cast<clang::FunctionDecl>(function_context);
    283 
    284   auto* type_source_info = var_decl->getTypeSourceInfo();
    285   assert(type_source_info && "no type source info for VarDecl?!");
    286   // Don't bother trying to handle qualifiers.
    287   clang::QualType qual_type = var_decl->getType();
    288   if (qual_type.hasQualifiers()) {
    289     return;
    290   }
    291 
    292   CollectDeclRefExprVisitor visitor(result.SourceManager, result.Context,
    293                                     var_decl, function_decl);
    294   if (!visitor.TraverseStmt(function_decl->getBody()))
    295     return;
    296 
    297   // Rewrite the variable type to use std::unique_ptr.
    298   clang::CharSourceRange type_range = clang::CharSourceRange::getTokenRange(
    299       type_source_info->getTypeLoc().getSourceRange());
    300   std::string replacement_type = "std::unique_ptr<";
    301   while (true) {
    302     const clang::Type* type = qual_type.getTypePtr();
    303     if (auto* auto_type = type->getAs<clang::AutoType>()) {
    304       if (!auto_type->isDeduced()) {
    305         // If an AutoType isn't deduced, the rewriter can't do anything.
    306         return;
    307       }
    308       qual_type = auto_type->getDeducedType();
    309     } else if (auto* pointer_type = type->getAs<clang::PointerType>()) {
    310       qual_type = pointer_type->getPointeeType();
    311     } else {
    312       break;
    313     }
    314   }
    315   replacement_type += qual_type.getAsString();
    316   replacement_type += ">";
    317   replacements_->emplace(*result.SourceManager, type_range, replacement_type);
    318 
    319   // Initialized with `='
    320   if (var_decl->hasInit() &&
    321       var_decl->getInitStyle() == clang::VarDecl::CInit) {
    322     clang::SourceLocation name_end = clang::Lexer::getLocForEndOfToken(
    323         var_decl->getLocation(), 0, *result.SourceManager,
    324         result.Context->getLangOpts());
    325     clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
    326         name_end, var_decl->getInit()->getLocStart());
    327     replacements_->emplace(*result.SourceManager, range, "(");
    328 
    329     clang::SourceLocation init_end = clang::Lexer::getLocForEndOfToken(
    330         var_decl->getInit()->getLocEnd(), 0, *result.SourceManager,
    331         result.Context->getLangOpts());
    332     replacements_->emplace(*result.SourceManager, init_end, 0, ")");
    333   }
    334 
    335   // Also append the collected replacements from visiting the DeclRefExprs.
    336   replacements_->insert(visitor.replacements().begin(),
    337                         visitor.replacements().end());
    338 }
    339 
    340 ListValueRewriter::ListValueRewriter(
    341     std::set<clang::tooling::Replacement>* replacements)
    342     : append_boolean_callback_(replacements),
    343       append_integer_callback_(replacements),
    344       append_double_callback_(replacements),
    345       append_string_callback_(replacements),
    346       append_released_unique_ptr_callback_(replacements),
    347       append_raw_ptr_callback_(replacements) {}
    348 
    349 void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) {
    350   auto is_list_append = cxxMemberCallExpr(
    351       callee(cxxMethodDecl(hasName("::base::ListValue::Append"))),
    352       argumentCountIs(1));
    353 
    354   // base::ListValue::Append(new base::FundamentalValue(bool))
    355   //     => base::ListValue::AppendBoolean()
    356   match_finder->addMatcher(
    357       id("callExpr",
    358          cxxMemberCallExpr(
    359              is_list_append,
    360              hasArgument(
    361                  0, ignoringParenImpCasts(id(
    362                         "newExpr",
    363                         cxxNewExpr(has(cxxConstructExpr(
    364                             hasDeclaration(cxxMethodDecl(hasName(
    365                                 "::base::FundamentalValue::FundamentalValue"))),
    366                             argumentCountIs(1),
    367                             hasArgument(
    368                                 0, id("argExpr",
    369                                       expr(hasType(booleanType())))))))))))),
    370       &append_boolean_callback_);
    371 
    372   // base::ListValue::Append(new base::FundamentalValue(int))
    373   //     => base::ListValue::AppendInteger()
    374   match_finder->addMatcher(
    375       id("callExpr",
    376          cxxMemberCallExpr(
    377              is_list_append,
    378              hasArgument(
    379                  0,
    380                  ignoringParenImpCasts(id(
    381                      "newExpr",
    382                      cxxNewExpr(has(cxxConstructExpr(
    383                          hasDeclaration(cxxMethodDecl(hasName(
    384                              "::base::FundamentalValue::FundamentalValue"))),
    385                          argumentCountIs(1),
    386                          hasArgument(0, id("argExpr",
    387                                            expr(hasType(isInteger()),
    388                                                 unless(hasType(
    389                                                     booleanType()))))))))))))),
    390       &append_integer_callback_);
    391 
    392   // base::ListValue::Append(new base::FundamentalValue(double))
    393   //     => base::ListValue::AppendDouble()
    394   match_finder->addMatcher(
    395       id("callExpr",
    396          cxxMemberCallExpr(
    397              is_list_append,
    398              hasArgument(
    399                  0, ignoringParenImpCasts(id(
    400                         "newExpr",
    401                         cxxNewExpr(has(cxxConstructExpr(
    402                             hasDeclaration(cxxMethodDecl(hasName(
    403                                 "::base::FundamentalValue::FundamentalValue"))),
    404                             argumentCountIs(1),
    405                             hasArgument(
    406                                 0, id("argExpr",
    407                                       expr(hasType(
    408                                           realFloatingPointType())))))))))))),
    409       &append_double_callback_);
    410 
    411   // base::ListValue::Append(new base::StringValue(...))
    412   //     => base::ListValue::AppendString()
    413   match_finder->addMatcher(
    414       id("callExpr",
    415          cxxMemberCallExpr(
    416              is_list_append,
    417              hasArgument(
    418                  0, ignoringParenImpCasts(id(
    419                         "newExpr",
    420                         cxxNewExpr(has(cxxConstructExpr(
    421                             hasDeclaration(cxxMethodDecl(
    422                                 hasName("::base::StringValue::StringValue"))),
    423                             argumentCountIs(1),
    424                             hasArgument(0, id("argExpr", expr())))))))))),
    425       &append_string_callback_);
    426 
    427   auto is_unique_ptr_release =
    428       allOf(callee(cxxMethodDecl(
    429                 hasName("release"),
    430                 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"))))),
    431             argumentCountIs(0));
    432 
    433   // base::ListValue::Append(ReturnsUniquePtr().release())
    434   //     => base::ListValue::Append(ReturnsUniquePtr())
    435   //   or
    436   // base::ListValue::Append(unique_ptr_var.release())
    437   //     => base::ListValue::Append(std::move(unique_ptr_var))
    438   match_finder->addMatcher(
    439       cxxMemberCallExpr(
    440           is_list_append,
    441           hasArgument(
    442               0, ignoringParenImpCasts(
    443                      id("memberCall",
    444                         cxxMemberCallExpr(has(id("memberExpr", memberExpr())),
    445                                           is_unique_ptr_release,
    446                                           on(id("objectExpr", expr()))))))),
    447       &append_released_unique_ptr_callback_);
    448 
    449   // Simple versions of the following pattern. Note the callback itself does
    450   // much of the filtering (to detect use-after-move, things that aren't
    451   // assigned the result of a new expression, etc).
    452   //
    453   // base::ListValue* this_list = new base::ListValue;
    454   // this_list->AppendInteger(1);
    455   // that_list->Append(this_list);
    456   //
    457   // will be rewritten to
    458   //
    459   // std::unique_ptr<base::ListValue> this_list(new base::ListValue);
    460   // this_list->AppendInteger(1);
    461   // that_list->Append(std::move(this_list);
    462   match_finder->addMatcher(
    463       cxxMemberCallExpr(
    464           is_list_append,
    465           hasArgument(
    466               0,
    467               ignoringParenImpCasts(id(
    468                   "declRefExpr",
    469                   declRefExpr(to(id(
    470                       "varDecl",
    471                       varDecl(
    472                           hasLocalStorage(),
    473                           anyOf(hasInitializer(
    474                                     // Note this won't match C++11 uniform
    475                                     // initialization syntax, since the
    476                                     // CXXNewExpr is wrapped in an
    477                                     // InitListExpr in that case.
    478                                     ignoringParenImpCasts(cxxNewExpr())),
    479                                 unless(hasInitializer(expr()))),
    480                           unless(parmVarDecl()))))))))),
    481       &append_raw_ptr_callback_);
    482 }
    483