PreferIsaOrDynCastInConditionalsCheck.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. //===--- PreferIsaOrDynCastInConditionalsCheck.cpp - clang-tidy
  2. //---------------------===//
  3. //
  4. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  5. // See https://llvm.org/LICENSE.txt for license information.
  6. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "PreferIsaOrDynCastInConditionalsCheck.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/Lex/Lexer.h"
  13. using namespace clang::ast_matchers;
  14. namespace clang {
  15. namespace ast_matchers {
  16. AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
  17. } // namespace ast_matchers
  18. namespace tidy::llvm_check {
  19. void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
  20. MatchFinder *Finder) {
  21. auto Condition = hasCondition(implicitCastExpr(has(
  22. callExpr(
  23. allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
  24. anyOf(callee(namedDecl(hasName("cast"))),
  25. callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast")))))
  26. .bind("call"))));
  27. auto Any = anyOf(
  28. has(declStmt(containsDeclaration(
  29. 0,
  30. varDecl(hasInitializer(
  31. callExpr(allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
  32. callee(namedDecl(hasName("cast")))))
  33. .bind("assign")))))),
  34. Condition);
  35. auto CallExpression =
  36. callExpr(
  37. allOf(
  38. unless(isMacroID()), unless(cxxMemberCallExpr()),
  39. allOf(callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null",
  40. "dyn_cast", "dyn_cast_or_null"))
  41. .bind("func")),
  42. hasArgument(
  43. 0,
  44. mapAnyOf(declRefExpr, cxxMemberCallExpr).bind("arg")))))
  45. .bind("rhs");
  46. Finder->addMatcher(
  47. traverse(TK_AsIs,
  48. stmt(anyOf(
  49. ifStmt(Any), whileStmt(Any), doStmt(Condition),
  50. binaryOperator(
  51. allOf(unless(isExpansionInFileMatching(
  52. "llvm/include/llvm/Support/Casting.h")),
  53. hasOperatorName("&&"),
  54. hasLHS(implicitCastExpr().bind("lhs")),
  55. hasRHS(anyOf(implicitCastExpr(has(CallExpression)),
  56. CallExpression))))
  57. .bind("and")))),
  58. this);
  59. }
  60. void PreferIsaOrDynCastInConditionalsCheck::check(
  61. const MatchFinder::MatchResult &Result) {
  62. if (const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("assign")) {
  63. SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
  64. SourceLocation EndLoc =
  65. StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
  66. diag(MatchedDecl->getBeginLoc(),
  67. "cast<> in conditional will assert rather than return a null pointer")
  68. << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
  69. "dyn_cast");
  70. } else if (const auto *MatchedDecl =
  71. Result.Nodes.getNodeAs<CallExpr>("call")) {
  72. SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
  73. SourceLocation EndLoc =
  74. StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
  75. StringRef Message =
  76. "cast<> in conditional will assert rather than return a null pointer";
  77. if (Result.Nodes.getNodeAs<NamedDecl>("dyn_cast"))
  78. Message = "return value from dyn_cast<> not used";
  79. diag(MatchedDecl->getBeginLoc(), Message)
  80. << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");
  81. } else if (const auto *MatchedDecl =
  82. Result.Nodes.getNodeAs<BinaryOperator>("and")) {
  83. const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>("lhs");
  84. const auto *RHS = Result.Nodes.getNodeAs<CallExpr>("rhs");
  85. const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
  86. const auto *Func = Result.Nodes.getNodeAs<NamedDecl>("func");
  87. assert(LHS && "LHS is null");
  88. assert(RHS && "RHS is null");
  89. assert(Arg && "Arg is null");
  90. assert(Func && "Func is null");
  91. StringRef LHSString(Lexer::getSourceText(
  92. CharSourceRange::getTokenRange(LHS->getSourceRange()),
  93. *Result.SourceManager, getLangOpts()));
  94. StringRef ArgString(Lexer::getSourceText(
  95. CharSourceRange::getTokenRange(Arg->getSourceRange()),
  96. *Result.SourceManager, getLangOpts()));
  97. if (ArgString != LHSString)
  98. return;
  99. StringRef RHSString(Lexer::getSourceText(
  100. CharSourceRange::getTokenRange(RHS->getSourceRange()),
  101. *Result.SourceManager, getLangOpts()));
  102. std::string Replacement("isa_and_nonnull");
  103. Replacement += RHSString.substr(Func->getName().size());
  104. diag(MatchedDecl->getBeginLoc(),
  105. "isa_and_nonnull<> is preferred over an explicit test for null "
  106. "followed by calling isa<>")
  107. << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(),
  108. MatchedDecl->getEndLoc()),
  109. Replacement);
  110. }
  111. }
  112. } // namespace tidy::llvm_check
  113. } // namespace clang