xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp (revision c27483763c883ad268ba61249d1c0274a719e2d6)
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