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