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

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: Handles auto as well now 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
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 130
129 if (arg_is_rvalue) 131 if (arg_is_rvalue)
130 return; 132 return;
131 133
132 // Insert `std::move(' for non-rvalue expressions. 134 // Insert `std::move(' for non-rvalue expressions.
133 clang::CharSourceRange insertion_range = clang::CharSourceRange::getCharRange( 135 clang::CharSourceRange insertion_range = clang::CharSourceRange::getCharRange(
134 object_expr->getLocStart(), object_expr->getLocStart()); 136 object_expr->getLocStart(), object_expr->getLocStart());
135 replacements_->emplace(*result.SourceManager, insertion_range, "std::move("); 137 replacements_->emplace(*result.SourceManager, insertion_range, "std::move(");
136 } 138 }
137 139
140 ListValueRewriter::AppendRawPtrCallback::AppendRawPtrCallback(
141 Replacements* replacements)
142 : replacements_(replacements) {}
143
144 void ListValueRewriter::AppendRawPtrCallback::run(
145 const MatchFinder::MatchResult& result) {
146 auto* var_decl = result.Nodes.getNodeAs<clang::VarDecl>("varDecl");
147 // As an optimization, skip processing if it's already been visited, since
148 // this match callback walks the entire function body.
149 if (visited_.find(var_decl) != visited_.end())
150 return;
151 visited_.insert(var_decl);
152 auto* function_context = var_decl->getParentFunctionOrMethod();
153 assert(function_context && "local var not in function context?!");
154 auto* function_decl = clang::cast<clang::FunctionDecl>(function_context);
155
156 auto* type_source_info = var_decl->getTypeSourceInfo();
157 assert(type_source_info && "no type source info for VarDecl?!");
158 // Don't bother trying to handle qualifiers.
159 clang::QualType qual_type = var_decl->getType();
160 if (qual_type.hasQualifiers()) {
161 return;
162 }
163
164 // Helper class to visit each DeclRefExpr for this VarDecl. If it finds a
165 // DeclRefExpr it can't figure out how to rewrite, the traversal will be
166 // terminated early.
167 class CollectDeclRefExprVisitor
danakj 2016/06/10 21:59:55 move this out of the method i think
dcheng 2016/06/10 22:39:43 Done.
168 : public clang::RecursiveASTVisitor<CollectDeclRefExprVisitor> {
169 public:
170 CollectDeclRefExprVisitor(clang::SourceManager* source_manager,
171 clang::ASTContext* ast_context,
172 const clang::VarDecl* decl,
173 const clang::FunctionDecl* containing_function)
174 : source_manager_(source_manager),
175 ast_context_(ast_context),
176 decl_(decl),
177 is_valid_(decl->hasInit()),
178 map_(containing_function->getBody()) {}
179
180 bool VisitDeclRefExpr(const clang::DeclRefExpr* expr) {
181 if (expr->getDecl() != decl_)
182 return true;
183
184 const clang::Stmt* stmt = expr;
185 while (stmt) {
186 // TODO(dcheng): Add a const version of getParentIgnoreParenImpCasts.
187 stmt =
188 map_.getParentIgnoreParenImpCasts(const_cast<clang::Stmt*>(stmt));
189
190 if (clang::isa<clang::MemberExpr>(stmt)) {
191 // Member expressions need no special rewriting since std::unique_ptr
192 // overloads `.' and `->'.
193 return is_valid_;
194 } else if (auto* member_call_expr =
195 clang::dyn_cast<clang::CXXMemberCallExpr>(stmt)) {
196 return HandleMemberCallExpr(member_call_expr, expr);
197 } else if (auto* binary_op =
198 clang::dyn_cast<clang::BinaryOperator>(stmt)) {
199 return HandleBinaryOp(binary_op);
200 } else {
201 // Can't handle this so cancel the rewrite.
202 stmt->dump();
203 return false;
204 }
205 }
206
207 assert(false);
208 return false;
209 }
210
211 const Replacements& replacements() const { return replacements_; }
212
213 private:
214 bool HandleMemberCallExpr(const clang::CXXMemberCallExpr* member_call_expr,
215 const clang::DeclRefExpr* decl_ref_expr) {
216 // If this isn't a ListValue::Append() call, cancel the rewrite: it
217 // will require manual inspection to determine if it's an ownership
218 // transferring call or not.
219 auto* method_decl = member_call_expr->getMethodDecl();
220 if (method_decl->getQualifiedNameAsString() != "base::ListValue::Append")
221 return false;
222 // Use-after-move is also a fatal error.
223 if (!is_valid_)
224 return false;
225
226 is_valid_ = false;
227
228 // Surround the DeclRefExpr with std::move().
229 replacements_.emplace(*source_manager_, decl_ref_expr->getLocStart(), 0,
230 "std::move(");
231
232 clang::SourceLocation end = clang::Lexer::getLocForEndOfToken(
233 decl_ref_expr->getLocEnd(), 0, *source_manager_,
234 ast_context_->getLangOpts());
235 replacements_.emplace(*source_manager_, end, 0, ")");
236 return true;
237 }
238
239 bool HandleBinaryOp(const clang::BinaryOperator* op) {
240 if (op->isRelationalOp() || op->isEqualityOp() || op->isLogicalOp()) {
241 // Supported binary operations for which no rewrites need to be done.
242 return is_valid_;
243 }
244 if (!op->isAssignmentOp()) {
245 // Pointer arithmetic or something else clever. Just cancel the rewrite.
246 return false;
247 }
248 if (op->isCompoundAssignmentOp()) {
249 // +=, -=, etc. Give up and cancel the rewrite.
250 return false;
251 }
252
253 const clang::Expr* rhs = op->getRHS()->IgnoreParenImpCasts();
254 const clang::CXXNewExpr* new_expr =
255 clang::dyn_cast<clang::CXXNewExpr>(rhs);
256 if (!new_expr) {
257 // The variable isn't being assigned the result of a new operation. Just
258 // cancel the rewrite.
259 return false;
260 }
261
262 is_valid_ = true;
263
264 // Rewrite the assignment operation to use std::unique_ptr::reset().
265 clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
266 op->getOperatorLoc(), op->getRHS()->getLocStart());
267 replacements_.emplace(*source_manager_, range, ".reset(");
268
269 clang::SourceLocation expr_end = clang::Lexer::getLocForEndOfToken(
270 op->getLocEnd(), 0, *source_manager_, ast_context_->getLangOpts());
271 replacements_.emplace(*source_manager_, expr_end, 0, ")");
272 return true;
273 }
274
275 clang::SourceManager* const source_manager_;
276 clang::ASTContext* const ast_context_;
277 const clang::VarDecl* const decl_;
278 // Tracks the state of |decl_| during the traversal. |decl_| becomes valid
279 // upon initialization/assignment and becomes invalid when passed as an
280 // argument to base::ListValue::Append(base::Value*).
281 bool is_valid_;
282 clang::ParentMap map_;
283 Replacements replacements_;
284 };
285
286 CollectDeclRefExprVisitor visitor(result.SourceManager, result.Context,
287 var_decl, function_decl);
288 if (!visitor.TraverseStmt(function_decl->getBody()))
289 return;
290
291 // Rewrite the variable type to use std::unique_ptr.
292 clang::CharSourceRange type_range = clang::CharSourceRange::getTokenRange(
293 type_source_info->getTypeLoc().getSourceRange());
294 std::string replacement_type = "std::unique_ptr<";
295 while (true) {
296 const clang::Type* type = qual_type.getTypePtr();
297 if (auto* auto_type = type->getAs<clang::AutoType>()) {
298 if (!auto_type->isDeduced()) {
299 // If an AutoType isn't deduced, the rewriter can't do anything.
300 return;
301 }
302 qual_type = auto_type->getDeducedType();
303 } else if (auto* pointer_type = type->getAs<clang::PointerType>()) {
304 qual_type = pointer_type->getPointeeType();
305 } else {
306 break;
307 }
308 }
309 replacement_type += qual_type.getAsString();
310 replacement_type += ">";
311 replacements_->emplace(*result.SourceManager, type_range, replacement_type);
312
313 // Initialized with `='
314 if (var_decl->hasInit() &&
315 var_decl->getInitStyle() == clang::VarDecl::CInit) {
316 clang::SourceLocation name_end = clang::Lexer::getLocForEndOfToken(
317 var_decl->getLocation(), 0, *result.SourceManager,
318 result.Context->getLangOpts());
319 clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
320 name_end, var_decl->getInit()->getLocStart());
321 replacements_->emplace(*result.SourceManager, range, "(");
danakj 2016/06/10 21:59:55 Can you skip the () if the range is empty (there w
dcheng 2016/06/10 22:39:43 I could, but that makes this already complicated l
322
323 clang::SourceLocation init_end = clang::Lexer::getLocForEndOfToken(
324 var_decl->getInit()->getLocEnd(), 0, *result.SourceManager,
325 result.Context->getLangOpts());
326 replacements_->emplace(*result.SourceManager, init_end, 0, ")");
327 }
328
329 // Also append the collected replacements from visiting the DeclRefExprs.
330 replacements_->insert(visitor.replacements().begin(),
331 visitor.replacements().end());
332 }
333
138 ListValueRewriter::ListValueRewriter(Replacements* replacements) 334 ListValueRewriter::ListValueRewriter(Replacements* replacements)
139 : append_boolean_callback_(replacements), 335 : append_boolean_callback_(replacements),
140 append_integer_callback_(replacements), 336 append_integer_callback_(replacements),
141 append_double_callback_(replacements), 337 append_double_callback_(replacements),
142 append_string_callback_(replacements), 338 append_string_callback_(replacements),
143 append_released_unique_ptr_callback_(replacements) {} 339 append_released_unique_ptr_callback_(replacements),
340 append_raw_ptr_callback_(replacements) {}
144 341
145 void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) { 342 void ListValueRewriter::RegisterMatchers(MatchFinder* match_finder) {
146 auto is_list_append = 343 auto is_list_append = cxxMemberCallExpr(
147 allOf(callee(cxxMethodDecl(hasName("::base::ListValue::Append"))), 344 callee(cxxMethodDecl(hasName("::base::ListValue::Append"))),
148 argumentCountIs(1)); 345 argumentCountIs(1));
149 346
150 // base::ListValue::Append(new base::FundamentalValue(bool)) 347 // base::ListValue::Append(new base::FundamentalValue(bool))
151 // => base::ListValue::AppendBoolean() 348 // => base::ListValue::AppendBoolean()
152 match_finder->addMatcher( 349 match_finder->addMatcher(
153 id("callExpr", 350 id("callExpr",
154 cxxMemberCallExpr( 351 cxxMemberCallExpr(
155 is_list_append, 352 is_list_append,
156 hasArgument( 353 hasArgument(
157 0, ignoringParenImpCasts(id( 354 0, ignoringParenImpCasts(id(
158 "newExpr", 355 "newExpr",
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 match_finder->addMatcher( 431 match_finder->addMatcher(
235 cxxMemberCallExpr( 432 cxxMemberCallExpr(
236 is_list_append, 433 is_list_append,
237 hasArgument( 434 hasArgument(
238 0, ignoringParenImpCasts( 435 0, ignoringParenImpCasts(
239 id("memberCall", 436 id("memberCall",
240 cxxMemberCallExpr(has(id("memberExpr", memberExpr())), 437 cxxMemberCallExpr(has(id("memberExpr", memberExpr())),
241 is_unique_ptr_release, 438 is_unique_ptr_release,
242 on(id("objectExpr", expr()))))))), 439 on(id("objectExpr", expr()))))))),
243 &append_released_unique_ptr_callback_); 440 &append_released_unique_ptr_callback_);
441
442 // Simple versions of the following pattern. Note the callback itself does
443 // much of the filtering (to detect use-after-move, things that aren't
444 // assigned the result of a new expression, etc).
445 // base::ListValue* this_list = new base::ListValue;
danakj 2016/06/10 21:59:55 Mention what it will turn this code into?
dcheng 2016/06/10 22:39:43 Done.
446 // this_list->AppendInteger(1);
447 // that_list->Append(this_list);
448 match_finder->addMatcher(
449 cxxMemberCallExpr(
450 is_list_append,
451 hasArgument(
452 0, ignoringParenImpCasts(id(
453 "declRefExpr",
454 declRefExpr(to(id(
455 "varDecl",
456 varDecl(hasLocalStorage(),
457 anyOf(hasInitializer(
458 ignoringParenImpCasts(cxxNewExpr())),
459 unless(hasInitializer(expr()))),
460 unless(parmVarDecl()))))))))),
danakj 2016/06/10 21:59:55 This needs more )
461 &append_raw_ptr_callback_);
244 } 462 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698