text: //===--- MutatingCopyCheck.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 "MutatingCopyCheck.h"
  #include "clang/AST/ASTContext.h"
  #include "clang/ASTMatchers/ASTMatchFinder.h"
  
  using namespace clang::ast_matchers;
  
  namespace clang {
  namespace tidy {
  namespace cert {
  
  static constexpr llvm::StringLiteral SourceDeclName = "ChangedPVD";
  static constexpr llvm::StringLiteral MutatingOperatorName = "MutatingOp";
  static constexpr llvm::StringLiteral MutatingCallName = "MutatingCall";
  
  void MutatingCopyCheck::registerMatchers(MatchFinder *Finder) {
    const auto MemberExprOrSourceObject = anyOf(
        memberExpr(),
        declRefExpr(to(decl(equalsBoundNode(std::string(SourceDeclName))))));
  
    const auto IsPartOfSource =
        allOf(unless(hasDescendant(expr(unless(MemberExprOrSourceObject)))),
              MemberExprOrSourceObject);
  
    const auto IsSourceMutatingAssignment = traverse(
        TK_AsIs, binaryOperation(hasOperatorName("="), hasLHS(IsPartOfSource))
                     .bind(MutatingOperatorName));
  
    const auto MemberExprOrSelf = anyOf(memberExpr(), cxxThisExpr());
  
    const auto IsPartOfSelf = allOf(
        unless(hasDescendant(expr(unless(MemberExprOrSelf)))), MemberExprOrSelf);
  
    const auto IsSelfMutatingAssignment =
        binaryOperation(isAssignmentOperator(), hasLHS(IsPartOfSelf));
  
    const auto IsSelfMutatingMemberFunction =
        functionDecl(hasBody(hasDescendant(IsSelfMutatingAssignment)));
  
    const auto IsSourceMutatingMemberCall =
        cxxMemberCallExpr(on(IsPartOfSource),
                          callee(IsSelfMutatingMemberFunction))
            .bind(MutatingCallName);
  
    const auto MutatesSource = allOf(
        hasParameter(
            0, parmVarDecl(hasType(lValueReferenceType())).bind(SourceDeclName)),
        anyOf(forEachDescendant(IsSourceMutatingAssignment),
              forEachDescendant(IsSourceMutatingMemberCall)));
  
    Finder->addMatcher(cxxConstructorDecl(isCopyConstructor(), MutatesSource),
                       this);
  
    Finder->addMatcher(cxxMethodDecl(isCopyAssignmentOperator(), MutatesSource),
                       this);
  }
  
  void MutatingCopyCheck::check(const MatchFinder::MatchResult &Result) {
    if (const auto *MemberCall =
            Result.Nodes.getNodeAs<CXXMemberCallExpr>(MutatingCallName))
      diag(MemberCall->getBeginLoc(), "call mutates copied object");
    else if (const auto *Assignment =
                 Result.Nodes.getNodeAs<Expr>(MutatingOperatorName))
      diag(Assignment->getBeginLoc(), "mutating copied object");
  }
  
  } // namespace cert
  } // namespace tidy
  } // namespace clang
  