void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) {
  // Swap as a function need not to be considered, because rvalue can not
  // be bound to a non-const reference.
  const auto ShrinkableAsMember =
      memberExpr(member(valueDecl().bind("ContainerDecl")));
  const auto ShrinkableAsDecl =
      declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl")));
  const auto CopyCtorCall = constructExpr(
      hasArgument(0, anyOf(ShrinkableAsMember, ShrinkableAsDecl,
                           unaryOperator(has(ShrinkableAsMember)),
                           unaryOperator(has(ShrinkableAsDecl)))));
  const auto SwapParam = expr(anyOf(
      memberExpr(member(equalsBoundNode("ContainerDecl"))),
      declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))),
      unaryOperator(has(memberExpr(member(equalsBoundNode("ContainerDecl"))))),
      unaryOperator(
          has(declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl")))))));

  Finder->addMatcher(
      memberCallExpr(on(hasType(namedDecl(stlShrinkableContainer()))),
                     callee(methodDecl(hasName("swap"))),
                     has(memberExpr(hasDescendant(CopyCtorCall))),
                     hasArgument(0, SwapParam.bind("ContainerToShrink")),
                     unless(isInTemplateInstantiation()))
          .bind("CopyAndSwapTrick"),
      this);
}
void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) {
  auto DescendantWithSideEffect =
      hasDescendant(expr(hasSideEffect(CheckFunctionCalls)));
  auto ConditionWithSideEffect = hasCondition(DescendantWithSideEffect);
  Finder->addMatcher(
      stmt(
          anyOf(conditionalOperator(ConditionWithSideEffect),
                ifStmt(ConditionWithSideEffect),
                unaryOperator(hasOperatorName("!"),
                              hasUnaryOperand(unaryOperator(
                                  hasOperatorName("!"),
                                  hasUnaryOperand(DescendantWithSideEffect))))))
          .bind("condStmt"),
      this);
}
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
  const auto WrongUse = anyOf(
      hasParent(
          binaryOperator(
              anyOf(has(integerLiteral(equals(0))),
                    allOf(anyOf(hasOperatorName("<"), hasOperatorName(">="),
                                hasOperatorName(">"), hasOperatorName("<=")),
                          hasEitherOperand(integerLiteral(equals(1))))))
              .bind("SizeBinaryOp")),
      hasParent(implicitCastExpr(
          hasImplicitDestinationType(isBoolType()),
          anyOf(
              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
              anything()))),
      hasParent(explicitCastExpr(hasDestinationType(isBoolType()))));

  Finder->addMatcher(
      memberCallExpr(
          on(expr(anyOf(hasType(namedDecl(stlContainer())),
                        hasType(pointsTo(namedDecl(stlContainer()))),
                        hasType(references(namedDecl(stlContainer())))))
                 .bind("STLObject")),
          callee(methodDecl(hasName("size"))), WrongUse).bind("SizeCallExpr"),
      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 UnconventionalAssignOperatorCheck::registerMatchers(
    ast_matchers::MatchFinder *Finder) {
  // Only register the matchers for C++; the functionality currently does not
  // provide any benefit to other languages, despite being benign.
  if (!getLangOpts().CPlusPlus)
    return;

  const auto HasGoodReturnType = cxxMethodDecl(returns(lValueReferenceType(
      pointee(unless(isConstQualified()),
              anyOf(autoType(), hasDeclaration(equalsBoundNode("class")))))));

  const auto IsSelf = qualType(
      anyOf(hasDeclaration(equalsBoundNode("class")),
            referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))));
  const auto IsAssign =
      cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
                    hasName("operator="), ofClass(recordDecl().bind("class")))
          .bind("method");
  const auto IsSelfAssign =
      cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf))))
          .bind("method");

  Finder->addMatcher(
      cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"),
      this);

  const auto BadSelf = referenceType(
      anyOf(lValueReferenceType(pointee(unless(isConstQualified()))),
            rValueReferenceType(pointee(isConstQualified()))));

  Finder->addMatcher(
      cxxMethodDecl(IsSelfAssign,
                    hasParameter(0, parmVarDecl(hasType(BadSelf))))
          .bind("ArgumentType"),
      this);

  Finder->addMatcher(
      cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"),
      this);

  const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts(
      anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())),
            cxxOperatorCallExpr(argumentCountIs(1),
                                callee(unresolvedLookupExpr()),
                                hasArgument(0, cxxThisExpr())))))));
  const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType);

  Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign))
                         .bind("returnStmt"),
                     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 NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
  // Add parameters to Parameters.
  Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this);

  // C++ constructor.
  Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);

  // Track unused parameters, there is Wunused-parameter about unused
  // parameters.
  Finder->addMatcher(declRefExpr().bind("Ref"), this);

  // Analyse parameter usage in function.
  Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName("++"),
                                                    hasOperatorName("--"))),
                                binaryOperator(), callExpr(), returnStmt(),
                                cxxConstructExpr()))
                         .bind("Mark"),
                     this);
  Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
}
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
  // Only register the matchers for C++; the functionality currently does not
  // provide any benefit to other languages, despite being benign.
  if (!getLangOpts().CPlusPlus)
    return;

  const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
      namedDecl(
          has(cxxMethodDecl(
                  isConst(), parameterCountIs(0), isPublic(), hasName("size"),
                  returns(qualType(isInteger(), unless(booleanType()))))
                  .bind("size")),
          has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
                            hasName("empty"), returns(booleanType()))
                  .bind("empty")))
          .bind("container")));

  const auto WrongUse = anyOf(
      hasParent(binaryOperator(
                    matchers::isComparisonOperator(),
                    hasEitherOperand(ignoringImpCasts(anyOf(
                        integerLiteral(equals(1)), integerLiteral(equals(0))))))
                    .bind("SizeBinaryOp")),
      hasParent(implicitCastExpr(
          hasImplicitDestinationType(booleanType()),
          anyOf(
              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
              anything()))),
      hasParent(explicitCastExpr(hasDestinationType(booleanType()))));

  Finder->addMatcher(
      cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
                                      hasType(pointsTo(ValidContainer)),
                                      hasType(references(ValidContainer))))
                               .bind("STLObject")),
                        callee(cxxMethodDecl(hasName("size"))), WrongUse)
          .bind("SizeCallExpr"),
      this);
}
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
  const auto Calc =
      expr(anyOf(binaryOperator(
                     anyOf(hasOperatorName("+"), hasOperatorName("-"),
                           hasOperatorName("*"), hasOperatorName("<<"))),
                 unaryOperator(hasOperatorName("~"))),
           hasType(isInteger()))
          .bind("Calc");

  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
                                             has(ignoringParenImpCasts(Calc)));
  const auto ImplicitCast =
      implicitCastExpr(hasImplicitDestinationType(isInteger()),
                       has(ignoringParenImpCasts(Calc)));
  const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");

  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
  Finder->addMatcher(
      binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
      this);
}
void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) {
  // There are two ways to get into trouble with objects like FILE *:
  // dereferencing the pointer type to be a non-pointer type, and declaring
  // the type as a non-pointer type in the first place. While the declaration
  // itself could technically be well-formed in the case where the type is not
  // an opaque type, it's highly suspicious behavior.
  //
  // POSIX types are a bit different in that it's reasonable to declare a
  // non-pointer variable or data member of the type, but it is not reasonable
  // to dereference a pointer to the type, or declare a parameter of non-pointer
  // type.
  auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl"));
  auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl"));
  auto BadEitherType = anyOf(BadFILEType, BadPOSIXType);

  Finder->addMatcher(
      namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType)))
          .bind("decl"),
      this);
  Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this);
  Finder->addMatcher(
      expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"),
      this);
}
void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) {
  // Match relational operators.
  const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!"));
  const auto ComparisonBinaryOperator =
      binaryOperator(matchers::isComparisonOperator());
  const auto ComparisonOperator =
      expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator));

  // Add the list of known string compare-like functions and add user-defined
  // functions.
  std::vector<std::string> FunctionNames = utils::options::parseStringList(
      (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions)
          .str());

  // Match a call to a string compare functions.
  const auto FunctionCompareDecl =
      functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
                                                     FunctionNames.end())))
          .bind("decl");
  const auto DirectStringCompareCallExpr =
      callExpr(hasDeclaration(FunctionCompareDecl)).bind("call");
  const auto MacroStringCompareCallExpr = conditionalOperator(anyOf(
      hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)),
      hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr))));
  // The implicit cast is not present in C.
  const auto StringCompareCallExpr = ignoringParenImpCasts(
      anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr));

  if (WarnOnImplicitComparison) {
    // Detect suspicious calls to string compare:
    //     'if (strcmp())'  ->  'if (strcmp() != 0)'
    Finder->addMatcher(
        stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
                   whileStmt(hasCondition(StringCompareCallExpr)),
                   doStmt(hasCondition(StringCompareCallExpr)),
                   forStmt(hasCondition(StringCompareCallExpr)),
                   binaryOperator(
                       anyOf(hasOperatorName("&&"), hasOperatorName("||")),
                       hasEitherOperand(StringCompareCallExpr))))
            .bind("missing-comparison"),
        this);
  }

  if (WarnOnLogicalNotComparison) {
    // Detect suspicious calls to string compared with '!' operator:
    //     'if (!strcmp())'  ->  'if (strcmp() == 0)'
    Finder->addMatcher(unaryOperator(hasOperatorName("!"),
                                     hasUnaryOperand(ignoringParenImpCasts(
                                         StringCompareCallExpr)))
                           .bind("logical-not-comparison"),
                       this);
  }

  // Detect suspicious cast to an inconsistant type (i.e. not integer type).
  Finder->addMatcher(
      implicitCastExpr(unless(hasType(isInteger())),
                       hasSourceExpression(StringCompareCallExpr))
          .bind("invalid-conversion"),
      this);

  // Detect suspicious operator with string compare function as operand.
  Finder->addMatcher(
      binaryOperator(
          unless(anyOf(matchers::isComparisonOperator(), hasOperatorName("&&"),
                       hasOperatorName("||"), hasOperatorName("="))),
          hasEitherOperand(StringCompareCallExpr))
          .bind("suspicious-operator"),
      this);

  // Detect comparison to invalid constant: 'strcmp() == -1'.
  const auto InvalidLiteral = ignoringParenImpCasts(
      anyOf(integerLiteral(unless(equals(0))),
            unaryOperator(
                hasOperatorName("-"),
                has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))),
            characterLiteral(), cxxBoolLiteral()));

  Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(),
                                    hasEitherOperand(StringCompareCallExpr),
                                    hasEitherOperand(InvalidLiteral))
                         .bind("invalid-comparison"),
                     this);
}