OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ListValueRewriter.h" | 5 #include "ListValueRewriter.h" |
6 | 6 |
7 #include <assert.h> | 7 #include <assert.h> |
8 #include <algorithm> | 8 #include <algorithm> |
9 | 9 |
10 #include "clang/AST/ASTContext.h" | 10 #include "clang/AST/ASTContext.h" |
| 11 #include "clang/AST/ParentMap.h" |
| 12 #include "clang/AST/RecursiveASTVisitor.h" |
11 #include "clang/ASTMatchers/ASTMatchFinder.h" | 13 #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 #include "clang/ASTMatchers/ASTMatchers.h" | 14 #include "clang/ASTMatchers/ASTMatchers.h" |
13 #include "clang/ASTMatchers/ASTMatchersMacros.h" | 15 #include "clang/ASTMatchers/ASTMatchersMacros.h" |
14 #include "clang/Basic/CharInfo.h" | 16 #include "clang/Basic/CharInfo.h" |
15 #include "clang/Basic/SourceManager.h" | 17 #include "clang/Basic/SourceManager.h" |
16 #include "clang/Frontend/FrontendActions.h" | 18 #include "clang/Frontend/FrontendActions.h" |
17 #include "clang/Lex/Lexer.h" | 19 #include "clang/Lex/Lexer.h" |
18 #include "clang/Tooling/Refactoring.h" | 20 #include "clang/Tooling/Refactoring.h" |
19 #include "llvm/ADT/STLExtras.h" | 21 #include "llvm/ADT/STLExtras.h" |
20 | 22 |
21 using namespace clang::ast_matchers; | 23 using namespace clang::ast_matchers; |
22 using clang::tooling::Replacement; | 24 using clang::tooling::Replacement; |
23 using clang::tooling::Replacements; | 25 using clang::tooling::Replacements; |
24 using llvm::StringRef; | 26 using llvm::StringRef; |
25 | 27 |
| 28 namespace { |
| 29 |
| 30 // Helper class for AppendRawCallback to visit each DeclRefExpr for a given |
| 31 // VarDecl. If it finds a DeclRefExpr it can't figure out how to rewrite, the |
| 32 // traversal will be terminated early. |
| 33 class CollectDeclRefExprVisitor |
| 34 : public clang::RecursiveASTVisitor<CollectDeclRefExprVisitor> { |
| 35 public: |
| 36 CollectDeclRefExprVisitor(clang::SourceManager* source_manager, |
| 37 clang::ASTContext* ast_context, |
| 38 const clang::VarDecl* decl, |
| 39 const clang::FunctionDecl* containing_function) |
| 40 : source_manager_(source_manager), |
| 41 ast_context_(ast_context), |
| 42 decl_(decl), |
| 43 is_valid_(decl->hasInit()), |
| 44 map_(containing_function->getBody()) {} |
| 45 |
| 46 // RecursiveASTVisitor: |
| 47 bool VisitDeclRefExpr(const clang::DeclRefExpr* expr) { |
| 48 if (expr->getDecl() != decl_) |
| 49 return true; |
| 50 |
| 51 const clang::Stmt* stmt = expr; |
| 52 while (stmt) { |
| 53 // TODO(dcheng): Add a const version of getParentIgnoreParenImpCasts. |
| 54 stmt = map_.getParentIgnoreParenImpCasts(const_cast<clang::Stmt*>(stmt)); |
| 55 |
| 56 if (clang::isa<clang::MemberExpr>(stmt)) { |
| 57 // Member expressions need no special rewriting since std::unique_ptr |
| 58 // overloads `.' and `->'. |
| 59 return is_valid_; |
| 60 } else if (auto* member_call_expr = |
| 61 clang::dyn_cast<clang::CXXMemberCallExpr>(stmt)) { |
| 62 return HandleMemberCallExpr(member_call_expr, expr); |
| 63 } else if (auto* binary_op = |
| 64 clang::dyn_cast<clang::BinaryOperator>(stmt)) { |
| 65 return HandleBinaryOp(binary_op); |
| 66 } else { |
| 67 // Can't handle this so cancel the rewrite. |
| 68 stmt->dump(); |
| 69 return false; |
| 70 } |
| 71 } |
| 72 |
| 73 assert(false); |
| 74 return false; |
| 75 } |
| 76 |
| 77 const Replacements& replacements() const { return replacements_; } |
| 78 |
| 79 private: |
| 80 bool HandleMemberCallExpr(const clang::CXXMemberCallExpr* member_call_expr, |
| 81 const clang::DeclRefExpr* decl_ref_expr) { |
| 82 // If this isn't a ListValue::Append() call, cancel the rewrite: it |
| 83 // will require manual inspection to determine if it's an ownership |
| 84 // transferring call or not. |
| 85 auto* method_decl = member_call_expr->getMethodDecl(); |
| 86 if (method_decl->getQualifiedNameAsString() != "base::ListValue::Append") |
| 87 return false; |
| 88 // Use-after-move is also a fatal error. |
| 89 if (!is_valid_) |
| 90 return false; |
| 91 |
| 92 is_valid_ = false; |
| 93 |
| 94 // Surround the DeclRefExpr with std::move(). |
| 95 replacements_.emplace(*source_manager_, decl_ref_expr->getLocStart(), 0, |
| 96 "std::move("); |
| 97 |
| 98 clang::SourceLocation end = clang::Lexer::getLocForEndOfToken( |
| 99 decl_ref_expr->getLocEnd(), 0, *source_manager_, |
| 100 ast_context_->getLangOpts()); |
| 101 replacements_.emplace(*source_manager_, end, 0, ")"); |
| 102 return true; |
| 103 } |
| 104 |
| 105 bool HandleBinaryOp(const clang::BinaryOperator* op) { |
| 106 if (op->isRelationalOp() || op->isEqualityOp() || op->isLogicalOp()) { |
| 107 // Supported binary operations for which no rewrites need to be done. |
| 108 return is_valid_; |
| 109 } |
| 110 if (!op->isAssignmentOp()) { |
| 111 // Pointer arithmetic or something else clever. Just cancel the rewrite. |
| 112 return false; |
| 113 } |
| 114 if (op->isCompoundAssignmentOp()) { |
| 115 // +=, -=, etc. Give up and cancel the rewrite. |
| 116 return false; |
| 117 } |
| 118 |
| 119 const clang::Expr* rhs = op->getRHS()->IgnoreParenImpCasts(); |
| 120 const clang::CXXNewExpr* new_expr = clang::dyn_cast<clang::CXXNewExpr>(rhs); |
| 121 if (!new_expr) { |
| 122 // The variable isn't being assigned the result of a new operation. Just |
| 123 // cancel the rewrite. |
| 124 return false; |
| 125 } |
| 126 |
| 127 is_valid_ = true; |
| 128 |
| 129 // Rewrite the assignment operation to use std::unique_ptr::reset(). |
| 130 clang::CharSourceRange range = clang::CharSourceRange::getCharRange( |
| 131 op->getOperatorLoc(), op->getRHS()->getLocStart()); |
| 132 replacements_.emplace(*source_manager_, range, ".reset("); |
| 133 |
| 134 clang::SourceLocation expr_end = clang::Lexer::getLocForEndOfToken( |
| 135 op->getLocEnd(), 0, *source_manager_, ast_context_->getLangOpts()); |
| 136 replacements_.emplace(*source_manager_, expr_end, 0, ")"); |
| 137 return true; |
| 138 } |
| 139 |
| 140 clang::SourceManager* const source_manager_; |
| 141 clang::ASTContext* const ast_context_; |
| 142 const clang::VarDecl* const decl_; |
| 143 // Tracks the state of |decl_| during the traversal. |decl_| becomes valid |
| 144 // upon initialization/assignment and becomes invalid when passed as an |
| 145 // argument to base::ListValue::Append(base::Value*). |
| 146 bool is_valid_; |
| 147 clang::ParentMap map_; |
| 148 Replacements replacements_; |
| 149 }; |
| 150 |
| 151 } // namespace |
| 152 |
26 ListValueRewriter::AppendCallback::AppendCallback(Replacements* replacements) | 153 ListValueRewriter::AppendCallback::AppendCallback(Replacements* replacements) |
27 : replacements_(replacements) {} | 154 : replacements_(replacements) {} |
28 | 155 |
29 void ListValueRewriter::AppendCallback::run( | 156 void ListValueRewriter::AppendCallback::run( |
30 const MatchFinder::MatchResult& result) { | 157 const MatchFinder::MatchResult& result) { |
31 // Delete `new base::*Value(' and `)'. | 158 // Delete `new base::*Value(' and `)'. |
32 auto* newExpr = result.Nodes.getNodeAs<clang::CXXNewExpr>("newExpr"); | 159 auto* newExpr = result.Nodes.getNodeAs<clang::CXXNewExpr>("newExpr"); |
33 auto* argExpr = result.Nodes.getNodeAs<clang::Expr>("argExpr"); | 160 auto* argExpr = result.Nodes.getNodeAs<clang::Expr>("argExpr"); |
34 | 161 |
35 // Note that for the end loc, we use the expansion loc: the argument might be | 162 // Note that for the end loc, we use the expansion loc: the argument might be |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 | 255 |
129 if (arg_is_rvalue) | 256 if (arg_is_rvalue) |
130 return; | 257 return; |
131 | 258 |
132 // Insert `std::move(' for non-rvalue expressions. | 259 // Insert `std::move(' for non-rvalue expressions. |
133 clang::CharSourceRange insertion_range = clang::CharSourceRange::getCharRange( | 260 clang::CharSourceRange insertion_range = clang::CharSourceRange::getCharRange( |
134 object_expr->getLocStart(), object_expr->getLocStart()); | 261 object_expr->getLocStart(), object_expr->getLocStart()); |
135 replacements_->emplace(*result.SourceManager, insertion_range, "std::move("); | 262 replacements_->emplace(*result.SourceManager, insertion_range, "std::move("); |
136 } | 263 } |
137 | 264 |
| 265 ListValueRewriter::AppendRawPtrCallback::AppendRawPtrCallback( |
| 266 Replacements* replacements) |
| 267 : replacements_(replacements) {} |
| 268 |
| 269 void ListValueRewriter::AppendRawPtrCallback::run( |
| 270 const MatchFinder::MatchResult& result) { |
| 271 auto* var_decl = result.Nodes.getNodeAs<clang::VarDecl>("varDecl"); |
| 272 // As an optimization, skip processing if it's already been visited, since |
| 273 // this match callback walks the entire function body. |
| 274 if (visited_.find(var_decl) != visited_.end()) |
| 275 return; |
| 276 visited_.insert(var_decl); |
| 277 auto* function_context = var_decl->getParentFunctionOrMethod(); |
| 278 assert(function_context && "local var not in function context?!"); |
| 279 auto* function_decl = clang::cast<clang::FunctionDecl>(function_context); |
| 280 |
| 281 auto* type_source_info = var_decl->getTypeSourceInfo(); |
| 282 assert(type_source_info && "no type source info for VarDecl?!"); |
| 283 // Don't bother trying to handle qualifiers. |
| 284 clang::QualType qual_type = var_decl->getType(); |
| 285 if (qual_type.hasQualifiers()) { |
| 286 return; |
| 287 } |
| 288 |
| 289 CollectDeclRefExprVisitor visitor(result.SourceManager, result.Context, |
| 290 var_decl, function_decl); |
| 291 if (!visitor.TraverseStmt(function_decl->getBody())) |
| 292 return; |
| 293 |
| 294 // Rewrite the variable type to use std::unique_ptr. |
| 295 clang::CharSourceRange type_range = clang::CharSourceRange::getTokenRange( |
| 296 type_source_info->getTypeLoc().getSourceRange()); |
| 297 std::string replacement_type = "std::unique_ptr<"; |
| 298 while (true) { |
| 299 const clang::Type* type = qual_type.getTypePtr(); |
| 300 if (auto* auto_type = type->getAs<clang::AutoType>()) { |
| 301 if (!auto_type->isDeduced()) { |
| 302 // If an AutoType isn't deduced, the rewriter can't do anything. |
| 303 return; |
| 304 } |
| 305 qual_type = auto_type->getDeducedType(); |
| 306 } else if (auto* pointer_type = type->getAs<clang::PointerType>()) { |
| 307 qual_type = pointer_type->getPointeeType(); |
| 308 } else { |
| 309 break; |
| 310 } |
| 311 } |
| 312 replacement_type += qual_type.getAsString(); |
| 313 replacement_type += ">"; |
| 314 replacements_->emplace(*result.SourceManager, type_range, replacement_type); |
| 315 |
| 316 // Initialized with `=' |
| 317 if (var_decl->hasInit() && |
| 318 var_decl->getInitStyle() == clang::VarDecl::CInit) { |
| 319 clang::SourceLocation name_end = clang::Lexer::getLocForEndOfToken( |
| 320 var_decl->getLocation(), 0, *result.SourceManager, |
| 321 result.Context->getLangOpts()); |
| 322 clang::CharSourceRange range = clang::CharSourceRange::getCharRange( |
| 323 name_end, var_decl->getInit()->getLocStart()); |
| 324 replacements_->emplace(*result.SourceManager, range, "("); |
| 325 |
| 326 clang::SourceLocation init_end = clang::Lexer::getLocForEndOfToken( |
| 327 var_decl->getInit()->getLocEnd(), 0, *result.SourceManager, |
| 328 result.Context->getLangOpts()); |
| 329 replacements_->emplace(*result.SourceManager, init_end, 0, ")"); |
| 330 } |
| 331 |
| 332 // Also append the collected replacements from visiting the DeclRefExprs. |
| 333 replacements_->insert(visitor.replacements().begin(), |
| 334 visitor.replacements().end()); |
| 335 } |
| 336 |
138 ListValueRewriter::ListValueRewriter(Replacements* replacements) | 337 ListValueRewriter::ListValueRewriter(Replacements* replacements) |
139 : append_boolean_callback_(replacements), | 338 : append_boolean_callback_(replacements), |
140 append_integer_callback_(replacements), | 339 append_integer_callback_(replacements), |
141 append_double_callback_(replacements), | 340 append_double_callback_(replacements), |
142 append_string_callback_(replacements), | 341 append_string_callback_(replacements), |
143 append_released_unique_ptr_callback_(replacements) {} | 342 append_released_unique_ptr_callback_(replacements), |
| 343 append_raw_ptr_callback_(replacements) {} |
144 | 344 |
145 void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) { | 345 void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) { |
146 auto is_list_append = | 346 auto is_list_append = cxxMemberCallExpr( |
147 allOf(callee(cxxMethodDecl(hasName("::base::ListValue::Append"))), | 347 callee(cxxMethodDecl(hasName("::base::ListValue::Append"))), |
148 argumentCountIs(1)); | 348 argumentCountIs(1)); |
149 | 349 |
150 // base::ListValue::Append(new base::FundamentalValue(bool)) | 350 // base::ListValue::Append(new base::FundamentalValue(bool)) |
151 // => base::ListValue::AppendBoolean() | 351 // => base::ListValue::AppendBoolean() |
152 match_finder->addMatcher( | 352 match_finder->addMatcher( |
153 id("callExpr", | 353 id("callExpr", |
154 cxxMemberCallExpr( | 354 cxxMemberCallExpr( |
155 is_list_append, | 355 is_list_append, |
156 hasArgument( | 356 hasArgument( |
157 0, ignoringParenImpCasts(id( | 357 0, ignoringParenImpCasts(id( |
158 "newExpr", | 358 "newExpr", |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
234 match_finder->addMatcher( | 434 match_finder->addMatcher( |
235 cxxMemberCallExpr( | 435 cxxMemberCallExpr( |
236 is_list_append, | 436 is_list_append, |
237 hasArgument( | 437 hasArgument( |
238 0, ignoringParenImpCasts( | 438 0, ignoringParenImpCasts( |
239 id("memberCall", | 439 id("memberCall", |
240 cxxMemberCallExpr(has(id("memberExpr", memberExpr())), | 440 cxxMemberCallExpr(has(id("memberExpr", memberExpr())), |
241 is_unique_ptr_release, | 441 is_unique_ptr_release, |
242 on(id("objectExpr", expr()))))))), | 442 on(id("objectExpr", expr()))))))), |
243 &append_released_unique_ptr_callback_); | 443 &append_released_unique_ptr_callback_); |
| 444 |
| 445 // Simple versions of the following pattern. Note the callback itself does |
| 446 // much of the filtering (to detect use-after-move, things that aren't |
| 447 // assigned the result of a new expression, etc). |
| 448 // |
| 449 // base::ListValue* this_list = new base::ListValue; |
| 450 // this_list->AppendInteger(1); |
| 451 // that_list->Append(this_list); |
| 452 // |
| 453 // will be rewritten to |
| 454 // |
| 455 // std::unique_ptr<base::ListValue> this_list(new base::ListValue); |
| 456 // this_list->AppendInteger(1); |
| 457 // that_list->Append(std::move(this_list); |
| 458 match_finder->addMatcher( |
| 459 cxxMemberCallExpr( |
| 460 is_list_append, |
| 461 hasArgument( |
| 462 0, |
| 463 ignoringParenImpCasts(id( |
| 464 "declRefExpr", |
| 465 declRefExpr(to(id( |
| 466 "varDecl", |
| 467 varDecl( |
| 468 hasLocalStorage(), |
| 469 anyOf(hasInitializer( |
| 470 // Note this won't match C++11 uniform |
| 471 // initialization syntax, since the |
| 472 // CXXNewExpr is wrapped in an |
| 473 // InitListExpr in that case. |
| 474 ignoringParenImpCasts(cxxNewExpr())), |
| 475 unless(hasInitializer(expr()))), |
| 476 unless(parmVarDecl()))))))))), |
| 477 &append_raw_ptr_callback_); |
244 } | 478 } |
OLD | NEW |