aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/CopyConstructorMutatesArgumentCheck.cpp74
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