818 lines · cpp
1// RUN: %check_clang_tidy %s bugprone-unhandled-self-assignment %t -- -- -fno-delayed-template-parsing2 3namespace std {4 5template <class T>6void swap(T &x, T &y) {7}8 9template <class T>10T &&move(T &x) {11}12 13template <typename T> class default_delete {};14 15template <class T, typename Deleter = std::default_delete<T>>16class unique_ptr {17};18 19template <class T>20class shared_ptr {21};22 23template <class T>24class weak_ptr {25};26 27template <class T>28class auto_ptr {29};30 31namespace pmr {32 template <typename TYPE = void>33 class allocator {};34}35 36struct allocator_arg_t {} allocator_arg;37 38} // namespace std39 40void assert(int expression){};41 42///////////////////////////////////////////////////////////////////43/// Test cases correctly caught by the check.44 45class PtrField {46public:47 PtrField &operator=(const PtrField &object);48 49private:50 int *p;51};52 53PtrField &PtrField::operator=(const PtrField &object) {54 // CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]55 // ...56 return *this;57}58 59// Class with an inline operator definition.60class InlineDefinition {61public:62 InlineDefinition &operator=(const InlineDefinition &object) {63 // CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]64 // ...65 return *this;66 }67 68private:69 int *p;70};71 72class UniquePtrField {73public:74 UniquePtrField &operator=(const UniquePtrField &object) {75 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]76 // ...77 return *this;78 }79 80private:81 std::unique_ptr<int> p;82};83 84class SharedPtrField {85public:86 SharedPtrField &operator=(const SharedPtrField &object) {87 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]88 // ...89 return *this;90 }91 92private:93 std::shared_ptr<int> p;94};95 96class WeakPtrField {97public:98 WeakPtrField &operator=(const WeakPtrField &object) {99 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]100 // ...101 return *this;102 }103 104private:105 std::weak_ptr<int> p;106};107 108class AutoPtrField {109public:110 AutoPtrField &operator=(const AutoPtrField &object) {111 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]112 // ...113 return *this;114 }115 116private:117 std::auto_ptr<int> p;118};119 120// Class with C array field.121class CArrayField {122public:123 CArrayField &operator=(const CArrayField &object) {124 // CHECK-MESSAGES: [[@LINE-1]]:16: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]125 // ...126 return *this;127 }128 129private:130 int array[256];131};132 133// Make sure to not ignore cases when the operator definition calls134// a copy constructor of another class.135class CopyConstruct {136public:137 CopyConstruct &operator=(const CopyConstruct &object) {138 // CHECK-MESSAGES: [[@LINE-1]]:18: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]139 WeakPtrField a;140 WeakPtrField b(a);141 // ...142 return *this;143 }144 145private:146 int *p;147};148 149// Make sure to not ignore cases when the operator definition calls150// a copy assignment operator of another class.151class AssignOperator {152public:153 AssignOperator &operator=(const AssignOperator &object) {154 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]155 a.operator=(object.a);156 // ...157 return *this;158 }159 160private:161 int *p;162 WeakPtrField a;163};164 165class NotSelfCheck {166public:167 NotSelfCheck &operator=(const NotSelfCheck &object) {168 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]169 if (&object == this->doSomething()) {170 // ...171 }172 return *this;173 }174 175 void *doSomething() {176 return p;177 }178 179private:180 int *p;181};182 183template <class T>184class TemplatePtrField {185public:186 TemplatePtrField<T> &operator=(const TemplatePtrField<T> &object) {187 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]188 // ...189 return *this;190 }191 192private:193 T *p;194};195 196template <class T>197class TemplateCArrayField {198public:199 TemplateCArrayField<T> &operator=(const TemplateCArrayField<T> &object) {200 // CHECK-MESSAGES: [[@LINE-1]]:27: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]201 // ...202 return *this;203 }204 205private:206 T p[256];207};208 209// Other template class's constructor is called inside a declaration.210template <class T>211class WrongTemplateCopyAndMove {212public:213 WrongTemplateCopyAndMove<T> &operator=(const WrongTemplateCopyAndMove<T> &object) {214 // CHECK-MESSAGES: [[@LINE-1]]:32: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]215 TemplatePtrField<T> temp;216 TemplatePtrField<T> temp2(temp);217 return *this;218 }219 220private:221 T *p;222};223 224// https://bugs.llvm.org/show_bug.cgi?id=44499225class Foo2;226template <int a>227bool operator!=(Foo2 &, Foo2 &) {228 class Bar2 {229 Bar2 &operator=(const Bar2 &other) {230 // CHECK-MESSAGES: [[@LINE-1]]:11: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]231 p = other.p;232 return *this;233 }234 235 int *p;236 };237}238 239// Missing call to `swap` function240class AllocatorAwareClassNoSwap {241 // pointer member to trigger bugprone-unhandled-self-assignment242 void *foo = nullptr;243 244 public:245 using allocator_type = std::pmr::allocator<>;246 247 AllocatorAwareClassNoSwap(const AllocatorAwareClassNoSwap& other) {248 }249 250 AllocatorAwareClassNoSwap(const AllocatorAwareClassNoSwap& other, const allocator_type& alloc) {251 }252 253 AllocatorAwareClassNoSwap& operator=(const AllocatorAwareClassNoSwap& other) {254 // CHECK-MESSAGES: [[@LINE-1]]:32: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]255 AllocatorAwareClassNoSwap tmp(other, get_allocator());256 return *this;257 }258 259 allocator_type get_allocator() const {260 return allocator_type();261 }262};263 264// "Wrong" type is passed to member `swap` function265class AllocatorAwareClassSwapWrongArgType {266 // pointer member to trigger bugprone-unhandled-self-assignment267 void *foo = nullptr;268 269 public:270 using allocator_type = std::pmr::allocator<>;271 272 AllocatorAwareClassSwapWrongArgType(const AllocatorAwareClassSwapWrongArgType& other) {273 }274 275 AllocatorAwareClassSwapWrongArgType(const AllocatorAwareClassSwapWrongArgType& other, const allocator_type& alloc) {276 }277 278 AllocatorAwareClassSwapWrongArgType& operator=(const AllocatorAwareClassSwapWrongArgType& other) {279 // CHECK-MESSAGES: [[@LINE-1]]:42: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]280 int tmp = 0;281 swap(tmp);282 283 return *this;284 }285 286 void swap(int&) noexcept {287 }288 289 allocator_type get_allocator() const {290 return allocator_type();291 }292};293 294 295// Making ADL `swap` call but with wrong argument type296class AllocatorAwareClassAdlSwapWrongArgType {297 // pointer member to trigger bugprone-unhandled-self-assignment298 void *foo = nullptr;299 300 public:301 using allocator_type = std::pmr::allocator<>;302 303 AllocatorAwareClassAdlSwapWrongArgType(const AllocatorAwareClassAdlSwapWrongArgType& other) {304 }305 306 AllocatorAwareClassAdlSwapWrongArgType(const AllocatorAwareClassAdlSwapWrongArgType& other, const allocator_type& alloc) {307 }308 309 AllocatorAwareClassAdlSwapWrongArgType& operator=(const AllocatorAwareClassAdlSwapWrongArgType& other) {310 // CHECK-MESSAGES: [[@LINE-1]]:45: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]311 int tmp = 0;312 swap(*this, tmp);313 314 return *this;315 }316 317 allocator_type get_allocator() const {318 return allocator_type();319 }320 321 friend void swap(AllocatorAwareClassAdlSwapWrongArgType&, int&) {322 }323};324 325// `this` isn't passed to `swap`326class AllocatorAwareClassAdlSwapWrongArgs {327 // pointer member to trigger bugprone-unhandled-self-assignment328 void *foo = nullptr;329 330 public:331 using allocator_type = std::pmr::allocator<>;332 333 AllocatorAwareClassAdlSwapWrongArgs(const AllocatorAwareClassAdlSwapWrongArgs& other) {334 }335 336 AllocatorAwareClassAdlSwapWrongArgs(const AllocatorAwareClassAdlSwapWrongArgs& other, const allocator_type& alloc) {337 }338 339 AllocatorAwareClassAdlSwapWrongArgs& operator=(const AllocatorAwareClassAdlSwapWrongArgs& other) {340 // CHECK-MESSAGES: [[@LINE-1]]:42: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]341 AllocatorAwareClassAdlSwapWrongArgs tmp1(other, get_allocator());342 AllocatorAwareClassAdlSwapWrongArgs tmp2(*this, get_allocator());343 swap(tmp1, tmp2);344 return *this;345 }346 347 allocator_type get_allocator() const {348 return allocator_type();349 }350 351 friend void swap(AllocatorAwareClassAdlSwapWrongArgs&, AllocatorAwareClassAdlSwapWrongArgs&) {352 }353};354 355///////////////////////////////////////////////////////////////////356/// Test cases correctly ignored by the check.357 358// Self-assignment is checked using the equality operator.359class SelfCheck1 {360public:361 SelfCheck1 &operator=(const SelfCheck1 &object) {362 if (this == &object)363 return *this;364 // ...365 return *this;366 }367 368private:369 int *p;370};371 372class SelfCheck2 {373public:374 SelfCheck2 &operator=(const SelfCheck2 &object) {375 if (&object == this)376 return *this;377 // ...378 return *this;379 }380 381private:382 int *p;383};384 385// Self-assignment is checked using the inequality operator.386class SelfCheck3 {387public:388 SelfCheck3 &operator=(const SelfCheck3 &object) {389 if (this != &object) {390 // ...391 }392 return *this;393 }394 395private:396 int *p;397};398 399class SelfCheck4 {400public:401 SelfCheck4 &operator=(const SelfCheck4 &object) {402 if (&object != this) {403 // ...404 }405 return *this;406 }407 408private:409 int *p;410};411 412template <class T>413class TemplateSelfCheck {414public:415 TemplateSelfCheck<T> &operator=(const TemplateSelfCheck<T> &object) {416 if (&object != this) {417 // ...418 }419 return *this;420 }421 422private:423 T *p;424};425 426// https://bugs.llvm.org/show_bug.cgi?id=44499427class Foo;428template <int a>429bool operator!=(Foo &, Foo &) {430 class Bar {431 Bar &operator=(const Bar &other) {432 if (this != &other) {433 }434 return *this;435 }436 437 int *p;438 };439}440 441// There is no warning if the copy assignment operator gets the object by value.442class PassedByValue {443public:444 PassedByValue &operator=(PassedByValue object) {445 // ...446 return *this;447 }448 449private:450 int *p;451};452 453// User-defined swap method calling std::swap inside.454class CopyAndSwap1 {455public:456 CopyAndSwap1 &operator=(const CopyAndSwap1 &object) {457 CopyAndSwap1 temp(object);458 doSwap(temp);459 return *this;460 }461 462private:463 int *p;464 465 void doSwap(CopyAndSwap1 &object) {466 using std::swap;467 swap(p, object.p);468 }469};470 471// User-defined swap method used with passed-by-value parameter.472class CopyAndSwap2 {473public:474 CopyAndSwap2 &operator=(CopyAndSwap2 object) {475 doSwap(object);476 return *this;477 }478 479private:480 int *p;481 482 void doSwap(CopyAndSwap2 &object) {483 using std::swap;484 swap(p, object.p);485 }486};487 488// Copy-and-swap method is used but without creating a separate method for it.489class CopyAndSwap3 {490public:491 CopyAndSwap3 &operator=(const CopyAndSwap3 &object) {492 CopyAndSwap3 temp(object);493 std::swap(p, temp.p);494 return *this;495 }496 497private:498 int *p;499};500 501template <class T>502class TemplateCopyAndSwap {503public:504 TemplateCopyAndSwap<T> &operator=(const TemplateCopyAndSwap<T> &object) {505 TemplateCopyAndSwap<T> temp(object);506 std::swap(p, temp.p);507 return *this;508 }509 510private:511 T *p;512};513 514// Move semantics is used on a temporary copy of the object.515class CopyAndMove1 {516public:517 CopyAndMove1 &operator=(const CopyAndMove1 &object) {518 CopyAndMove1 temp(object);519 *this = std::move(temp);520 return *this;521 }522 523private:524 int *p;525};526 527// There is no local variable for the temporary copy.528class CopyAndMove2 {529public:530 CopyAndMove2 &operator=(const CopyAndMove2 &object) {531 *this = CopyAndMove2(object);532 return *this;533 }534 535private:536 int *p;537};538 539template <class T>540class TemplateCopyAndMove {541public:542 TemplateCopyAndMove<T> &operator=(const TemplateCopyAndMove<T> &object) {543 TemplateCopyAndMove<T> temp(object);544 *this = std::move(temp);545 return *this;546 }547 548private:549 T *p;550};551 552// There is no local variable for the temporary copy.553template <class T>554class TemplateCopyAndMove2 {555public:556 TemplateCopyAndMove2<T> &operator=(const TemplateCopyAndMove2<T> &object) {557 *this = std::move(TemplateCopyAndMove2<T>(object));558 return *this;559 }560 561private:562 T *p;563};564 565// We should not catch move assignment operators.566class MoveAssignOperator {567public:568 MoveAssignOperator &operator=(MoveAssignOperator &&object) {569 // ...570 return *this;571 }572 573private:574 int *p;575};576 577// We ignore copy assignment operators without user-defined implementation.578class DefaultOperator {579public:580 DefaultOperator &operator=(const DefaultOperator &object) = default;581 582private:583 int *p;584};585 586class DeletedOperator {587public:588 DeletedOperator &operator=(const DefaultOperator &object) = delete;589 590private:591 int *p;592};593 594class ImplicitOperator {595private:596 int *p;597};598 599// Check ignores those classes which has no any pointer or array field.600class TrivialFields {601public:602 TrivialFields &operator=(const TrivialFields &object) {603 // ...604 return *this;605 }606 607private:608 int m;609 float f;610 double d;611 bool b;612};613 614// There is no warning when the class calls another assignment operator on 'this'615// inside the copy assignment operator's definition.616class AssignIsForwarded {617public:618 AssignIsForwarded &operator=(const AssignIsForwarded &object) {619 operator=(object.p);620 return *this;621 }622 623 AssignIsForwarded &operator=(int *pp) {624 if (p != pp) {625 delete p;626 p = new int(*pp);627 }628 return *this;629 }630 631private:632 int *p;633};634 635// Assertion is a valid way to say that self-assignment is not expected to happen.636class AssertGuard {637public:638 AssertGuard &operator=(const AssertGuard &object) {639 assert(this != &object);640 // ...641 return *this;642 }643 644private:645 int *p;646};647 648// Make sure we don't catch this operator=() as a copy assignment operator.649// Note that RHS has swapped template arguments.650template <typename Ty, typename Uy>651class NotACopyAssignmentOperator {652 Ty *Ptr1;653 Uy *Ptr2;654 655public:656 NotACopyAssignmentOperator& operator=(const NotACopyAssignmentOperator<Uy, Ty> &RHS) {657 Ptr1 = RHS.getUy();658 Ptr2 = RHS.getTy();659 return *this;660 }661 662 Ty *getTy() const { return Ptr1; }663 Uy *getUy() const { return Ptr2; }664};665 666// Support "extended" copy/move constructors667class AllocatorAwareClass {668 // pointer member to trigger bugprone-unhandled-self-assignment669 void *foo = nullptr;670 671 public:672 using allocator_type = std::pmr::allocator<>;673 674 AllocatorAwareClass(const AllocatorAwareClass& other) {675 }676 677 AllocatorAwareClass(const AllocatorAwareClass& other, const allocator_type& alloc) {678 }679 680 AllocatorAwareClass& operator=(const AllocatorAwareClass& other) {681 AllocatorAwareClass tmp(other, get_allocator());682 swap(tmp);683 return *this;684 }685 686 void swap(AllocatorAwareClass& other) noexcept {687 }688 689 allocator_type get_allocator() const {690 return allocator_type();691 }692};693 694// Support "extended" copy/move constructors + std::swap695class AllocatorAwareClassStdSwap {696 // pointer member to trigger bugprone-unhandled-self-assignment697 void *foo = nullptr;698 699 public:700 using allocator_type = std::pmr::allocator<>;701 702 AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other) {703 }704 705 AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other, const allocator_type& alloc) {706 }707 708 AllocatorAwareClassStdSwap& operator=(const AllocatorAwareClassStdSwap& other) {709 using std::swap;710 711 AllocatorAwareClassStdSwap tmp(other, get_allocator());712 swap(*this, tmp);713 return *this;714 }715 716 allocator_type get_allocator() const {717 return allocator_type();718 }719};720 721// Support "extended" copy/move constructors + ADL swap722class AllocatorAwareClassAdlSwap {723 // pointer member to trigger bugprone-unhandled-self-assignment724 void *foo = nullptr;725 726 public:727 using allocator_type = std::pmr::allocator<>;728 729 AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other) {730 }731 732 AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other, const allocator_type& alloc) {733 }734 735 AllocatorAwareClassAdlSwap& operator=(const AllocatorAwareClassAdlSwap& other) {736 AllocatorAwareClassAdlSwap tmp(other, get_allocator());737 swap(*this, tmp);738 return *this;739 }740 741 allocator_type get_allocator() const {742 return allocator_type();743 }744 745 friend void swap(AllocatorAwareClassAdlSwap&, AllocatorAwareClassAdlSwap&) {746 }747};748 749///////////////////////////////////////////////////////////////////750/// Test cases which should be caught by the check.751 752// TODO: handle custom pointers.753template <class T>754class custom_ptr {755};756 757class CustomPtrField {758public:759 CustomPtrField &operator=(const CustomPtrField &object) {760 // ...761 return *this;762 }763 764private:765 custom_ptr<int> p;766};767 768/////////////////////////////////////////////////////////////////////////////////////////////////////769/// False positives: These are self-assignment safe, but they don't use any of the three patterns.770 771class ArrayCopy {772public:773 ArrayCopy &operator=(const ArrayCopy &object) {774 // CHECK-MESSAGES: [[@LINE-1]]:14: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]775 for (int i = 0; i < 256; i++)776 array[i] = object.array[i];777 return *this;778 }779 780private:781 int array[256];782};783 784class GetterSetter {785public:786 GetterSetter &operator=(const GetterSetter &object) {787 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]788 setValue(object.getValue());789 return *this;790 }791 792 int *getValue() const { return value; }793 794 void setValue(int *newPtr) {795 int *pTmp(newPtr ? new int(*newPtr) : nullptr);796 std::swap(value, pTmp);797 delete pTmp;798 }799 800private:801 int *value;802};803 804class CustomSelfCheck {805public:806 CustomSelfCheck &operator=(const CustomSelfCheck &object) {807 // CHECK-MESSAGES: [[@LINE-1]]:20: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]808 if (index != object.index) {809 // ...810 }811 return *this;812 }813 814private:815 int *value;816 int index;817};818