OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 // This implements a Clang tool to rewrite all instances of | 5 // This implements a Clang tool to rewrite all instances of |
6 // scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to | 6 // scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to |
7 // the .get() method. | 7 // the .get() method. |
8 | 8 |
9 #include <assert.h> | 9 #include <assert.h> |
10 #include <algorithm> | 10 #include <algorithm> |
11 #include <memory> | 11 #include <memory> |
12 #include <string> | 12 #include <string> |
13 | 13 |
14 #include "clang/AST/ASTContext.h" | 14 #include "clang/AST/ASTContext.h" |
15 #include "clang/ASTMatchers/ASTMatchers.h" | 15 #include "clang/ASTMatchers/ASTMatchers.h" |
16 #include "clang/ASTMatchers/ASTMatchersMacros.h" | 16 #include "clang/ASTMatchers/ASTMatchersMacros.h" |
17 #include "clang/ASTMatchers/ASTMatchFinder.h" | 17 #include "clang/ASTMatchers/ASTMatchFinder.h" |
18 #include "clang/Basic/SourceManager.h" | 18 #include "clang/Basic/SourceManager.h" |
19 #include "clang/Frontend/FrontendActions.h" | 19 #include "clang/Frontend/FrontendActions.h" |
20 #include "clang/Lex/Lexer.h" | 20 #include "clang/Lex/Lexer.h" |
21 #include "clang/Tooling/CommonOptionsParser.h" | 21 #include "clang/Tooling/CommonOptionsParser.h" |
22 #include "clang/Tooling/Refactoring.h" | 22 #include "clang/Tooling/Refactoring.h" |
23 #include "clang/Tooling/Tooling.h" | 23 #include "clang/Tooling/Tooling.h" |
24 #include "llvm/Support/CommandLine.h" | 24 #include "llvm/Support/CommandLine.h" |
25 #include "llvm/support/TargetSelect.h" | 25 #include "llvm/Support/TargetSelect.h" |
26 | 26 |
27 using namespace clang::ast_matchers; | 27 using namespace clang::ast_matchers; |
28 using clang::tooling::CommonOptionsParser; | 28 using clang::tooling::CommonOptionsParser; |
29 using clang::tooling::Replacement; | 29 using clang::tooling::Replacement; |
30 using clang::tooling::Replacements; | 30 using clang::tooling::Replacements; |
31 using llvm::StringRef; | 31 using llvm::StringRef; |
32 | 32 |
33 namespace clang { | 33 namespace clang { |
34 namespace ast_matchers { | 34 namespace ast_matchers { |
35 | 35 |
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 llvm::InitializeNativeTarget(); | 261 llvm::InitializeNativeTarget(); |
262 llvm::InitializeNativeTargetAsmParser(); | 262 llvm::InitializeNativeTargetAsmParser(); |
263 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); | 263 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); |
264 CommonOptionsParser options(argc, argv, category); | 264 CommonOptionsParser options(argc, argv, category); |
265 clang::tooling::ClangTool tool(options.getCompilations(), | 265 clang::tooling::ClangTool tool(options.getCompilations(), |
266 options.getSourcePathList()); | 266 options.getSourcePathList()); |
267 | 267 |
268 MatchFinder match_finder; | 268 MatchFinder match_finder; |
269 Replacements replacements; | 269 Replacements replacements; |
270 | 270 |
271 auto is_scoped_refptr = recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | 271 auto is_scoped_refptr = cxxRecordDecl(isSameOrDerivedFrom("::scoped_refptr"), |
272 isTemplateInstantiation()); | 272 isTemplateInstantiation()); |
273 | 273 |
274 // Finds all calls to conversion operator member function. This catches calls | 274 // Finds all calls to conversion operator member function. This catches calls |
275 // to "operator T*", "operator Testable", and "operator bool" equally. | 275 // to "operator T*", "operator Testable", and "operator bool" equally. |
276 auto base_matcher = memberCallExpr(thisPointerType(is_scoped_refptr), | 276 auto base_matcher = |
277 callee(conversionDecl()), | 277 cxxMemberCallExpr(thisPointerType(is_scoped_refptr), |
278 on(id("arg", expr()))); | 278 callee(conversionDecl()), on(id("arg", expr()))); |
279 | 279 |
280 // The heuristic for whether or not converting a temporary is 'unsafe'. An | 280 // The heuristic for whether or not converting a temporary is 'unsafe'. An |
281 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to | 281 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to |
282 // another type. The matcher provides an exception for a temporary | 282 // another type. The matcher provides an exception for a temporary |
283 // scoped_refptr that is the result of an operator call. In this case, assume | 283 // scoped_refptr that is the result of an operator call. In this case, assume |
284 // that it's the result of an iterator dereference, and the container itself | 284 // that it's the result of an iterator dereference, and the container itself |
285 // retains the necessary reference, since this is a common idiom to see in | 285 // retains the necessary reference, since this is a common idiom to see in |
286 // loop bodies. | 286 // loop bodies. |
287 auto is_unsafe_temporary_conversion = | 287 auto is_unsafe_temporary_conversion = |
288 on(bindTemporaryExpr(unless(has(operatorCallExpr())))); | 288 on(cxxBindTemporaryExpr(unless(has(cxxOperatorCallExpr())))); |
289 | 289 |
290 // Returning a scoped_refptr<T> as a T* is considered unsafe if either are | 290 // Returning a scoped_refptr<T> as a T* is considered unsafe if either are |
291 // true: | 291 // true: |
292 // - The scoped_refptr<T> is a temporary. | 292 // - The scoped_refptr<T> is a temporary. |
293 // - The scoped_refptr<T> has local lifetime. | 293 // - The scoped_refptr<T> has local lifetime. |
294 auto returned_as_raw_ptr = hasParent( | 294 auto returned_as_raw_ptr = hasParent( |
295 returnStmt(hasAncestor(id("fn", functionDecl(returns(pointerType())))))); | 295 returnStmt(hasAncestor(id("fn", functionDecl(returns(pointerType())))))); |
296 // This matcher intentionally matches more than it should. For example, this | 296 // This matcher intentionally matches more than it should. For example, this |
297 // will match: | 297 // will match: |
298 // scoped_refptr<Foo>& foo = some_other_foo; | 298 // scoped_refptr<Foo>& foo = some_other_foo; |
(...skipping 16 matching lines...) Expand all Loading... |
315 implicitCastExpr(hasImplicitDestinationType(isBoolean())); | 315 implicitCastExpr(hasImplicitDestinationType(isBoolean())); |
316 | 316 |
317 // Avoid converting calls to of "operator Testable" -> "bool" and calls of | 317 // Avoid converting calls to of "operator Testable" -> "bool" and calls of |
318 // "operator T*" -> "bool". | 318 // "operator T*" -> "bool". |
319 auto bool_conversion_matcher = hasParent( | 319 auto bool_conversion_matcher = hasParent( |
320 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); | 320 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); |
321 | 321 |
322 auto is_logging_helper = | 322 auto is_logging_helper = |
323 functionDecl(anyOf(hasName("CheckEQImpl"), hasName("CheckNEImpl"))); | 323 functionDecl(anyOf(hasName("CheckEQImpl"), hasName("CheckNEImpl"))); |
324 auto is_gtest_helper = functionDecl( | 324 auto is_gtest_helper = functionDecl( |
325 anyOf(methodDecl(ofClass(recordDecl(isSameOrDerivedFrom( | 325 anyOf(cxxMethodDecl(ofClass(cxxRecordDecl(isSameOrDerivedFrom( |
326 hasName("::testing::internal::EqHelper")))), | 326 hasName("::testing::internal::EqHelper")))), |
327 hasName("Compare")), | 327 hasName("Compare")), |
328 hasName("::testing::internal::CmpHelperNE"))); | 328 hasName("::testing::internal::CmpHelperNE"))); |
329 auto is_gtest_assertion_result_ctor = constructorDecl(ofClass( | 329 auto is_gtest_assertion_result_ctor = |
330 recordDecl(isSameOrDerivedFrom(hasName("::testing::AssertionResult"))))); | 330 cxxConstructorDecl(ofClass(cxxRecordDecl( |
| 331 isSameOrDerivedFrom(hasName("::testing::AssertionResult"))))); |
331 | 332 |
332 // Find all calls to an operator overload that are 'safe'. | 333 // Find all calls to an operator overload that are 'safe'. |
333 // | 334 // |
334 // All bool conversions will be handled with the Testable trick, but that | 335 // All bool conversions will be handled with the Testable trick, but that |
335 // can only be used once "operator T*" is removed, since otherwise it leaves | 336 // can only be used once "operator T*" is removed, since otherwise it leaves |
336 // the call ambiguous. | 337 // the call ambiguous. |
337 GetRewriterCallback get_callback(&replacements); | 338 GetRewriterCallback get_callback(&replacements); |
338 match_finder.addMatcher( | 339 match_finder.addMatcher( |
339 memberCallExpr( | 340 cxxMemberCallExpr( |
340 base_matcher, | 341 base_matcher, |
341 // Excluded since the conversion may be unsafe. | 342 // Excluded since the conversion may be unsafe. |
342 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return)), | 343 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return)), |
343 // Excluded since the conversion occurs inside a helper function that | 344 // Excluded since the conversion occurs inside a helper function that |
344 // the macro wraps. Letting this callback handle the rewrite would | 345 // the macro wraps. Letting this callback handle the rewrite would |
345 // result in an incorrect replacement that changes the helper function | 346 // result in an incorrect replacement that changes the helper function |
346 // itself. Instead, the right replacement is to rewrite the macro's | 347 // itself. Instead, the right replacement is to rewrite the macro's |
347 // arguments. | 348 // arguments. |
348 unless(hasAncestor(decl(anyOf(is_logging_helper, | 349 unless(hasAncestor(decl(anyOf(is_logging_helper, is_gtest_helper, |
349 is_gtest_helper, | |
350 is_gtest_assertion_result_ctor))))), | 350 is_gtest_assertion_result_ctor))))), |
351 &get_callback); | 351 &get_callback); |
352 | 352 |
353 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. | 353 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. |
354 VarRewriterCallback var_callback(&replacements); | 354 VarRewriterCallback var_callback(&replacements); |
355 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( | 355 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( |
356 has(memberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); | 356 has(cxxMemberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); |
357 match_finder.addMatcher(id("var", | 357 match_finder.addMatcher(id("var", |
358 varDecl(hasInitializer(initialized_with_temporary), | 358 varDecl(hasInitializer(initialized_with_temporary), |
359 hasType(pointerType()))), | 359 hasType(pointerType()))), |
360 &var_callback); | 360 &var_callback); |
361 match_finder.addMatcher( | 361 match_finder.addMatcher( |
362 constructorDecl(forEachConstructorInitializer( | 362 cxxConstructorDecl(forEachConstructorInitializer( |
363 allOf(withInitializer(initialized_with_temporary), | 363 allOf(withInitializer(initialized_with_temporary), |
364 forField(id("var", fieldDecl(hasType(pointerType()))))))), | 364 forField(id("var", fieldDecl(hasType(pointerType()))))))), |
365 &var_callback); | 365 &var_callback); |
366 | 366 |
367 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when | 367 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when |
368 // returning a value. | 368 // returning a value. |
369 FunctionRewriterCallback fn_callback(&replacements); | 369 FunctionRewriterCallback fn_callback(&replacements); |
370 match_finder.addMatcher(memberCallExpr(base_matcher, is_unsafe_return), | 370 match_finder.addMatcher(cxxMemberCallExpr(base_matcher, is_unsafe_return), |
371 &fn_callback); | 371 &fn_callback); |
372 | 372 |
373 // Rewrite logging / gtest expressions that result in an implicit conversion. | 373 // Rewrite logging / gtest expressions that result in an implicit conversion. |
374 // Luckily, the matchers don't need to handle the case where one of the macro | 374 // Luckily, the matchers don't need to handle the case where one of the macro |
375 // arguments is NULL, such as: | 375 // arguments is NULL, such as: |
376 // CHECK_EQ(my_scoped_refptr, NULL) | 376 // CHECK_EQ(my_scoped_refptr, NULL) |
377 // because it simply doesn't compile--since NULL is actually of integral type, | 377 // because it simply doesn't compile--since NULL is actually of integral type, |
378 // this doesn't trigger scoped_refptr<T>'s implicit conversion. Since there is | 378 // this doesn't trigger scoped_refptr<T>'s implicit conversion. Since there is |
379 // no comparison overload for scoped_refptr<T> and int, this fails to compile. | 379 // no comparison overload for scoped_refptr<T> and int, this fails to compile. |
380 MacroRewriterCallback macro_callback(&replacements); | 380 MacroRewriterCallback macro_callback(&replacements); |
(...skipping 19 matching lines...) Expand all Loading... |
400 ¯o_callback); | 400 ¯o_callback); |
401 // ASSERT_TRUE/EXPECT_TRUE helpers. Note that this matcher doesn't need to | 401 // ASSERT_TRUE/EXPECT_TRUE helpers. Note that this matcher doesn't need to |
402 // handle ASSERT_FALSE/EXPECT_FALSE, because it gets coerced to bool before | 402 // handle ASSERT_FALSE/EXPECT_FALSE, because it gets coerced to bool before |
403 // being passed as an argument to AssertionResult's constructor. As a result, | 403 // being passed as an argument to AssertionResult's constructor. As a result, |
404 // GetRewriterCallback handles this case properly since the conversion isn't | 404 // GetRewriterCallback handles this case properly since the conversion isn't |
405 // hidden inside AssertionResult, and the generated replacement properly | 405 // hidden inside AssertionResult, and the generated replacement properly |
406 // rewrites the macro argument. | 406 // rewrites the macro argument. |
407 // However, the tool does need to handle the _TRUE counterparts, since the | 407 // However, the tool does need to handle the _TRUE counterparts, since the |
408 // conversion occurs inside the constructor in those cases. | 408 // conversion occurs inside the constructor in those cases. |
409 match_finder.addMatcher( | 409 match_finder.addMatcher( |
410 constructExpr( | 410 cxxConstructExpr( |
411 argumentCountIs(2), | 411 argumentCountIs(2), |
412 hasArgument(0, id("expr", expr(hasType(is_scoped_refptr)))), | 412 hasArgument(0, id("expr", expr(hasType(is_scoped_refptr)))), |
413 hasDeclaration(is_gtest_assertion_result_ctor)), | 413 hasDeclaration(is_gtest_assertion_result_ctor)), |
414 ¯o_callback); | 414 ¯o_callback); |
415 | 415 |
416 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = | 416 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
417 clang::tooling::newFrontendActionFactory(&match_finder); | 417 clang::tooling::newFrontendActionFactory(&match_finder); |
418 int result = tool.run(factory.get()); | 418 int result = tool.run(factory.get()); |
419 if (result != 0) | 419 if (result != 0) |
420 return result; | 420 return result; |
421 | 421 |
422 // Serialization format is documented in tools/clang/scripts/run_tool.py | 422 // Serialization format is documented in tools/clang/scripts/run_tool.py |
423 llvm::outs() << "==== BEGIN EDITS ====\n"; | 423 llvm::outs() << "==== BEGIN EDITS ====\n"; |
424 for (const auto& r : replacements) { | 424 for (const auto& r : replacements) { |
425 std::string replacement_text = r.getReplacementText().str(); | 425 std::string replacement_text = r.getReplacementText().str(); |
426 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 426 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
427 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() << ":::" | 427 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() << ":::" |
428 << r.getLength() << ":::" << replacement_text << "\n"; | 428 << r.getLength() << ":::" << replacement_text << "\n"; |
429 } | 429 } |
430 llvm::outs() << "==== END EDITS ====\n"; | 430 llvm::outs() << "==== END EDITS ====\n"; |
431 | 431 |
432 return 0; | 432 return 0; |
433 } | 433 } |
OLD | NEW |