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 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 } |
OLD | NEW |