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