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 <typename T> class default_delete {};
14
15template <class T, typename Deleter = std::default_delete<T>>
16class unique_ptr {
17};
18
19template <class T>
20class shared_ptr {
21};
22
23template <class T>
24class weak_ptr {
25};
26
27template <class T>
28class auto_ptr {
29};
30
31} // namespace std
32
33void assert(int expression){};
34
35///////////////////////////////////////////////////////////////////
36/// Test cases correctly caught by the check.
37
38class PtrField {
39public:
40 PtrField &operator=(const PtrField &object);
41
42private:
43 int *p;
44};
45
46PtrField &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.
53class InlineDefinition {
54public:
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
61private:
62 int *p;
63};
64
65class UniquePtrField {
66public:
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
73private:
74 std::unique_ptr<int> p;
75};
76
77class SharedPtrField {
78public:
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
85private:
86 std::shared_ptr<int> p;
87};
88
89class WeakPtrField {
90public:
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
97private:
98 std::weak_ptr<int> p;
99};
100
101class AutoPtrField {
102public:
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
109private:
110 std::auto_ptr<int> p;
111};
112
113// Class with C array field.
114class CArrayField {
115public:
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
122private:
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.
128class CopyConstruct {
129public:
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
138private:
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.
144class AssignOperator {
145public:
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: object.a);
149 // ...
150 return *this;
151 }
152
153private:
154 int *p;
155 WeakPtrField a;
156};
157
158class NotSelfCheck {
159public:
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
172private:
173 int *p;
174};
175
176template <class T>
177class TemplatePtrField {
178public:
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
185private:
186 T *p;
187};
188
189template <class T>
190class TemplateCArrayField {
191public:
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
198private:
199 T p[256];
200};
201
202// Other template class's constructor is called inside a declaration.
203template <class T>
204class WrongTemplateCopyAndMove {
205public:
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
213private:
214 T *p;
215};
216
217// https://bugs.llvm.org/show_bug.cgi?id=44499
218class Foo2;
219template <int a>
220bool 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.
236class SelfCheck1 {
237public:
238 SelfCheck1 &operator=(const SelfCheck1 &object) {
239 if (this == &object)
240 return *this;
241 // ...
242 return *this;
243 }
244
245private:
246 int *p;
247};
248
249class SelfCheck2 {
250public:
251 SelfCheck2 &operator=(const SelfCheck2 &object) {
252 if (&object == this)
253 return *this;
254 // ...
255 return *this;
256 }
257
258private:
259 int *p;
260};
261
262// Self-assignment is checked using the inequality operator.
263class SelfCheck3 {
264public:
265 SelfCheck3 &operator=(const SelfCheck3 &object) {
266 if (this != &object) {
267 // ...
268 }
269 return *this;
270 }
271
272private:
273 int *p;
274};
275
276class SelfCheck4 {
277public:
278 SelfCheck4 &operator=(const SelfCheck4 &object) {
279 if (&object != this) {
280 // ...
281 }
282 return *this;
283 }
284
285private:
286 int *p;
287};
288
289template <class T>
290class TemplateSelfCheck {
291public:
292 TemplateSelfCheck<T> &operator=(const TemplateSelfCheck<T> &object) {
293 if (&object != this) {
294 // ...
295 }
296 return *this;
297 }
298
299private:
300 T *p;
301};
302
303// https://bugs.llvm.org/show_bug.cgi?id=44499
304class Foo;
305template <int a>
306bool 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.
319class PassedByValue {
320public:
321 PassedByValue &operator=(PassedByValue object) {
322 // ...
323 return *this;
324 }
325
326private:
327 int *p;
328};
329
330// User-defined swap method calling std::swap inside.
331class CopyAndSwap1 {
332public:
333 CopyAndSwap1 &operator=(const CopyAndSwap1 &object) {
334 CopyAndSwap1 temp(object);
335 doSwap(object&: temp);
336 return *this;
337 }
338
339private:
340 int *p;
341
342 void doSwap(CopyAndSwap1 &object) {
343 using std::swap;
344 swap(x&: p, y&: object.p);
345 }
346};
347
348// User-defined swap method used with passed-by-value parameter.
349class CopyAndSwap2 {
350public:
351 CopyAndSwap2 &operator=(CopyAndSwap2 object) {
352 doSwap(object);
353 return *this;
354 }
355
356private:
357 int *p;
358
359 void doSwap(CopyAndSwap2 &object) {
360 using std::swap;
361 swap(x&: p, y&: object.p);
362 }
363};
364
365// Copy-and-swap method is used but without creating a separate method for it.
366class CopyAndSwap3 {
367public:
368 CopyAndSwap3 &operator=(const CopyAndSwap3 &object) {
369 CopyAndSwap3 temp(object);
370 std::swap(x&: p, y&: temp.p);
371 return *this;
372 }
373
374private:
375 int *p;
376};
377
378template <class T>
379class TemplateCopyAndSwap {
380public:
381 TemplateCopyAndSwap<T> &operator=(const TemplateCopyAndSwap<T> &object) {
382 TemplateCopyAndSwap<T> temp(object);
383 std::swap(p, temp.p);
384 return *this;
385 }
386
387private:
388 T *p;
389};
390
391// Move semantics is used on a temporary copy of the object.
392class CopyAndMove1 {
393public:
394 CopyAndMove1 &operator=(const CopyAndMove1 &object) {
395 CopyAndMove1 temp(object);
396 *this = std::move(temp);
397 return *this;
398 }
399
400private:
401 int *p;
402};
403
404// There is no local variable for the temporary copy.
405class CopyAndMove2 {
406public:
407 CopyAndMove2 &operator=(const CopyAndMove2 &object) {
408 *this = CopyAndMove2(object);
409 return *this;
410 }
411
412private:
413 int *p;
414};
415
416template <class T>
417class TemplateCopyAndMove {
418public:
419 TemplateCopyAndMove<T> &operator=(const TemplateCopyAndMove<T> &object) {
420 TemplateCopyAndMove<T> temp(object);
421 *this = std::move(temp);
422 return *this;
423 }
424
425private:
426 T *p;
427};
428
429// There is no local variable for the temporary copy.
430template <class T>
431class TemplateCopyAndMove2 {
432public:
433 TemplateCopyAndMove2<T> &operator=(const TemplateCopyAndMove2<T> &object) {
434 *this = std::move(TemplateCopyAndMove2<T>(object));
435 return *this;
436 }
437
438private:
439 T *p;
440};
441
442// We should not catch move assignment operators.
443class MoveAssignOperator {
444public:
445 MoveAssignOperator &operator=(MoveAssignOperator &&object) {
446 // ...
447 return *this;
448 }
449
450private:
451 int *p;
452};
453
454// We ignore copy assignment operators without user-defined implementation.
455class DefaultOperator {
456public:
457 DefaultOperator &operator=(const DefaultOperator &object) = default;
458
459private:
460 int *p;
461};
462
463class DeletedOperator {
464public:
465 DeletedOperator &operator=(const DefaultOperator &object) = delete;
466
467private:
468 int *p;
469};
470
471class ImplicitOperator {
472private:
473 int *p;
474};
475
476// Check ignores those classes which has no any pointer or array field.
477class TrivialFields {
478public:
479 TrivialFields &operator=(const TrivialFields &object) {
480 // ...
481 return *this;
482 }
483
484private:
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.
493class AssignIsForwarded {
494public:
495 AssignIsForwarded &operator=(const AssignIsForwarded &object) {
496 operator=(pp: 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
508private:
509 int *p;
510};
511
512// Assertion is a valid way to say that self-assignment is not expected to happen.
513class AssertGuard {
514public:
515 AssertGuard &operator=(const AssertGuard &object) {
516 assert(expression: this != &object);
517 // ...
518 return *this;
519 }
520
521private:
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.
527template <typename Ty, typename Uy>
528class NotACopyAssignmentOperator {
529 Ty *Ptr1;
530 Uy *Ptr2;
531
532public:
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.
547template <class T>
548class custom_ptr {
549};
550
551class CustomPtrField {
552public:
553 CustomPtrField &operator=(const CustomPtrField &object) {
554 // ...
555 return *this;
556 }
557
558private:
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
565class ArrayCopy {
566public:
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
574private:
575 int array[256];
576};
577
578class GetterSetter {
579public:
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(x&: value, y&: pTmp);
591 delete pTmp;
592 }
593
594private:
595 int *value;
596};
597
598class CustomSelfCheck {
599public:
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
608private:
609 int *value;
610 int index;
611};
612

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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