1// RUN: %check_clang_tidy %s bugprone-unhandled-self-assignment %t -- -- -fno-delayed-template-parsing
2
3namespace std {
4
5template <class T>
6void swap(T &x, T &y) {
7}
8
9template <class T>
10T &&move(T &x) {
11}
12
13template <class T>
14class unique_ptr {
15};
16
17template <class T>
18class shared_ptr {
19};
20
21template <class T>
22class weak_ptr {
23};
24
25template <class T>
26class auto_ptr {
27};
28
29} // namespace std
30
31void assert(int expression){};
32
33///////////////////////////////////////////////////////////////////
34/// Test cases correctly caught by the check.
35
36class PtrField {
37public:
38 PtrField &operator=(const PtrField &object);
39
40private:
41 int *p;
42};
43
44PtrField &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.
51class InlineDefinition {
52public:
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
59private:
60 int *p;
61};
62
63class UniquePtrField {
64public:
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
71private:
72 std::unique_ptr<int> p;
73};
74
75class SharedPtrField {
76public:
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
83private:
84 std::shared_ptr<int> p;
85};
86
87class WeakPtrField {
88public:
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
95private:
96 std::weak_ptr<int> p;
97};
98
99class AutoPtrField {
100public:
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
107private:
108 std::auto_ptr<int> p;
109};
110
111// Class with C array field.
112class CArrayField {
113public:
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
120private:
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.
126class CopyConstruct {
127public:
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
136private:
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.
142class AssignOperator {
143public:
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: object.a);
147 // ...
148 return *this;
149 }
150
151private:
152 int *p;
153 WeakPtrField a;
154};
155
156class NotSelfCheck {
157public:
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
166 void *doSomething() {
167 return p;
168 }
169
170private:
171 int *p;
172};
173
174template <class T>
175class TemplatePtrField {
176public:
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
183private:
184 T *p;
185};
186
187template <class T>
188class TemplateCArrayField {
189public:
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
196private:
197 T p[256];
198};
199
200// Other template class's constructor is called inside a declaration.
201template <class T>
202class WrongTemplateCopyAndMove {
203public:
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
211private:
212 T *p;
213};
214
215// https://bugs.llvm.org/show_bug.cgi?id=44499
216class Foo2;
217template <int a>
218bool 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.
234class SelfCheck1 {
235public:
236 SelfCheck1 &operator=(const SelfCheck1 &object) {
237 if (this == &object)
238 return *this;
239 // ...
240 return *this;
241 }
242
243private:
244 int *p;
245};
246
247class SelfCheck2 {
248public:
249 SelfCheck2 &operator=(const SelfCheck2 &object) {
250 if (&object == this)
251 return *this;
252 // ...
253 return *this;
254 }
255
256private:
257 int *p;
258};
259
260// Self-assignment is checked using the inequality operator.
261class SelfCheck3 {
262public:
263 SelfCheck3 &operator=(const SelfCheck3 &object) {
264 if (this != &object) {
265 // ...
266 }
267 return *this;
268 }
269
270private:
271 int *p;
272};
273
274class SelfCheck4 {
275public:
276 SelfCheck4 &operator=(const SelfCheck4 &object) {
277 if (&object != this) {
278 // ...
279 }
280 return *this;
281 }
282
283private:
284 int *p;
285};
286
287template <class T>
288class TemplateSelfCheck {
289public:
290 TemplateSelfCheck<T> &operator=(const TemplateSelfCheck<T> &object) {
291 if (&object != this) {
292 // ...
293 }
294 return *this;
295 }
296
297private:
298 T *p;
299};
300
301// https://bugs.llvm.org/show_bug.cgi?id=44499
302class Foo;
303template <int a>
304bool 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.
317class PassedByValue {
318public:
319 PassedByValue &operator=(PassedByValue object) {
320 // ...
321 return *this;
322 }
323
324private:
325 int *p;
326};
327
328// User-defined swap method calling std::swap inside.
329class CopyAndSwap1 {
330public:
331 CopyAndSwap1 &operator=(const CopyAndSwap1 &object) {
332 CopyAndSwap1 temp(object);
333 doSwap(object&: temp);
334 return *this;
335 }
336
337private:
338 int *p;
339
340 void doSwap(CopyAndSwap1 &object) {
341 using std::swap;
342 swap(x&: p, y&: object.p);
343 }
344};
345
346// User-defined swap method used with passed-by-value parameter.
347class CopyAndSwap2 {
348public:
349 CopyAndSwap2 &operator=(CopyAndSwap2 object) {
350 doSwap(object);
351 return *this;
352 }
353
354private:
355 int *p;
356
357 void doSwap(CopyAndSwap2 &object) {
358 using std::swap;
359 swap(x&: p, y&: object.p);
360 }
361};
362
363// Copy-and-swap method is used but without creating a separate method for it.
364class CopyAndSwap3 {
365public:
366 CopyAndSwap3 &operator=(const CopyAndSwap3 &object) {
367 CopyAndSwap3 temp(object);
368 std::swap(x&: p, y&: temp.p);
369 return *this;
370 }
371
372private:
373 int *p;
374};
375
376template <class T>
377class TemplateCopyAndSwap {
378public:
379 TemplateCopyAndSwap<T> &operator=(const TemplateCopyAndSwap<T> &object) {
380 TemplateCopyAndSwap<T> temp(object);
381 std::swap(p, temp.p);
382 return *this;
383 }
384
385private:
386 T *p;
387};
388
389// Move semantics is used on a temporary copy of the object.
390class CopyAndMove1 {
391public:
392 CopyAndMove1 &operator=(const CopyAndMove1 &object) {
393 CopyAndMove1 temp(object);
394 *this = std::move(temp);
395 return *this;
396 }
397
398private:
399 int *p;
400};
401
402// There is no local variable for the temporary copy.
403class CopyAndMove2 {
404public:
405 CopyAndMove2 &operator=(const CopyAndMove2 &object) {
406 *this = CopyAndMove2(object);
407 return *this;
408 }
409
410private:
411 int *p;
412};
413
414template <class T>
415class TemplateCopyAndMove {
416public:
417 TemplateCopyAndMove<T> &operator=(const TemplateCopyAndMove<T> &object) {
418 TemplateCopyAndMove<T> temp(object);
419 *this = std::move(temp);
420 return *this;
421 }
422
423private:
424 T *p;
425};
426
427// There is no local variable for the temporary copy.
428template <class T>
429class TemplateCopyAndMove2 {
430public:
431 TemplateCopyAndMove2<T> &operator=(const TemplateCopyAndMove2<T> &object) {
432 *this = std::move(TemplateCopyAndMove2<T>(object));
433 return *this;
434 }
435
436private:
437 T *p;
438};
439
440// We should not catch move assignment operators.
441class MoveAssignOperator {
442public:
443 MoveAssignOperator &operator=(MoveAssignOperator &&object) {
444 // ...
445 return *this;
446 }
447
448private:
449 int *p;
450};
451
452// We ignore copy assignment operators without user-defined implementation.
453class DefaultOperator {
454public:
455 DefaultOperator &operator=(const DefaultOperator &object) = default;
456
457private:
458 int *p;
459};
460
461class DeletedOperator {
462public:
463 DeletedOperator &operator=(const DefaultOperator &object) = delete;
464
465private:
466 int *p;
467};
468
469class ImplicitOperator {
470private:
471 int *p;
472};
473
474// Check ignores those classes which has no any pointer or array field.
475class TrivialFields {
476public:
477 TrivialFields &operator=(const TrivialFields &object) {
478 // ...
479 return *this;
480 }
481
482private:
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.
491class AssignIsForwarded {
492public:
493 AssignIsForwarded &operator=(const AssignIsForwarded &object) {
494 operator=(pp: object.p);
495 return *this;
496 }
497
498 AssignIsForwarded &operator=(int *pp) {
499 if (p != pp) {
500 delete p;
501 p = new int(*pp);
502 }
503 return *this;
504 }
505
506private:
507 int *p;
508};
509
510// Assertion is a valid way to say that self-assignment is not expected to happen.
511class AssertGuard {
512public:
513 AssertGuard &operator=(const AssertGuard &object) {
514 assert(expression: this != &object);
515 // ...
516 return *this;
517 }
518
519private:
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.
525template <typename Ty, typename Uy>
526class NotACopyAssignmentOperator {
527 Ty *Ptr1;
528 Uy *Ptr2;
529
530public:
531 NotACopyAssignmentOperator& operator=(const NotACopyAssignmentOperator<Uy, Ty> &RHS) {
532 Ptr1 = RHS.getUy();
533 Ptr2 = RHS.getTy();
534 return *this;
535 }
536
537 Ty *getTy() const { return Ptr1; }
538 Uy *getUy() const { return Ptr2; }
539};
540
541///////////////////////////////////////////////////////////////////
542/// Test cases which should be caught by the check.
543
544// TODO: handle custom pointers.
545template <class T>
546class custom_ptr {
547};
548
549class CustomPtrField {
550public:
551 CustomPtrField &operator=(const CustomPtrField &object) {
552 // ...
553 return *this;
554 }
555
556private:
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
563class ArrayCopy {
564public:
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
572private:
573 int array[256];
574};
575
576class GetterSetter {
577public:
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
584 int *getValue() const { return value; }
585
586 void setValue(int *newPtr) {
587 int *pTmp(newPtr ? new int(*newPtr) : nullptr);
588 std::swap(x&: value, y&: pTmp);
589 delete pTmp;
590 }
591
592private:
593 int *value;
594};
595
596class CustomSelfCheck {
597public:
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
606private:
607 int *value;
608 int index;
609};
610

source code of clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp