[clang-tools-extra] [clang-tidy] Make `bugprone-unhandled-self-assignment` check more general (PR #147066)
Andrey Karlov via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 14 04:08:29 PDT 2025
https://github.com/negativ updated https://github.com/llvm/llvm-project/pull/147066
>From 950b6dce92eb2a831e7bd7e7ba44b3e8cc354dd4 Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Fri, 4 Jul 2025 17:13:20 +0300
Subject: [PATCH 1/7] Checking that some kind of constructor is called and
followed by a `swap`
---
.../bugprone/UnhandledSelfAssignmentCheck.cpp | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
index 1f432c4ccc5f0..8307e744a434c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
@@ -68,7 +68,23 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
const auto HasNoNestedSelfAssign =
cxxMethodDecl(unless(hasDescendant(cxxMemberCallExpr(callee(cxxMethodDecl(
hasName("operator="), ofClass(equalsBoundNode("class"))))))));
-
+
+ // Checking that some kind of constructor is called and followed by a `swap`:
+ // T& operator=(const T& other) {
+ // T tmp{this->internal_data(), some, other, args};
+ // swap(tmp);
+ // return *this;
+ // }
+ const auto HasCopyAndSwap = cxxMethodDecl(
+ ofClass(cxxRecordDecl(unless(hasAncestor(classTemplateDecl())))),
+ hasDescendant(
+ stmt(hasDescendant(
+ varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))))
+ .bind("tmp_var")),
+ hasDescendant(callExpr(callee(functionDecl(hasName("swap"))),
+ hasAnyArgument(declRefExpr(to(varDecl(
+ equalsBoundNode("tmp_var"))))))))));
+
DeclarationMatcher AdditionalMatcher = cxxMethodDecl();
if (WarnOnlyIfThisHasSuspiciousField) {
// Matcher for standard smart pointers.
@@ -94,6 +110,7 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
HasReferenceParam, HasNoSelfCheck,
unless(HasNonTemplateSelfCopy),
unless(HasTemplateSelfCopy),
+ unless(HasCopyAndSwap),
HasNoNestedSelfAssign, AdditionalMatcher)
.bind("copyAssignmentOperator"),
this);
>From 0cd33ec49693cfc6f5a72b72a9c50ba868634221 Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Fri, 4 Jul 2025 17:19:02 +0300
Subject: [PATCH 2/7] Tests
---
.../bugprone/unhandled-self-assignment.cpp | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp
index 8610393449f97..f2f61062f883f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp
@@ -28,6 +28,13 @@ template <class T>
class auto_ptr {
};
+namespace pmr {
+ template <typename TYPE = void>
+ class allocator {};
+}
+
+struct allocator_arg_t {} allocator_arg;
+
} // namespace std
void assert(int expression){};
@@ -540,6 +547,89 @@ class NotACopyAssignmentOperator {
Uy *getUy() const { return Ptr2; }
};
+// Support "extended" copy/move constructors
+class AllocatorAwareClass {
+ // pointer member to trigger bugprone-unhandled-self-assignment
+ void *foo = nullptr;
+
+ public:
+ using allocator_type = std::pmr::allocator<>;
+
+ AllocatorAwareClass(const AllocatorAwareClass& other) {
+ }
+
+ AllocatorAwareClass(const AllocatorAwareClass& other, const allocator_type& alloc) {
+ }
+
+ AllocatorAwareClass& operator=(const AllocatorAwareClass& other) {
+ AllocatorAwareClass tmp(other, get_allocator());
+ swap(tmp);
+ return *this;
+ }
+
+ void swap(AllocatorAwareClass& other) noexcept {
+ }
+
+ allocator_type get_allocator() const {
+ return allocator_type();
+ }
+};
+
+// Support "extended" copy/move constructors + std::swap
+class AllocatorAwareClassStdSwap {
+ // pointer member to trigger bugprone-unhandled-self-assignment
+ void *foo = nullptr;
+
+ public:
+ using allocator_type = std::pmr::allocator<>;
+
+ AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other) {
+ }
+
+ AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other, const allocator_type& alloc) {
+ }
+
+ AllocatorAwareClassStdSwap& operator=(const AllocatorAwareClassStdSwap& other) {
+ using std::swap;
+
+ AllocatorAwareClassStdSwap tmp(other, get_allocator());
+ swap(*this, tmp);
+ return *this;
+ }
+
+ allocator_type get_allocator() const {
+ return allocator_type();
+ }
+};
+
+// Support "extended" copy/move constructors + ADL swap
+class AllocatorAwareClassAdlSwap {
+ // pointer member to trigger bugprone-unhandled-self-assignment
+ void *foo = nullptr;
+
+ public:
+ using allocator_type = std::pmr::allocator<>;
+
+ AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other) {
+ }
+
+ AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other, const allocator_type& alloc) {
+ }
+
+ AllocatorAwareClassAdlSwap& operator=(const AllocatorAwareClassAdlSwap& other) {
+ AllocatorAwareClassAdlSwap tmp(other, get_allocator());
+ swap(*this, tmp);
+ return *this;
+ }
+
+ allocator_type get_allocator() const {
+ return allocator_type();
+ }
+
+ friend void swap(AllocatorAwareClassAdlSwap&, AllocatorAwareClassAdlSwap&) {
+ }
+};
+
///////////////////////////////////////////////////////////////////
/// Test cases which should be caught by the check.
>From 3771a9c519ace58827508ac422cadbbf17e2699a Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Mon, 7 Jul 2025 10:43:13 +0300
Subject: [PATCH 3/7] clang-format
---
.../bugprone/UnhandledSelfAssignmentCheck.cpp | 21 +++++++++----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
index 8307e744a434c..69f02ee37bba5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
@@ -68,7 +68,7 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
const auto HasNoNestedSelfAssign =
cxxMethodDecl(unless(hasDescendant(cxxMemberCallExpr(callee(cxxMethodDecl(
hasName("operator="), ofClass(equalsBoundNode("class"))))))));
-
+
// Checking that some kind of constructor is called and followed by a `swap`:
// T& operator=(const T& other) {
// T tmp{this->internal_data(), some, other, args};
@@ -84,7 +84,7 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
hasDescendant(callExpr(callee(functionDecl(hasName("swap"))),
hasAnyArgument(declRefExpr(to(varDecl(
equalsBoundNode("tmp_var"))))))))));
-
+
DeclarationMatcher AdditionalMatcher = cxxMethodDecl();
if (WarnOnlyIfThisHasSuspiciousField) {
// Matcher for standard smart pointers.
@@ -105,15 +105,14 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
hasType(arrayType())))))));
}
- Finder->addMatcher(cxxMethodDecl(ofClass(cxxRecordDecl().bind("class")),
- isCopyAssignmentOperator(), IsUserDefined,
- HasReferenceParam, HasNoSelfCheck,
- unless(HasNonTemplateSelfCopy),
- unless(HasTemplateSelfCopy),
- unless(HasCopyAndSwap),
- HasNoNestedSelfAssign, AdditionalMatcher)
- .bind("copyAssignmentOperator"),
- this);
+ Finder->addMatcher(
+ cxxMethodDecl(
+ ofClass(cxxRecordDecl().bind("class")), isCopyAssignmentOperator(),
+ IsUserDefined, HasReferenceParam, HasNoSelfCheck,
+ unless(HasNonTemplateSelfCopy), unless(HasTemplateSelfCopy),
+ unless(HasCopyAndSwap), HasNoNestedSelfAssign, AdditionalMatcher)
+ .bind("copyAssignmentOperator"),
+ this);
}
void UnhandledSelfAssignmentCheck::check(
>From 4f576fe13dad850149de43c9a28ac8dcb2d1e0ff Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Mon, 7 Jul 2025 11:03:43 +0300
Subject: [PATCH 4/7] Update ReleaseNotes
---
clang-tools-extra/docs/ReleaseNotes.rst | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f8f183e9de1cc..d27ff43f3af52 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -189,6 +189,12 @@ Changes in existing checks
calls of ``std::string`` constructor with char pointer, start position and
length parameters.
+- Improved :doc:`bugprone-unhandled-self-assignment
+ <clang-tidy/checks/bugprone/bugprone-unhandled-self-assignment` check
+ by adding an additional matcher that generalizes the copy-and-swap idiom
+ pattern detection. The checker now properly recognizes copy-and-swap
+ implementations that use "extended" copy/move constructors.
+
- Improved :doc:`bugprone-unchecked-optional-access
<clang-tidy/checks/bugprone/unchecked-optional-access>` fixing false
positives from smart pointer accessors repeated in checking ``has_value``
>From 4b84a6ae3ab2df97bc7bdc80f24d2ba9cd5791a6 Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Mon, 7 Jul 2025 11:30:31 +0300
Subject: [PATCH 5/7] Update ReleaseNotes.rst
---
clang-tools-extra/docs/ReleaseNotes.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9ee23710d8f4b..33e17946eb5c8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -190,10 +190,10 @@ Changes in existing checks
length parameters.
- Improved :doc:`bugprone-unhandled-self-assignment
- <clang-tidy/checks/bugprone/bugprone-unhandled-self-assignment` check
- by adding an additional matcher that generalizes the copy-and-swap idiom
- pattern detection. The checker now properly recognizes copy-and-swap
- implementations that use "extended" copy/move constructors.
+ <clang-tidy/checks/bugprone/unhandled-self-assignment` check by adding
+ an additional matcher that generalizes the copy-and-swap idiom pattern
+ detection. The checker now properly recognizes copy-and-swap implementations
+ that use "extended" copy/move constructors.
- Improved :doc:`bugprone-unchecked-optional-access
<clang-tidy/checks/bugprone/unchecked-optional-access>` fixing false
>From 6f4ec4ad355cb64c327c2748fa3aee082a23c294 Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Mon, 7 Jul 2025 12:03:45 +0300
Subject: [PATCH 6/7] Update ReleaseNotes.rst (typo fix)
---
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 33e17946eb5c8..aab36269413d6 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -190,7 +190,7 @@ Changes in existing checks
length parameters.
- Improved :doc:`bugprone-unhandled-self-assignment
- <clang-tidy/checks/bugprone/unhandled-self-assignment` check by adding
+ <clang-tidy/checks/bugprone/unhandled-self-assignment>` check by adding
an additional matcher that generalizes the copy-and-swap idiom pattern
detection. The checker now properly recognizes copy-and-swap implementations
that use "extended" copy/move constructors.
>From 7d5e9f5e3ca23ce4a525bd83bbbe8a41bb6a99cd Mon Sep 17 00:00:00 2001
From: Andrey Karlov <dein.negativ at gmail.com>
Date: Mon, 14 Jul 2025 14:03:48 +0300
Subject: [PATCH 7/7] Adressing PR questions
---
.../bugprone/UnhandledSelfAssignmentCheck.cpp | 22 ++++++++++++-------
clang-tools-extra/docs/ReleaseNotes.rst | 11 +++++-----
2 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
index 69f02ee37bba5..c4c4267545b59 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnhandledSelfAssignmentCheck.cpp
@@ -76,14 +76,20 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
// return *this;
// }
const auto HasCopyAndSwap = cxxMethodDecl(
- ofClass(cxxRecordDecl(unless(hasAncestor(classTemplateDecl())))),
- hasDescendant(
- stmt(hasDescendant(
- varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))))
- .bind("tmp_var")),
- hasDescendant(callExpr(callee(functionDecl(hasName("swap"))),
- hasAnyArgument(declRefExpr(to(varDecl(
- equalsBoundNode("tmp_var"))))))))));
+ ofClass(cxxRecordDecl()),
+ hasBody(compoundStmt(
+ hasDescendant(
+ varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))))
+ .bind("tmp_var")),
+ hasDescendant(stmt(anyOf(
+ cxxMemberCallExpr(hasArgument(
+ 0, declRefExpr(to(varDecl(equalsBoundNode("tmp_var")))))),
+ callExpr(
+ callee(functionDecl(hasName("swap"))), argumentCountIs(2),
+ hasAnyArgument(
+ declRefExpr(to(varDecl(equalsBoundNode("tmp_var"))))),
+ hasAnyArgument(unaryOperator(has(cxxThisExpr()),
+ hasOperatorName("*"))))))))));
DeclarationMatcher AdditionalMatcher = cxxMethodDecl();
if (WarnOnlyIfThisHasSuspiciousField) {
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 2162146923b2b..433febebc3388 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -205,12 +205,6 @@ Changes in existing checks
calls of ``std::string`` constructor with char pointer, start position and
length parameters.
-- Improved :doc:`bugprone-unhandled-self-assignment
- <clang-tidy/checks/bugprone/unhandled-self-assignment>` check by adding
- an additional matcher that generalizes the copy-and-swap idiom pattern
- detection. The checker now properly recognizes copy-and-swap implementations
- that use "extended" copy/move constructors.
-
- Improved :doc:`bugprone-unchecked-optional-access
<clang-tidy/checks/bugprone/unchecked-optional-access>` fixing false
positives from smart pointer accessors repeated in checking ``has_value``
@@ -218,6 +212,11 @@ Changes in existing checks
no longer be needed and will be removed. Also fixing false positive from
const reference accessors to objects containing optional member.
+- Improved :doc:`bugprone-unhandled-self-assignment
+ <clang-tidy/checks/bugprone/unhandled-self-assignment>` check by adding
+ an additional matcher that generalizes the copy-and-swap idiom pattern
+ detection.
+
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
More information about the cfe-commits
mailing list