1 //===--- MutatingCopyCheck.cpp - clang-tidy -------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "MutatingCopyCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang { 16 namespace tidy { 17 namespace cert { 18 19 static constexpr llvm::StringLiteral SourceDeclName = "ChangedPVD"; 20 static constexpr llvm::StringLiteral MutatingOperatorName = "MutatingOp"; 21 static constexpr llvm::StringLiteral MutatingCallName = "MutatingCall"; 22 23 void MutatingCopyCheck::registerMatchers(MatchFinder *Finder) { 24 const auto MemberExprOrSourceObject = anyOf( 25 memberExpr(), 26 declRefExpr(to(decl(equalsBoundNode(std::string(SourceDeclName)))))); 27 28 const auto IsPartOfSource = 29 allOf(unless(hasDescendant(expr(unless(MemberExprOrSourceObject)))), 30 MemberExprOrSourceObject); 31 32 const auto IsSourceMutatingAssignment = traverse( 33 TK_AsIs, binaryOperation(hasOperatorName("="), hasLHS(IsPartOfSource)) 34 .bind(MutatingOperatorName)); 35 36 const auto MemberExprOrSelf = anyOf(memberExpr(), cxxThisExpr()); 37 38 const auto IsPartOfSelf = allOf( 39 unless(hasDescendant(expr(unless(MemberExprOrSelf)))), MemberExprOrSelf); 40 41 const auto IsSelfMutatingAssignment = 42 binaryOperation(isAssignmentOperator(), hasLHS(IsPartOfSelf)); 43 44 const auto IsSelfMutatingMemberFunction = 45 functionDecl(hasBody(hasDescendant(IsSelfMutatingAssignment))); 46 47 const auto IsSourceMutatingMemberCall = 48 cxxMemberCallExpr(on(IsPartOfSource), 49 callee(IsSelfMutatingMemberFunction)) 50 .bind(MutatingCallName); 51 52 const auto MutatesSource = allOf( 53 hasParameter( 54 0, parmVarDecl(hasType(lValueReferenceType())).bind(SourceDeclName)), 55 anyOf(forEachDescendant(IsSourceMutatingAssignment), 56 forEachDescendant(IsSourceMutatingMemberCall))); 57 58 Finder->addMatcher(cxxConstructorDecl(isCopyConstructor(), MutatesSource), 59 this); 60 61 Finder->addMatcher(cxxMethodDecl(isCopyAssignmentOperator(), MutatesSource), 62 this); 63 } 64 65 void MutatingCopyCheck::check(const MatchFinder::MatchResult &Result) { 66 if (const auto *MemberCall = 67 Result.Nodes.getNodeAs<CXXMemberCallExpr>(MutatingCallName)) 68 diag(MemberCall->getBeginLoc(), "call mutates copied object"); 69 else if (const auto *Assignment = 70 Result.Nodes.getNodeAs<Expr>(MutatingOperatorName)) 71 diag(Assignment->getBeginLoc(), "mutating copied object"); 72 } 73 74 } // namespace cert 75 } // namespace tidy 76 } // namespace clang 77