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: 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(object&: temp); |
336 | return *this; |
337 | } |
338 | |
339 | private: |
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. |
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(x&: p, y&: 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(x&: p, y&: 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=(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 | |
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(expression: 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(x&: value, y&: 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 |
Definitions
- swap
- move
- default_delete
- unique_ptr
- shared_ptr
- weak_ptr
- auto_ptr
- assert
- PtrField
- operator=
- InlineDefinition
- operator=
- UniquePtrField
- operator=
- SharedPtrField
- operator=
- WeakPtrField
- operator=
- AutoPtrField
- operator=
- CArrayField
- operator=
- CopyConstruct
- operator=
- AssignOperator
- operator=
- NotSelfCheck
- operator=
- doSomething
- TemplatePtrField
- operator=
- TemplateCArrayField
- operator=
- WrongTemplateCopyAndMove
- operator=
- operator!=
- SelfCheck1
- operator=
- SelfCheck2
- operator=
- SelfCheck3
- operator=
- SelfCheck4
- operator=
- TemplateSelfCheck
- operator=
- operator!=
- PassedByValue
- operator=
- CopyAndSwap1
- operator=
- doSwap
- CopyAndSwap2
- operator=
- doSwap
- CopyAndSwap3
- operator=
- TemplateCopyAndSwap
- operator=
- CopyAndMove1
- operator=
- CopyAndMove2
- operator=
- TemplateCopyAndMove
- operator=
- TemplateCopyAndMove2
- operator=
- MoveAssignOperator
- operator=
- DefaultOperator
- operator=
- DeletedOperator
- operator=
- ImplicitOperator
- TrivialFields
- operator=
- AssignIsForwarded
- operator=
- operator=
- AssertGuard
- operator=
- NotACopyAssignmentOperator
- operator=
- getTy
- getUy
- custom_ptr
- CustomPtrField
- operator=
- ArrayCopy
- operator=
- GetterSetter
- operator=
- getValue
- setValue
- CustomSelfCheck
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more