| 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 |