diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp')
| -rw-r--r-- | clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp new file mode 100644 index 0000000..cbbb1a0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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 "CopyConstructorMutatesArgumentCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +static constexpr llvm::StringLiteral SourceDeclName = "ChangedPVD"; +static constexpr llvm::StringLiteral MutatingOperatorName = "MutatingOp"; +static constexpr llvm::StringLiteral MutatingCallName = "MutatingCall"; + +void CopyConstructorMutatesArgumentCheck::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 CopyConstructorMutatesArgumentCheck::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 clang::tidy::bugprone |
