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.nega...@gmail.com> Date: Fri, 4 Jul 2025 17:13:20 +0300 Subject: [PATCH 1/3] 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.nega...@gmail.com> Date: Fri, 4 Jul 2025 17:19:02 +0300 Subject: [PATCH 2/3] 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.nega...@gmail.com> Date: Mon, 7 Jul 2025 10:43:13 +0300 Subject: [PATCH 3/3] 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( _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits