Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(727)

Side by Side Diff: tools/clang/value_cleanup/ListValueRewriter.cpp

Issue 2056153003: value_cleanup: Rewrite more base::ListValue::Append(base::Value*) calls. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: feedback Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « tools/clang/value_cleanup/ListValueRewriter.h ('k') | tools/clang/value_cleanup/tests/list-value-append-expected.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698