reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
//===--- PreferIsaOrDynCastInConditionalsCheck.cpp - clang-tidy
//---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "PreferIsaOrDynCastInConditionalsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang {
namespace ast_matchers {
AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
} // namespace ast_matchers

namespace tidy {
namespace llvm_check {

void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
    MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  auto Condition = hasCondition(implicitCastExpr(has(
      callExpr(
          allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
                anyOf(callee(namedDecl(hasName("cast"))),
                      callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast")))))
          .bind("call"))));

  auto Any = anyOf(
      has(declStmt(containsDeclaration(
          0,
          varDecl(hasInitializer(
              callExpr(allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
                             callee(namedDecl(hasName("cast")))))
                  .bind("assign")))))),
      Condition);

  auto CallExpression =
      callExpr(
          allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
                allOf(callee(namedDecl(anyOf(hasName("isa"), hasName("cast"),
                                             hasName("cast_or_null"),
                                             hasName("dyn_cast"),
                                             hasName("dyn_cast_or_null")))
                                 .bind("func")),
                      hasArgument(0, anyOf(declRefExpr().bind("arg"),
                                           cxxMemberCallExpr().bind("arg"))))))
          .bind("rhs");

  Finder->addMatcher(
      stmt(anyOf(ifStmt(Any), whileStmt(Any), doStmt(Condition),
                 binaryOperator(
                     allOf(unless(isExpansionInFileMatching(
                               "llvm/include/llvm/Support/Casting.h")),
                           hasOperatorName("&&"),
                           hasLHS(implicitCastExpr().bind("lhs")),
                           hasRHS(anyOf(implicitCastExpr(has(CallExpression)),
                                        CallExpression))))
                     .bind("and"))),
      this);
}

void PreferIsaOrDynCastInConditionalsCheck::check(
    const MatchFinder::MatchResult &Result) {
  if (const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("assign")) {
    SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
    SourceLocation EndLoc =
        StartLoc.getLocWithOffset(StringRef("cast").size() - 1);

    diag(MatchedDecl->getBeginLoc(),
         "cast<> in conditional will assert rather than return a null pointer")
        << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
                                        "dyn_cast");
  } else if (const auto *MatchedDecl =
                 Result.Nodes.getNodeAs<CallExpr>("call")) {
    SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
    SourceLocation EndLoc =
        StartLoc.getLocWithOffset(StringRef("cast").size() - 1);

    StringRef Message =
        "cast<> in conditional will assert rather than return a null pointer";
    if (Result.Nodes.getNodeAs<NamedDecl>("dyn_cast"))
      Message = "return value from dyn_cast<> not used";

    diag(MatchedDecl->getBeginLoc(), Message)
        << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");
  } else if (const auto *MatchedDecl =
                 Result.Nodes.getNodeAs<BinaryOperator>("and")) {
    const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>("lhs");
    const auto *RHS = Result.Nodes.getNodeAs<CallExpr>("rhs");
    const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
    const auto *Func = Result.Nodes.getNodeAs<NamedDecl>("func");

    assert(LHS && "LHS is null");
    assert(RHS && "RHS is null");
    assert(Arg && "Arg is null");
    assert(Func && "Func is null");

    StringRef LHSString(Lexer::getSourceText(
        CharSourceRange::getTokenRange(LHS->getSourceRange()),
        *Result.SourceManager, getLangOpts()));

    StringRef ArgString(Lexer::getSourceText(
        CharSourceRange::getTokenRange(Arg->getSourceRange()),
        *Result.SourceManager, getLangOpts()));

    if (ArgString != LHSString)
      return;

    StringRef RHSString(Lexer::getSourceText(
        CharSourceRange::getTokenRange(RHS->getSourceRange()),
        *Result.SourceManager, getLangOpts()));

    std::string Replacement("isa_and_nonnull");
    Replacement += RHSString.substr(Func->getName().size());

    diag(MatchedDecl->getBeginLoc(),
         "isa_and_nonnull<> is preferred over an explicit test for null "
         "followed by calling isa<>")
        << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(),
                                                    MatchedDecl->getEndLoc()),
                                        Replacement);
  }
}

} // namespace llvm_check
} // namespace tidy
} // namespace clang