1 // RUN: %check_clang_tidy %s bugprone-unhandled-self-assignment %t -- -- -fno-delayed-template-parsing 2 3 namespace std { 4 5 template <class T> 6 void swap(T &x, T &y) { 7 } 8 9 template <class T> 10 T &&move(T &x) { 11 } 12 13 template <typename T> class default_delete {}; 14 15 template <class T, typename Deleter = std::default_delete<T>> 16 class unique_ptr { 17 }; 18 19 template <class T> 20 class shared_ptr { 21 }; 22 23 template <class T> 24 class weak_ptr { 25 }; 26 27 template <class T> 28 class auto_ptr { 29 }; 30 31 } // namespace std 32 33 void assert(int expression){}; 34 35 /////////////////////////////////////////////////////////////////// 36 /// Test cases correctly caught by the check. 37 38 class PtrField { 39 public: 40 PtrField &operator=(const PtrField &object); 41 42 private: 43 int *p; 44 }; 45 46 PtrField &PtrField::operator=(const PtrField &object) { 47 // CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 48 // ... 49 return *this; 50 } 51 52 // Class with an inline operator definition. 53 class InlineDefinition { 54 public: 55 InlineDefinition &operator=(const InlineDefinition &object) { 56 // CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 57 // ... 58 return *this; 59 } 60 61 private: 62 int *p; 63 }; 64 65 class UniquePtrField { 66 public: 67 UniquePtrField &operator=(const UniquePtrField &object) { 68 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 69 // ... 70 return *this; 71 } 72 73 private: 74 std::unique_ptr<int> p; 75 }; 76 77 class SharedPtrField { 78 public: 79 SharedPtrField &operator=(const SharedPtrField &object) { 80 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 81 // ... 82 return *this; 83 } 84 85 private: 86 std::shared_ptr<int> p; 87 }; 88 89 class WeakPtrField { 90 public: 91 WeakPtrField &operator=(const WeakPtrField &object) { 92 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 93 // ... 94 return *this; 95 } 96 97 private: 98 std::weak_ptr<int> p; 99 }; 100 101 class AutoPtrField { 102 public: 103 AutoPtrField &operator=(const AutoPtrField &object) { 104 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 105 // ... 106 return *this; 107 } 108 109 private: 110 std::auto_ptr<int> p; 111 }; 112 113 // Class with C array field. 114 class CArrayField { 115 public: 116 CArrayField &operator=(const CArrayField &object) { 117 // CHECK-MESSAGES: [[@LINE-1]]:16: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 118 // ... 119 return *this; 120 } 121 122 private: 123 int array[256]; 124 }; 125 126 // Make sure to not ignore cases when the operator definition calls 127 // a copy constructor of another class. 128 class CopyConstruct { 129 public: 130 CopyConstruct &operator=(const CopyConstruct &object) { 131 // CHECK-MESSAGES: [[@LINE-1]]:18: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 132 WeakPtrField a; 133 WeakPtrField b(a); 134 // ... 135 return *this; 136 } 137 138 private: 139 int *p; 140 }; 141 142 // Make sure to not ignore cases when the operator definition calls 143 // a copy assignment operator of another class. 144 class AssignOperator { 145 public: 146 AssignOperator &operator=(const AssignOperator &object) { 147 // CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 148 a.operator=(object.a); 149 // ... 150 return *this; 151 } 152 153 private: 154 int *p; 155 WeakPtrField a; 156 }; 157 158 class NotSelfCheck { 159 public: 160 NotSelfCheck &operator=(const NotSelfCheck &object) { 161 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 162 if (&object == this->doSomething()) { 163 // ... 164 } 165 return *this; 166 } 167 168 void *doSomething() { 169 return p; 170 } 171 172 private: 173 int *p; 174 }; 175 176 template <class T> 177 class TemplatePtrField { 178 public: 179 TemplatePtrField<T> &operator=(const TemplatePtrField<T> &object) { 180 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 181 // ... 182 return *this; 183 } 184 185 private: 186 T *p; 187 }; 188 189 template <class T> 190 class TemplateCArrayField { 191 public: 192 TemplateCArrayField<T> &operator=(const TemplateCArrayField<T> &object) { 193 // CHECK-MESSAGES: [[@LINE-1]]:27: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 194 // ... 195 return *this; 196 } 197 198 private: 199 T p[256]; 200 }; 201 202 // Other template class's constructor is called inside a declaration. 203 template <class T> 204 class WrongTemplateCopyAndMove { 205 public: 206 WrongTemplateCopyAndMove<T> &operator=(const WrongTemplateCopyAndMove<T> &object) { 207 // CHECK-MESSAGES: [[@LINE-1]]:32: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 208 TemplatePtrField<T> temp; 209 TemplatePtrField<T> temp2(temp); 210 return *this; 211 } 212 213 private: 214 T *p; 215 }; 216 217 // https://bugs.llvm.org/show_bug.cgi?id=44499 218 class Foo2; 219 template <int a> 220 bool operator!=(Foo2 &, Foo2 &) { 221 class Bar2 { 222 Bar2 &operator=(const Bar2 &other) { 223 // CHECK-MESSAGES: [[@LINE-1]]:11: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 224 p = other.p; 225 return *this; 226 } 227 228 int *p; 229 }; 230 } 231 232 /////////////////////////////////////////////////////////////////// 233 /// Test cases correctly ignored by the check. 234 235 // Self-assignment is checked using the equality operator. 236 class SelfCheck1 { 237 public: 238 SelfCheck1 &operator=(const SelfCheck1 &object) { 239 if (this == &object) 240 return *this; 241 // ... 242 return *this; 243 } 244 245 private: 246 int *p; 247 }; 248 249 class SelfCheck2 { 250 public: 251 SelfCheck2 &operator=(const SelfCheck2 &object) { 252 if (&object == this) 253 return *this; 254 // ... 255 return *this; 256 } 257 258 private: 259 int *p; 260 }; 261 262 // Self-assignment is checked using the inequality operator. 263 class SelfCheck3 { 264 public: 265 SelfCheck3 &operator=(const SelfCheck3 &object) { 266 if (this != &object) { 267 // ... 268 } 269 return *this; 270 } 271 272 private: 273 int *p; 274 }; 275 276 class SelfCheck4 { 277 public: 278 SelfCheck4 &operator=(const SelfCheck4 &object) { 279 if (&object != this) { 280 // ... 281 } 282 return *this; 283 } 284 285 private: 286 int *p; 287 }; 288 289 template <class T> 290 class TemplateSelfCheck { 291 public: 292 TemplateSelfCheck<T> &operator=(const TemplateSelfCheck<T> &object) { 293 if (&object != this) { 294 // ... 295 } 296 return *this; 297 } 298 299 private: 300 T *p; 301 }; 302 303 // https://bugs.llvm.org/show_bug.cgi?id=44499 304 class Foo; 305 template <int a> 306 bool operator!=(Foo &, Foo &) { 307 class Bar { 308 Bar &operator=(const Bar &other) { 309 if (this != &other) { 310 } 311 return *this; 312 } 313 314 int *p; 315 }; 316 } 317 318 // There is no warning if the copy assignment operator gets the object by value. 319 class PassedByValue { 320 public: 321 PassedByValue &operator=(PassedByValue object) { 322 // ... 323 return *this; 324 } 325 326 private: 327 int *p; 328 }; 329 330 // User-defined swap method calling std::swap inside. 331 class CopyAndSwap1 { 332 public: 333 CopyAndSwap1 &operator=(const CopyAndSwap1 &object) { 334 CopyAndSwap1 temp(object); 335 doSwap(temp); 336 return *this; 337 } 338 339 private: 340 int *p; 341 342 void doSwap(CopyAndSwap1 &object) { 343 using std::swap; 344 swap(p, object.p); 345 } 346 }; 347 348 // User-defined swap method used with passed-by-value parameter. 349 class CopyAndSwap2 { 350 public: 351 CopyAndSwap2 &operator=(CopyAndSwap2 object) { 352 doSwap(object); 353 return *this; 354 } 355 356 private: 357 int *p; 358 359 void doSwap(CopyAndSwap2 &object) { 360 using std::swap; 361 swap(p, object.p); 362 } 363 }; 364 365 // Copy-and-swap method is used but without creating a separate method for it. 366 class CopyAndSwap3 { 367 public: 368 CopyAndSwap3 &operator=(const CopyAndSwap3 &object) { 369 CopyAndSwap3 temp(object); 370 std::swap(p, temp.p); 371 return *this; 372 } 373 374 private: 375 int *p; 376 }; 377 378 template <class T> 379 class TemplateCopyAndSwap { 380 public: 381 TemplateCopyAndSwap<T> &operator=(const TemplateCopyAndSwap<T> &object) { 382 TemplateCopyAndSwap<T> temp(object); 383 std::swap(p, temp.p); 384 return *this; 385 } 386 387 private: 388 T *p; 389 }; 390 391 // Move semantics is used on a temporary copy of the object. 392 class CopyAndMove1 { 393 public: 394 CopyAndMove1 &operator=(const CopyAndMove1 &object) { 395 CopyAndMove1 temp(object); 396 *this = std::move(temp); 397 return *this; 398 } 399 400 private: 401 int *p; 402 }; 403 404 // There is no local variable for the temporary copy. 405 class CopyAndMove2 { 406 public: 407 CopyAndMove2 &operator=(const CopyAndMove2 &object) { 408 *this = CopyAndMove2(object); 409 return *this; 410 } 411 412 private: 413 int *p; 414 }; 415 416 template <class T> 417 class TemplateCopyAndMove { 418 public: 419 TemplateCopyAndMove<T> &operator=(const TemplateCopyAndMove<T> &object) { 420 TemplateCopyAndMove<T> temp(object); 421 *this = std::move(temp); 422 return *this; 423 } 424 425 private: 426 T *p; 427 }; 428 429 // There is no local variable for the temporary copy. 430 template <class T> 431 class TemplateCopyAndMove2 { 432 public: 433 TemplateCopyAndMove2<T> &operator=(const TemplateCopyAndMove2<T> &object) { 434 *this = std::move(TemplateCopyAndMove2<T>(object)); 435 return *this; 436 } 437 438 private: 439 T *p; 440 }; 441 442 // We should not catch move assignment operators. 443 class MoveAssignOperator { 444 public: 445 MoveAssignOperator &operator=(MoveAssignOperator &&object) { 446 // ... 447 return *this; 448 } 449 450 private: 451 int *p; 452 }; 453 454 // We ignore copy assignment operators without user-defined implementation. 455 class DefaultOperator { 456 public: 457 DefaultOperator &operator=(const DefaultOperator &object) = default; 458 459 private: 460 int *p; 461 }; 462 463 class DeletedOperator { 464 public: 465 DeletedOperator &operator=(const DefaultOperator &object) = delete; 466 467 private: 468 int *p; 469 }; 470 471 class ImplicitOperator { 472 private: 473 int *p; 474 }; 475 476 // Check ignores those classes which has no any pointer or array field. 477 class TrivialFields { 478 public: 479 TrivialFields &operator=(const TrivialFields &object) { 480 // ... 481 return *this; 482 } 483 484 private: 485 int m; 486 float f; 487 double d; 488 bool b; 489 }; 490 491 // There is no warning when the class calls another assignment operator on 'this' 492 // inside the copy assignment operator's definition. 493 class AssignIsForwarded { 494 public: 495 AssignIsForwarded &operator=(const AssignIsForwarded &object) { 496 operator=(object.p); 497 return *this; 498 } 499 500 AssignIsForwarded &operator=(int *pp) { 501 if (p != pp) { 502 delete p; 503 p = new int(*pp); 504 } 505 return *this; 506 } 507 508 private: 509 int *p; 510 }; 511 512 // Assertion is a valid way to say that self-assignment is not expected to happen. 513 class AssertGuard { 514 public: 515 AssertGuard &operator=(const AssertGuard &object) { 516 assert(this != &object); 517 // ... 518 return *this; 519 } 520 521 private: 522 int *p; 523 }; 524 525 // Make sure we don't catch this operator=() as a copy assignment operator. 526 // Note that RHS has swapped template arguments. 527 template <typename Ty, typename Uy> 528 class NotACopyAssignmentOperator { 529 Ty *Ptr1; 530 Uy *Ptr2; 531 532 public: 533 NotACopyAssignmentOperator& operator=(const NotACopyAssignmentOperator<Uy, Ty> &RHS) { 534 Ptr1 = RHS.getUy(); 535 Ptr2 = RHS.getTy(); 536 return *this; 537 } 538 539 Ty *getTy() const { return Ptr1; } 540 Uy *getUy() const { return Ptr2; } 541 }; 542 543 /////////////////////////////////////////////////////////////////// 544 /// Test cases which should be caught by the check. 545 546 // TODO: handle custom pointers. 547 template <class T> 548 class custom_ptr { 549 }; 550 551 class CustomPtrField { 552 public: 553 CustomPtrField &operator=(const CustomPtrField &object) { 554 // ... 555 return *this; 556 } 557 558 private: 559 custom_ptr<int> p; 560 }; 561 562 ///////////////////////////////////////////////////////////////////////////////////////////////////// 563 /// False positives: These are self-assignment safe, but they don't use any of the three patterns. 564 565 class ArrayCopy { 566 public: 567 ArrayCopy &operator=(const ArrayCopy &object) { 568 // CHECK-MESSAGES: [[@LINE-1]]:14: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 569 for (int i = 0; i < 256; i++) 570 array[i] = object.array[i]; 571 return *this; 572 } 573 574 private: 575 int array[256]; 576 }; 577 578 class GetterSetter { 579 public: 580 GetterSetter &operator=(const GetterSetter &object) { 581 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 582 setValue(object.getValue()); 583 return *this; 584 } 585 586 int *getValue() const { return value; } 587 588 void setValue(int *newPtr) { 589 int *pTmp(newPtr ? new int(*newPtr) : nullptr); 590 std::swap(value, pTmp); 591 delete pTmp; 592 } 593 594 private: 595 int *value; 596 }; 597 598 class CustomSelfCheck { 599 public: 600 CustomSelfCheck &operator=(const CustomSelfCheck &object) { 601 // CHECK-MESSAGES: [[@LINE-1]]:20: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment] 602 if (index != object.index) { 603 // ... 604 } 605 return *this; 606 } 607 608 private: 609 int *value; 610 int index; 611 }; 612