void AssignOperatorSignatureCheck::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) {
    const auto HasGoodReturnType = methodDecl(returns(lValueReferenceType(
        pointee(unless(isConstQualified()),
                hasDeclaration(equalsBoundNode("class"))))));

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

    Finder->addMatcher(
        methodDecl(IsSelfAssign, unless(HasGoodReturnType)).bind("ReturnType"),
        this);

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

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

    Finder->addMatcher(methodDecl(IsSelfAssign, isConst()).bind("Const"), 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);
}
static TypeMatcher constRefType() {
  return lValueReferenceType(pointee(isConstQualified()));
}