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