void SuspiciousEnumUsageCheck::registerMatchers(MatchFinder *Finder) {
  const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
    return allOf(ignoringImpCasts(expr().bind(RefName)),
                 ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
  };

  Finder->addMatcher(
      binaryOperator(hasOperatorName("|"), hasLHS(enumExpr("", "enumDecl")),
                     hasRHS(allOf(enumExpr("", "otherEnumDecl"),
                                  ignoringImpCasts(hasType(enumDecl(
                                      unless(equalsBoundNode("enumDecl"))))))))
          .bind("diffEnumOp"),
      this);

  Finder->addMatcher(
      binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
                     hasLHS(enumExpr("lhsExpr", "enumDecl")),
                     hasRHS(allOf(enumExpr("rhsExpr", ""),
                                  ignoringImpCasts(hasType(enumDecl(
                                      equalsBoundNode("enumDecl"))))))),
      this);

  Finder->addMatcher(
      binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
                     hasEitherOperand(
                         allOf(hasType(isInteger()), unless(enumExpr("", "")))),
                     hasEitherOperand(enumExpr("enumExpr", "enumDecl"))),
      this);

  Finder->addMatcher(
      binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("+=")),
                     hasRHS(enumExpr("enumExpr", "enumDecl"))),
      this);
}
void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) {
  // Match a floating literal with value 0.5.
  auto FloatHalf = floatLiteral(floatHalf());

  // Match a floating point expression.
  auto FloatType = expr(hasType(realFloatingPointType()));

  // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
  // cast to floating type.
  auto FloatOrCastHalf =
      anyOf(FloatHalf, implicitCastExpr(FloatType, has(FloatHalf)));

  // Match if either the LHS or RHS is a floating literal of 0.5 or a floating
  // literal of 0.5 and the other is of type double or vice versa.
  auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
                           allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));

  // Find expressions of cast to int of the sum of a floating point expression
  // and 0.5.
  MatchFinder->addMatcher(
      implicitCastExpr(
          hasImplicitDestinationType(isInteger()),
          ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
          .bind("CastExpr"),
      this);
}
void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  // Flag all operators +, -, +=, -=, ++, -- that result in a pointer
  Finder->addMatcher(
      binaryOperator(
          anyOf(hasOperatorName("+"), hasOperatorName("-"),
                hasOperatorName("+="), hasOperatorName("-=")),
          hasType(pointerType()),
          unless(hasLHS(ignoringImpCasts(declRefExpr(to(isImplicit()))))))
          .bind("expr"),
      this);

  Finder->addMatcher(
      unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
                    hasType(pointerType()))
          .bind("expr"),
      this);

  // Array subscript on a pointer (not an array) is also pointer arithmetic
  Finder->addMatcher(
      arraySubscriptExpr(
          hasBase(ignoringImpCasts(
              anyOf(hasType(pointerType()),
                    hasType(decayedType(hasDecayedType(pointerType())))))))
          .bind("expr"),
      this);
}
void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) {
  const auto SignedIntegerOperand =
      expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed-operand");

  // The standard [bitmask.types] allows some integral types to be implemented
  // as signed types. Exclude these types from diagnosing for bitwise or(|) and
  // bitwise and(&). Shifting and complementing such values is still not
  // allowed.
  const auto BitmaskType = namedDecl(anyOf(
      hasName("::std::locale::category"), hasName("::std::ctype_base::mask"),
      hasName("::std::ios_base::fmtflags"), hasName("::std::ios_base::iostate"),
      hasName("::std::ios_base::openmode")));
  const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType)));

  // Match binary bitwise operations on signed integer arguments.
  Finder->addMatcher(
      binaryOperator(
          allOf(anyOf(hasOperatorName("^"), hasOperatorName("|"),
                      hasOperatorName("&"), hasOperatorName("^="),
                      hasOperatorName("|="), hasOperatorName("&=")),

                unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))),

                hasEitherOperand(SignedIntegerOperand),
                hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))))
          .bind("binary-no-sign-interference"),
      this);

  // Shifting and complement is not allowed for any signed integer type because
  // the sign bit may corrupt the result.
  Finder->addMatcher(
      binaryOperator(
          allOf(anyOf(hasOperatorName("<<"), hasOperatorName(">>"),
                      hasOperatorName("<<="), hasOperatorName(">>=")),
                hasEitherOperand(SignedIntegerOperand),
                hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))))
          .bind("binary-sign-interference"),
      this);

  // Match unary operations on signed integer types.
  Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"),
                                         hasUnaryOperand(SignedIntegerOperand)))
                         .bind("unary-signed"),
                     this);
}
void DurationSubtractionCheck::registerMatchers(MatchFinder *Finder) {
  Finder->addMatcher(
      binaryOperator(
          hasOperatorName("-"),
          hasLHS(callExpr(callee(functionDecl(DurationConversionFunction())
                                     .bind("function_decl")),
                          hasArgument(0, expr().bind("lhs_arg")))))
          .bind("binop"),
      this);
}
void PointerAndIntegralOperationCheck::registerMatchers(MatchFinder *Finder) {
  const auto PointerExpr = expr(hasType(pointerType()));
  const auto BoolExpr = ignoringParenImpCasts(hasType(booleanType()));
  const auto CharExpr = ignoringParenImpCasts(hasType(isAnyCharacter()));

  const auto BinOpWithPointerExpr =
      binaryOperator(unless(anyOf(hasOperatorName(","), hasOperatorName("="))),
                     hasEitherOperand(PointerExpr));

  const auto AssignToPointerExpr =
      binaryOperator(hasOperatorName("="), hasLHS(PointerExpr));

  const auto CompareToPointerExpr =
      binaryOperator(matchers::isRelationalOperator(),
                     hasEitherOperand(PointerExpr));

  // Detect expression like: ptr = (x != y);
  Finder->addMatcher(binaryOperator(AssignToPointerExpr, hasRHS(BoolExpr))
                         .bind("assign-bool-to-pointer"),
                     this);

  // Detect expression like: ptr = A[i]; where A is char*.
  Finder->addMatcher(binaryOperator(AssignToPointerExpr, hasRHS(CharExpr))
                         .bind("assign-char-to-pointer"),
                     this);

  // Detect expression like: ptr < false;
  Finder->addMatcher(
      binaryOperator(BinOpWithPointerExpr,
                     hasEitherOperand(ignoringParenImpCasts(cxxBoolLiteral())))
          .bind("pointer-and-bool-literal"),
      this);

  // Detect expression like: ptr < 'a';
  Finder->addMatcher(binaryOperator(BinOpWithPointerExpr,
                                    hasEitherOperand(ignoringParenImpCasts(
                                        characterLiteral())))
                         .bind("pointer-and-char-literal"),
                     this);

  // Detect expression like: ptr < 0;
  Finder->addMatcher(binaryOperator(CompareToPointerExpr,
                                    hasEitherOperand(ignoringParenImpCasts(
                                        integerLiteral(equals(0)))))
                         .bind("compare-pointer-to-zero"),
                     this);

  // Detect expression like: ptr < nullptr;
  Finder->addMatcher(binaryOperator(CompareToPointerExpr,
                                    hasEitherOperand(ignoringParenImpCasts(
                                        cxxNullPtrLiteralExpr())))
                         .bind("compare-pointer-to-null"),
                     this);
}
void MisplacedOperatorInStrlenInAllocCheck::registerMatchers(
    MatchFinder *Finder) {
  const auto StrLenFunc = functionDecl(anyOf(
      hasName("::strlen"), hasName("::std::strlen"), hasName("::strnlen"),
      hasName("::std::strnlen"), hasName("::strnlen_s"),
      hasName("::std::strnlen_s"), hasName("::wcslen"),
      hasName("::std::wcslen"), hasName("::wcsnlen"), hasName("::std::wcsnlen"),
      hasName("::wcsnlen_s"), hasName("std::wcsnlen_s")));

  const auto BadUse =
      callExpr(callee(StrLenFunc),
               hasAnyArgument(ignoringImpCasts(
                   binaryOperator(
                       hasOperatorName("+"),
                       hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))
                       .bind("BinOp"))))
          .bind("StrLen");

  const auto BadArg = anyOf(
      allOf(unless(binaryOperator(
                hasOperatorName("+"), hasLHS(BadUse),
                hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))),
            hasDescendant(BadUse)),
      BadUse);

  const auto Alloc0Func =
      functionDecl(anyOf(hasName("::malloc"), hasName("std::malloc"),
                         hasName("::alloca"), hasName("std::alloca")));
  const auto Alloc1Func =
      functionDecl(anyOf(hasName("::calloc"), hasName("std::calloc"),
                         hasName("::realloc"), hasName("std::realloc")));

  const auto Alloc0FuncPtr =
      varDecl(hasType(isConstQualified()),
              hasInitializer(ignoringParenImpCasts(
                  declRefExpr(hasDeclaration(Alloc0Func)))));
  const auto Alloc1FuncPtr =
      varDecl(hasType(isConstQualified()),
              hasInitializer(ignoringParenImpCasts(
                  declRefExpr(hasDeclaration(Alloc1Func)))));

  Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))),
                              hasArgument(0, BadArg))
                         .bind("Alloc"),
                     this);
  Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))),
                              hasArgument(1, BadArg))
                         .bind("Alloc"),
                     this);
  Finder->addMatcher(
      cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc"), this);
}
void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) {
  Finder->addMatcher(
      expr(unless(isInTemplateInstantiation()),
           expr(sizeOfExpr(has(ignoringParenImpCasts(
                    expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
                        matchesName("^(::std::|::string)"),
                        unless(matchesName("^::std::(bitset|array)$")),
                        hasMethod(cxxMethodDecl(hasName("size"), isPublic(),
                                                isConst())))))))))))
               .bind("sizeof"),
           // Ignore ARRAYSIZE(<array of containers>) pattern.
           unless(hasAncestor(binaryOperator(
               anyOf(hasOperatorName("/"), hasOperatorName("%")),
               hasLHS(ignoringParenCasts(sizeOfExpr(expr()))),
               hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))),
      this);
}