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