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 <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 | |
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 | |
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: |
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: |
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: |
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: |
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: |
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: |
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: |
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: |
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 | |
151 | private: |
152 | int *p; |
153 | WeakPtrField a; |
154 | }; |
155 | |
156 | class NotSelfCheck { |
157 | public: |
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 | |
170 | private: |
171 | int *p; |
172 | }; |
173 | |
174 | template <class T> |
175 | class TemplatePtrField { |
176 | public: |
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: |
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: |
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> |
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: |
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: |
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: |
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: |
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: |
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> |
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: |
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: |
331 | CopyAndSwap1 &operator=(const CopyAndSwap1 &object) { |
332 | CopyAndSwap1 temp(object); |
333 | doSwap(object&: temp); |
334 | return *this; |
335 | } |
336 | |
337 | private: |
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. |
347 | class CopyAndSwap2 { |
348 | public: |
349 | CopyAndSwap2 &operator=(CopyAndSwap2 object) { |
350 | doSwap(object); |
351 | return *this; |
352 | } |
353 | |
354 | private: |
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. |
364 | class CopyAndSwap3 { |
365 | public: |
366 | CopyAndSwap3 &operator=(const CopyAndSwap3 &object) { |
367 | CopyAndSwap3 temp(object); |
368 | std::swap(x&: p, y&: temp.p); |
369 | return *this; |
370 | } |
371 | |
372 | private: |
373 | int *p; |
374 | }; |
375 | |
376 | template <class T> |
377 | class TemplateCopyAndSwap { |
378 | public: |
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: |
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: |
405 | CopyAndMove2 &operator=(const CopyAndMove2 &object) { |
406 | *this = CopyAndMove2(object); |
407 | return *this; |
408 | } |
409 | |
410 | private: |
411 | int *p; |
412 | }; |
413 | |
414 | template <class T> |
415 | class TemplateCopyAndMove { |
416 | public: |
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: |
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: |
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: |
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: |
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 | |
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: |
513 | AssertGuard &operator=(const AssertGuard &object) { |
514 | assert(expression: 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: |
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. |
545 | template <class T> |
546 | class custom_ptr { |
547 | }; |
548 | |
549 | class CustomPtrField { |
550 | public: |
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: |
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: |
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 | |
592 | private: |
593 | int *value; |
594 | }; |
595 | |
596 | class CustomSelfCheck { |
597 | public: |
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 | |