1// RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX11 %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing
2// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing
3
4typedef decltype(nullptr) nullptr_t;
5
6namespace std {
7typedef unsigned size_t;
8
9template <typename T>
10struct unique_ptr {
11 unique_ptr();
12 T *get() const;
13 explicit operator bool() const;
14 void reset(T *ptr);
15 T &operator*() const;
16 T *operator->() const;
17 T& operator[](size_t i) const;
18};
19
20template <typename T>
21struct shared_ptr {
22 shared_ptr();
23 T *get() const;
24 explicit operator bool() const;
25 void reset(T *ptr);
26 T &operator*() const;
27 T *operator->() const;
28};
29
30template <typename T>
31struct weak_ptr {
32 weak_ptr();
33 bool expired() const;
34};
35
36template <typename T>
37struct optional {
38 optional();
39 T& operator*();
40 const T& operator*() const;
41 void reset();
42};
43
44struct any {
45 any();
46 void reset();
47};
48
49template <typename T1, typename T2>
50struct pair {};
51
52template <typename Key, typename T>
53struct map {
54 struct iterator {};
55
56 map();
57 void clear();
58 bool empty();
59 template <class... Args>
60 pair<iterator, bool> try_emplace(const Key &key, Args &&...args);
61};
62
63template <typename Key, typename T>
64struct unordered_map {
65 struct iterator {};
66
67 unordered_map();
68 void clear();
69 bool empty();
70 template <class... Args>
71 pair<iterator, bool> try_emplace(const Key &key, Args &&...args);
72};
73
74#define DECLARE_STANDARD_CONTAINER(name) \
75 template <typename T> \
76 struct name { \
77 name(); \
78 void clear(); \
79 bool empty(); \
80 }
81
82#define DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(name) \
83 template <typename T> \
84 struct name { \
85 name(); \
86 void clear(); \
87 bool empty(); \
88 void assign(size_t, const T &); \
89 }
90
91DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(basic_string);
92DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(vector);
93DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(deque);
94DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(forward_list);
95DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(list);
96DECLARE_STANDARD_CONTAINER(set);
97DECLARE_STANDARD_CONTAINER(multiset);
98DECLARE_STANDARD_CONTAINER(multimap);
99DECLARE_STANDARD_CONTAINER(unordered_set);
100DECLARE_STANDARD_CONTAINER(unordered_multiset);
101DECLARE_STANDARD_CONTAINER(unordered_multimap);
102
103typedef basic_string<char> string;
104
105template <typename>
106struct remove_reference;
107
108template <typename _Tp>
109struct remove_reference {
110 typedef _Tp type;
111};
112
113template <typename _Tp>
114struct remove_reference<_Tp &> {
115 typedef _Tp type;
116};
117
118template <typename _Tp>
119struct remove_reference<_Tp &&> {
120 typedef _Tp type;
121};
122
123template <typename _Tp>
124constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept {
125 return static_cast<typename remove_reference<_Tp>::type &&>(__t);
126}
127
128template <class _Tp>
129constexpr _Tp&&
130forward(typename std::remove_reference<_Tp>::type& __t) noexcept {
131 return static_cast<_Tp&&>(__t);
132}
133
134template <class _Tp>
135constexpr _Tp&&
136forward(typename std::remove_reference<_Tp>::type&& __t) noexcept {
137 return static_cast<_Tp&&>(__t);
138}
139
140} // namespace std
141
142class A {
143public:
144 A();
145 A(const A &);
146 A(A &&);
147
148 A &operator=(const A &);
149 A &operator=(A &&);
150
151 void foo() const;
152 void bar(int i) const;
153 int getInt() const;
154
155 operator bool() const;
156
157 int i;
158};
159
160template <class T>
161class AnnotatedContainer {
162public:
163 AnnotatedContainer();
164
165 void foo() const;
166 [[clang::reinitializes]] void clear();
167};
168
169////////////////////////////////////////////////////////////////////////////////
170// General tests.
171
172// Simple case.
173void simple() {
174 A a;
175 a.foo();
176 A other_a = std::move(a);
177 a.foo();
178 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
179 // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here
180}
181
182// Don't flag a move-to-self.
183void selfMove() {
184 A a;
185 a = std::move(a);
186 a.foo();
187}
188
189// A warning should only be emitted for one use-after-move.
190void onlyFlagOneUseAfterMove() {
191 A a;
192 a.foo();
193 A other_a = std::move(a);
194 a.foo();
195 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
196 // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here
197 a.foo();
198}
199
200void moveAfterMove() {
201 // Move-after-move also counts as a use.
202 {
203 A a;
204 std::move(a);
205 std::move(a);
206 // CHECK-NOTES: [[@LINE-1]]:15: warning: 'a' used after it was moved
207 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
208 }
209 // This is also true if the move itself turns into the use on the second loop
210 // iteration.
211 {
212 A a;
213 for (int i = 0; i < 10; ++i) {
214 std::move(a);
215 // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved
216 // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here
217 // CHECK-NOTES: [[@LINE-3]]:17: note: the use happens in a later loop
218 }
219 }
220}
221
222// Checks also works on function parameters that have a use-after move.
223void parameters(A a) {
224 std::move(a);
225 a.foo();
226 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
227 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here
228}
229
230void standardSmartPtr() {
231 // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to
232 // be null after a std::move. So the check only flags accesses that would
233 // dereference the pointer.
234 {
235 std::unique_ptr<A> ptr;
236 std::move(ptr);
237 ptr.get();
238 static_cast<bool>(ptr);
239 *ptr;
240 // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
241 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here
242 }
243 {
244 std::unique_ptr<A> ptr;
245 std::move(ptr);
246 ptr->foo();
247 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
248 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
249 }
250 {
251 std::unique_ptr<A> ptr;
252 std::move(ptr);
253 ptr[0];
254 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
255 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
256 }
257 {
258 std::shared_ptr<A> ptr;
259 std::move(ptr);
260 ptr.get();
261 static_cast<bool>(ptr);
262 *ptr;
263 // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
264 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here
265 }
266 {
267 std::shared_ptr<A> ptr;
268 std::move(ptr);
269 ptr->foo();
270 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
271 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
272 }
273 {
274 std::optional<A> opt;
275 std::move(opt);
276 A val = *opt;
277 (void)val;
278 // CHECK-NOTES: [[@LINE-2]]:14: warning: 'opt' used after it was moved
279 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
280 }
281 {
282 // std::weak_ptr<> cannot be dereferenced directly, so we only check that
283 // member functions may be called on it after a move.
284 std::weak_ptr<A> ptr;
285 std::move(ptr);
286 ptr.expired();
287 }
288 // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're
289 // wrapped in a typedef.
290 {
291 typedef std::unique_ptr<A> PtrToA;
292 PtrToA ptr;
293 std::move(ptr);
294 ptr.get();
295 }
296 {
297 typedef std::shared_ptr<A> PtrToA;
298 PtrToA ptr;
299 std::move(ptr);
300 ptr.get();
301 }
302 // And we don't get confused if the template argument is a little more
303 // involved.
304 {
305 struct B {
306 typedef A AnotherNameForA;
307 };
308 std::unique_ptr<B::AnotherNameForA> ptr;
309 std::move(ptr);
310 ptr.get();
311 }
312 // Make sure we treat references to smart pointers correctly.
313 {
314 std::unique_ptr<A> ptr;
315 std::unique_ptr<A>& ref_to_ptr = ptr;
316 std::move(ref_to_ptr);
317 ref_to_ptr.get();
318 }
319 {
320 std::unique_ptr<A> ptr;
321 std::unique_ptr<A>&& rvalue_ref_to_ptr = std::move(ptr);
322 std::move(rvalue_ref_to_ptr);
323 rvalue_ref_to_ptr.get();
324 }
325 // We don't give any special treatment to types that are called "unique_ptr"
326 // or "shared_ptr" but are not in the "::std" namespace.
327 {
328 struct unique_ptr {
329 void get();
330 } ptr;
331 std::move(ptr);
332 ptr.get();
333 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
334 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
335 }
336}
337
338// The check also works in member functions.
339class Container {
340 void useAfterMoveInMemberFunction() {
341 A a;
342 std::move(a);
343 a.foo();
344 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
345 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
346 }
347};
348
349// We see the std::move() if it's inside a declaration.
350void moveInDeclaration() {
351 A a;
352 A another_a(std::move(a));
353 a.foo();
354 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
355 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
356}
357
358// We see the std::move if it's inside an initializer list. Initializer lists
359// are a special case because they cause ASTContext::getParents() to return
360// multiple parents for certain nodes in their subtree. This is because
361// RecursiveASTVisitor visits both the syntactic and semantic forms of
362// InitListExpr, and the parent-child relationships are different between the
363// two forms.
364void moveInInitList() {
365 struct S {
366 A a;
367 };
368 A a;
369 S s{.a: std::move(a)};
370 a.foo();
371 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
372 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here
373}
374
375void lambdas() {
376 // Use-after-moves inside a lambda should be detected.
377 {
378 A a;
379 auto lambda = [a] {
380 std::move(a);
381 a.foo();
382 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
383 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here
384 };
385 }
386 // This is just as true if the variable was declared inside the lambda.
387 {
388 auto lambda = [] {
389 A a;
390 std::move(a);
391 a.foo();
392 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
393 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here
394 };
395 }
396 // But don't warn if the move happened inside the lambda but the use happened
397 // outside -- because
398 // - the 'a' inside the lambda is a copy, and
399 // - we don't know when the lambda will get called anyway
400 {
401 A a;
402 auto lambda = [a] {
403 std::move(a);
404 };
405 a.foo();
406 }
407 // Don't warn if 'a' is a copy inside a synchronous lambda
408 {
409 A a;
410 A copied{[a] mutable { return std::move(a); }()};
411 a.foo();
412 }
413 // False negative (should warn if 'a' is a ref inside a synchronous lambda)
414 {
415 A a;
416 A moved{[&a] mutable { return std::move(a); }()};
417 a.foo();
418 }
419 // Warn if the use consists of a capture that happens after a move.
420 {
421 A a;
422 std::move(a);
423 auto lambda = [a]() { a.foo(); };
424 // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved
425 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
426 }
427 // ...even if the capture was implicit.
428 {
429 A a;
430 std::move(a);
431 auto lambda = [=]() { a.foo(); };
432 // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved
433 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
434 }
435 // Same tests but for capture by reference.
436 {
437 A a;
438 std::move(a);
439 auto lambda = [&a]() { a.foo(); };
440 // CHECK-NOTES: [[@LINE-1]]:21: warning: 'a' used after it was moved
441 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
442 }
443 {
444 A a;
445 std::move(a);
446 auto lambda = [&]() { a.foo(); };
447 // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved
448 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
449 }
450 // But don't warn if the move happened after the capture.
451 {
452 A a;
453 auto lambda = [a]() { a.foo(); };
454 std::move(a);
455 }
456 // ...and again, same thing with an implicit move.
457 {
458 A a;
459 auto lambda = [=]() { a.foo(); };
460 std::move(a);
461 }
462 // Same tests but for capture by reference.
463 {
464 A a;
465 auto lambda = [&a]() { a.foo(); };
466 std::move(a);
467 }
468 {
469 A a;
470 auto lambda = [&]() { a.foo(); };
471 std::move(a);
472 }
473 {
474 A a;
475 auto lambda = [a = std::move(a)] { a.foo(); };
476 a.foo();
477 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
478 // CHECK-NOTES: [[@LINE-3]]:24: note: move occurred here
479 }
480}
481
482// Use-after-moves are detected in uninstantiated templates if the moved type
483// is not a dependent type.
484template <class T>
485void movedTypeIsNotDependentType() {
486 T t;
487 A a;
488 std::move(a);
489 a.foo();
490 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
491 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here
492}
493
494// And if the moved type is a dependent type, the use-after-move is detected if
495// the template is instantiated.
496template <class T>
497void movedTypeIsDependentType() {
498 T t;
499 std::move(t);
500 t.foo();
501 // CHECK-NOTES: [[@LINE-1]]:3: warning: 't' used after it was moved
502 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here
503}
504template void movedTypeIsDependentType<A>();
505
506// We handle the case correctly where the move consists of an implicit call
507// to a conversion operator.
508void implicitConversionOperator() {
509 struct Convertible {
510 operator A() && { return A(); }
511 };
512 void takeA(A a);
513
514 Convertible convertible;
515 takeA(a: std::move(convertible));
516 convertible;
517 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved
518 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here
519}
520
521// Using decltype on an expression is not a use.
522void decltypeIsNotUse() {
523 A a;
524 std::move(a);
525 decltype(a) other_a;
526}
527
528// Ignore moves or uses that occur as part of template arguments.
529template <int>
530class ClassTemplate {
531public:
532 void foo(A a);
533};
534template <int>
535void functionTemplate(A a);
536void templateArgIsNotUse() {
537 {
538 // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
539 // Google Test.
540 A a;
541 ClassTemplate<sizeof(A(std::move(a)))>().foo(a: std::move(a));
542 }
543 {
544 A a;
545 functionTemplate<sizeof(A(std::move(a)))>(a: std::move(a));
546 }
547}
548
549// Ignore moves of global variables.
550A global_a;
551void ignoreGlobalVariables() {
552 std::move(global_a);
553 global_a.foo();
554}
555
556// Ignore moves of member variables.
557class IgnoreMemberVariables {
558 A a;
559 static A static_a;
560
561 void f() {
562 std::move(a);
563 a.foo();
564
565 std::move(static_a);
566 static_a.foo();
567 }
568};
569
570// Ignore moves that happen in a try_emplace.
571void ignoreMoveInTryEmplace() {
572 {
573 std::map<int, A> amap;
574 A a;
575 amap.try_emplace(key: 1, args: std::move(a));
576 a.foo();
577 }
578 {
579 std::unordered_map<int, A> amap;
580 A a;
581 amap.try_emplace(key: 1, args: std::move(a));
582 a.foo();
583 }
584}
585
586////////////////////////////////////////////////////////////////////////////////
587// Tests involving control flow.
588
589void useAndMoveInLoop() {
590 // Warn about use-after-moves if they happen in a later loop iteration than
591 // the std::move().
592 {
593 A a;
594 for (int i = 0; i < 10; ++i) {
595 a.foo();
596 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
597 // CHECK-NOTES: [[@LINE+2]]:7: note: move occurred here
598 // CHECK-NOTES: [[@LINE-3]]:7: note: the use happens in a later loop
599 std::move(a);
600 }
601 }
602 // Same as above, but the use and the move are in different CFG blocks.
603 {
604 A a;
605 for (int i = 0; i < 10; ++i) {
606 if (i < 10)
607 a.foo();
608 // CHECK-NOTES: [[@LINE-1]]:9: warning: 'a' used after it was moved
609 // CHECK-NOTES: [[@LINE+3]]:9: note: move occurred here
610 // CHECK-NOTES: [[@LINE-3]]:9: note: the use happens in a later loop
611 if (i < 10)
612 std::move(a);
613 }
614 }
615 // However, this case shouldn't be flagged -- the scope of the declaration of
616 // 'a' is important.
617 {
618 for (int i = 0; i < 10; ++i) {
619 A a;
620 a.foo();
621 std::move(a);
622 }
623 }
624 // Same as above, except that we have an unrelated variable being declared in
625 // the same declaration as 'a'. This case is interesting because it tests that
626 // the synthetic DeclStmts generated by the CFG are sequenced correctly
627 // relative to the other statements.
628 {
629 for (int i = 0; i < 10; ++i) {
630 A a, other;
631 a.foo();
632 std::move(a);
633 }
634 }
635 // Don't warn if we return after the move.
636 {
637 A a;
638 for (int i = 0; i < 10; ++i) {
639 a.foo();
640 if (a.getInt() > 0) {
641 std::move(a);
642 return;
643 }
644 }
645 }
646}
647
648void differentBranches(int i) {
649 // Don't warn if the use is in a different branch from the move.
650 {
651 A a;
652 if (i > 0) {
653 std::move(a);
654 } else {
655 a.foo();
656 }
657 }
658 // Same thing, but with a ternary operator.
659 {
660 A a;
661 i > 0 ? (void)std::move(a) : a.foo();
662 }
663 // A variation on the theme above.
664 {
665 A a;
666 a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt();
667 }
668 // Same thing, but with a switch statement.
669 {
670 A a;
671 switch (i) {
672 case 1:
673 std::move(a);
674 break;
675 case 2:
676 a.foo();
677 break;
678 }
679 }
680 // However, if there's a fallthrough, we do warn.
681 {
682 A a;
683 switch (i) {
684 case 1:
685 std::move(a);
686 case 2:
687 a.foo();
688 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
689 // CHECK-NOTES: [[@LINE-4]]:7: note: move occurred here
690 break;
691 }
692 }
693}
694
695// False positive: A use-after-move is flagged even though the "if (b)" and
696// "if (!b)" are mutually exclusive.
697void mutuallyExclusiveBranchesFalsePositive(bool b) {
698 A a;
699 if (b) {
700 std::move(a);
701 }
702 if (!b) {
703 a.foo();
704 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
705 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here
706 }
707}
708
709// Destructors marked [[noreturn]] are handled correctly in the control flow
710// analysis. (These are used in some styles of assertion macros.)
711class FailureLogger {
712public:
713 FailureLogger();
714 [[noreturn]] ~FailureLogger();
715 void log(const char *);
716};
717#define ASSERT(x) \
718 while (x) \
719 FailureLogger().log(#x)
720bool operationOnA(A);
721void noreturnDestructor() {
722 A a;
723 // The while loop in the ASSERT() would ordinarily have the potential to cause
724 // a use-after-move because the second iteration of the loop would be using a
725 // variable that had been moved from in the first iteration. Check that the
726 // CFG knows that the second iteration of the loop is never reached because
727 // the FailureLogger destructor is marked [[noreturn]].
728 ASSERT(operationOnA(std::move(a)));
729}
730#undef ASSERT
731
732////////////////////////////////////////////////////////////////////////////////
733// Tests for reinitializations
734
735template <class T>
736void swap(T &a, T &b) {
737 T tmp = std::move(a);
738 a = std::move(b);
739 b = std::move(tmp);
740}
741void assignments(int i) {
742 // Don't report a use-after-move if the variable was assigned to in the
743 // meantime.
744 {
745 A a;
746 std::move(a);
747 a = A();
748 a.foo();
749 }
750 // The assignment should also be recognized if move, assignment and use don't
751 // all happen in the same block (but the assignment is still guaranteed to
752 // prevent a use-after-move).
753 {
754 A a;
755 if (i == 1) {
756 std::move(a);
757 a = A();
758 }
759 if (i == 2) {
760 a.foo();
761 }
762 }
763 {
764 A a;
765 if (i == 1) {
766 std::move(a);
767 }
768 if (i == 2) {
769 a = A();
770 a.foo();
771 }
772 }
773 // The built-in assignment operator should also be recognized as a
774 // reinitialization. (std::move() may be called on built-in types in template
775 // code.)
776 {
777 int a1 = 1, a2 = 2;
778 swap(a&: a1, b&: a2);
779 }
780 // A std::move() after the assignment makes the variable invalid again.
781 {
782 A a;
783 std::move(a);
784 a = A();
785 std::move(a);
786 a.foo();
787 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
788 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
789 }
790 // Report a use-after-move if we can't be sure that the variable was assigned
791 // to.
792 {
793 A a;
794 std::move(a);
795 if (i < 10) {
796 a = A();
797 }
798 if (i > 5) {
799 a.foo();
800 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
801 // CHECK-NOTES: [[@LINE-7]]:5: note: move occurred here
802 }
803 }
804}
805
806// Passing the object to a function through a non-const pointer or reference
807// counts as a re-initialization.
808void passByNonConstPointer(A *);
809void passByNonConstReference(A &);
810void passByNonConstPointerIsReinit() {
811 {
812 A a;
813 std::move(a);
814 passByNonConstPointer(&a);
815 a.foo();
816 }
817 {
818 A a;
819 std::move(a);
820 passByNonConstReference(a);
821 a.foo();
822 }
823}
824
825// Passing the object through a const pointer or reference counts as a use --
826// since the called function cannot reinitialize the object.
827void passByConstPointer(const A *);
828void passByConstReference(const A &);
829void passByConstPointerIsUse() {
830 {
831 // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the
832 // AST -- we wouldn't want the check to rely solely on that to detect a
833 // const pointer argument.
834 const A a;
835 std::move(a);
836 passByConstPointer(&a);
837 // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved
838 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
839 }
840 const A a;
841 std::move(a);
842 passByConstReference(a);
843 // CHECK-NOTES: [[@LINE-1]]:24: warning: 'a' used after it was moved
844 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here
845}
846
847// Clearing a standard container using clear() is treated as a
848// re-initialization.
849void standardContainerClearIsReinit() {
850 {
851 std::string container;
852 std::move(container);
853 container.clear();
854 container.empty();
855 }
856 {
857 std::vector<int> container;
858 std::move(container);
859 container.clear();
860 container.empty();
861
862 auto container2 = container;
863 std::move(container2);
864 container2.clear();
865 container2.empty();
866 }
867 {
868 std::deque<int> container;
869 std::move(container);
870 container.clear();
871 container.empty();
872 }
873 {
874 std::forward_list<int> container;
875 std::move(container);
876 container.clear();
877 container.empty();
878 }
879 {
880 std::list<int> container;
881 std::move(container);
882 container.clear();
883 container.empty();
884 }
885 {
886 std::set<int> container;
887 std::move(container);
888 container.clear();
889 container.empty();
890 }
891 {
892 std::map<int, int> container;
893 std::move(container);
894 container.clear();
895 container.empty();
896 }
897 {
898 std::multiset<int> container;
899 std::move(container);
900 container.clear();
901 container.empty();
902 }
903 {
904 std::multimap<int> container;
905 std::move(container);
906 container.clear();
907 container.empty();
908 }
909 {
910 std::unordered_set<int> container;
911 std::move(container);
912 container.clear();
913 container.empty();
914 }
915 {
916 std::unordered_map<int, int> container;
917 std::move(container);
918 container.clear();
919 container.empty();
920 }
921 {
922 std::unordered_multiset<int> container;
923 std::move(container);
924 container.clear();
925 container.empty();
926 }
927 {
928 std::unordered_multimap<int> container;
929 std::move(container);
930 container.clear();
931 container.empty();
932 }
933 // This should also work for typedefs of standard containers.
934 {
935 typedef std::vector<int> IntVector;
936 IntVector container;
937 std::move(container);
938 container.clear();
939 container.empty();
940 }
941 // But it shouldn't work for non-standard containers.
942 {
943 // This might be called "vector", but it's not in namespace "std".
944 struct vector {
945 void clear() {}
946 } container;
947 std::move(container);
948 container.clear();
949 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container' used after it was
950 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
951 }
952 // An intervening clear() on a different container does not reinitialize.
953 {
954 std::vector<int> container1, container2;
955 std::move(container1);
956 container2.clear();
957 container1.empty();
958 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container1' used after it was
959 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
960 }
961}
962
963// Clearing a standard container using assign() is treated as a
964// re-initialization.
965void standardContainerAssignIsReinit() {
966 {
967 std::string container;
968 std::move(container);
969 container.assign(0, ' ');
970 container.empty();
971 }
972 {
973 std::vector<int> container;
974 std::move(container);
975 container.assign(0, 0);
976 container.empty();
977 }
978 {
979 std::deque<int> container;
980 std::move(container);
981 container.assign(0, 0);
982 container.empty();
983 }
984 {
985 std::forward_list<int> container;
986 std::move(container);
987 container.assign(0, 0);
988 container.empty();
989 }
990 {
991 std::list<int> container;
992 std::move(container);
993 container.clear();
994 container.empty();
995 }
996 // But it doesn't work for non-standard containers.
997 {
998 // This might be called "vector", but it's not in namespace "std".
999 struct vector {
1000 void assign(std::size_t, int) {}
1001 } container;
1002 std::move(container);
1003 container.assign(0, 0);
1004 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container' used after it was
1005 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
1006 }
1007 // An intervening assign() on a different container does not reinitialize.
1008 {
1009 std::vector<int> container1, container2;
1010 std::move(container1);
1011 container2.assign(0, 0);
1012 container1.empty();
1013 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container1' used after it was
1014 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
1015 }
1016}
1017
1018// Resetting the standard smart owning types using reset() is treated as a
1019// re-initialization. (We don't test std::weak_ptr<> because it can't be
1020// dereferenced directly.)
1021void resetIsReinit() {
1022 {
1023 std::unique_ptr<A> ptr;
1024 std::move(ptr);
1025 ptr.reset(ptr: new A);
1026 *ptr;
1027 }
1028 {
1029 std::shared_ptr<A> ptr;
1030 std::move(ptr);
1031 ptr.reset(ptr: new A);
1032 *ptr;
1033 }
1034 {
1035 std::optional<A> opt;
1036 std::move(opt);
1037 opt.reset();
1038 std::optional<A> opt2 = opt;
1039 (void)opt2;
1040 }
1041 {
1042 std::any a;
1043 std::move(a);
1044 a.reset();
1045 std::any a2 = a;
1046 (void)a2;
1047 }
1048}
1049
1050void reinitAnnotation() {
1051 {
1052 AnnotatedContainer<int> obj;
1053 std::move(obj);
1054 obj.foo();
1055 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'obj' used after it was
1056 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
1057 }
1058 {
1059 AnnotatedContainer<int> obj;
1060 std::move(obj);
1061 obj.clear();
1062 obj.foo();
1063 }
1064 {
1065 // Calling clear() on a different object to the one that was moved is not
1066 // considered a reinitialization.
1067 AnnotatedContainer<int> obj1, obj2;
1068 std::move(obj1);
1069 obj2.clear();
1070 obj1.foo();
1071 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'obj1' used after it was
1072 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
1073 }
1074}
1075
1076////////////////////////////////////////////////////////////////////////////////
1077// Tests related to order of evaluation within expressions
1078
1079// Relative sequencing of move and use.
1080void passByRvalueReference(int i, A &&a);
1081void passByValue(int i, A a);
1082void passByValue(A a, int i);
1083A g(A, A &&);
1084int intFromA(A &&);
1085int intFromInt(int);
1086void sequencingOfMoveAndUse() {
1087 // This case is fine because the move only happens inside
1088 // passByRvalueReference(). At this point, a.getInt() is guaranteed to have
1089 // been evaluated.
1090 {
1091 A a;
1092 passByRvalueReference(i: a.getInt(), a: std::move(a));
1093 }
1094 // However, if we pass by value, the move happens when the move constructor is
1095 // called to create a temporary, and this happens before the call to
1096 // passByValue(). Because the order in which arguments are evaluated isn't
1097 // defined, the move may happen before the call to a.getInt().
1098 //
1099 // Check that we warn about a potential use-after move for both orderings of
1100 // a.getInt() and std::move(a), independent of the order in which the
1101 // arguments happen to get evaluated by the compiler.
1102 {
1103 A a;
1104 passByValue(i: a.getInt(), a: std::move(a));
1105 // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved
1106 // CHECK-NOTES: [[@LINE-2]]:29: note: move occurred here
1107 // CHECK-NOTES: [[@LINE-3]]:17: note: the use and move are unsequenced
1108 }
1109 {
1110 A a;
1111 passByValue(a: std::move(a), i: a.getInt());
1112 // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved
1113 // CHECK-NOTES: [[@LINE-2]]:17: note: move occurred here
1114 // CHECK-NOTES: [[@LINE-3]]:31: note: the use and move are unsequenced
1115 }
1116 // An even more convoluted example.
1117 {
1118 A a;
1119 g(g(a, std::move(a)), g(a, std::move(a)));
1120 // CHECK-NOTES: [[@LINE-1]]:9: warning: 'a' used after it was moved
1121 // CHECK-NOTES: [[@LINE-2]]:27: note: move occurred here
1122 // CHECK-NOTES: [[@LINE-3]]:9: note: the use and move are unsequenced
1123 // CHECK-NOTES: [[@LINE-4]]:29: warning: 'a' used after it was moved
1124 // CHECK-NOTES: [[@LINE-5]]:7: note: move occurred here
1125 // CHECK-NOTES: [[@LINE-6]]:29: note: the use and move are unsequenced
1126 }
1127 // This case is fine because the actual move only happens inside the call to
1128 // operator=(). a.getInt(), by necessity, is evaluated before that call.
1129 {
1130 A a;
1131 A vec[1];
1132 vec[a.getInt()] = std::move(a);
1133 }
1134 // However, in the following case, the move happens before the assignment, and
1135 // so the order of evaluation is not guaranteed.
1136 {
1137 A a;
1138 int v[3];
1139 v[a.getInt()] = intFromA(std::move(a));
1140 // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
1141 // CHECK-NOTES: [[@LINE-2]]:21: note: move occurred here
1142 // CHECK-NOTES: [[@LINE-3]]:7: note: the use and move are unsequenced
1143 }
1144 {
1145 A a;
1146 int v[3];
1147 v[intFromA(std::move(a))] = intFromInt(a.i);
1148 // CHECK-NOTES: [[@LINE-1]]:44: warning: 'a' used after it was moved
1149 // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here
1150 // CHECK-NOTES: [[@LINE-3]]:44: note: the use and move are unsequenced
1151 }
1152}
1153
1154// Relative sequencing of move and reinitialization. If the two are unsequenced,
1155// we conservatively assume that the move happens after the reinitialization,
1156// i.e. the that object does not get reinitialized after the move.
1157A MutateA(A a);
1158void passByValue(A a1, A a2);
1159void sequencingOfMoveAndReinit() {
1160 // Move and reinitialization as function arguments (which are indeterminately
1161 // sequenced). Again, check that we warn for both orderings.
1162 {
1163 A a;
1164 passByValue(a1: std::move(a), a2: (a = A()));
1165 a.foo();
1166 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
1167 // CHECK-NOTES: [[@LINE-3]]:17: note: move occurred here
1168 }
1169 {
1170 A a;
1171 passByValue(a1: (a = A()), a2: std::move(a));
1172 a.foo();
1173 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
1174 // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here
1175 }
1176 // Common usage pattern: Move the object to a function that mutates it in some
1177 // way, then reassign the result to the object. This pattern is fine.
1178 {
1179 A a;
1180 a = MutateA(a: std::move(a));
1181 a.foo();
1182 }
1183}
1184
1185// Relative sequencing of reinitialization and use. If the two are unsequenced,
1186// we conservatively assume that the reinitialization happens after the use,
1187// i.e. that the object is not reinitialized at the point in time when it is
1188// used.
1189void sequencingOfReinitAndUse() {
1190 // Reinitialization and use in function arguments. Again, check both possible
1191 // orderings.
1192 {
1193 A a;
1194 std::move(a);
1195 passByValue(i: a.getInt(), a: (a = A()));
1196 // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved
1197 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
1198 }
1199 {
1200 A a;
1201 std::move(a);
1202 passByValue(a: (a = A()), i: a.getInt());
1203 // CHECK-NOTES: [[@LINE-1]]:28: warning: 'a' used after it was moved
1204 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
1205 }
1206}
1207
1208// The comma operator sequences its operands.
1209void commaOperatorSequences() {
1210 {
1211 A a;
1212 A(std::move(a))
1213 , (a = A());
1214 a.foo();
1215 }
1216 {
1217 A a;
1218 (a = A()), A(std::move(a));
1219 a.foo();
1220 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
1221 // CHECK-NOTES: [[@LINE-3]]:16: note: move occurred here
1222 }
1223}
1224
1225namespace InitializerListSequences {
1226
1227struct S1 {
1228 int i;
1229 A a;
1230};
1231
1232struct S2 {
1233 A a;
1234 int i;
1235};
1236
1237struct S3 {
1238 S3() {}
1239 S3(int, A) {}
1240 S3(A, int) {}
1241};
1242
1243// An initializer list sequences its initialization clauses.
1244void initializerListSequences() {
1245 {
1246 A a;
1247 S1 s1{.i: a.getInt(), .a: std::move(a)};
1248 }
1249 {
1250 A a;
1251 S1 s1{.i = a.getInt(), .a = std::move(a)};
1252 }
1253 {
1254 A a;
1255 S2 s2{.a: std::move(a), .i: a.getInt()};
1256 // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved
1257 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
1258 }
1259 {
1260 A a;
1261 S2 s2{.a = std::move(a), .i = a.getInt()};
1262 // CHECK-NOTES: [[@LINE-1]]:35: warning: 'a' used after it was moved
1263 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
1264 }
1265 {
1266 // Check the case where the constructed type has a constructor and the
1267 // initializer list therefore manifests as a `CXXConstructExpr` instead of
1268 // an `InitListExpr`.
1269 A a;
1270 S3 s3{a.getInt(), std::move(a)};
1271 }
1272 {
1273 A a;
1274 S3 s3{std::move(a), a.getInt()};
1275 // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved
1276 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
1277 }
1278}
1279
1280} // namespace InitializerListSequences
1281
1282// A declaration statement containing multiple declarations sequences the
1283// initializer expressions.
1284void declarationSequences() {
1285 {
1286 A a;
1287 A a1 = a, a2 = std::move(a);
1288 }
1289 {
1290 A a;
1291 A a1 = std::move(a), a2 = a;
1292 // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved
1293 // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here
1294 }
1295}
1296
1297// The logical operators && and || sequence their operands.
1298void logicalOperatorsSequence() {
1299 {
1300 A a;
1301 if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) {
1302 A().foo();
1303 }
1304 }
1305 // A variation: Negate the result of the && (which pushes the && further down
1306 // into the AST).
1307 {
1308 A a;
1309 if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) {
1310 A().foo();
1311 }
1312 }
1313 {
1314 A a;
1315 if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) {
1316 // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved
1317 // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here
1318 A().foo();
1319 }
1320 }
1321 {
1322 A a;
1323 if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) {
1324 A().foo();
1325 }
1326 }
1327 {
1328 A a;
1329 if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) {
1330 // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved
1331 // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here
1332 A().foo();
1333 }
1334 }
1335}
1336
1337// A range-based for sequences the loop variable declaration before the body.
1338void forRangeSequences() {
1339 A v[2] = {A(), A()};
1340 for (A &a : v) {
1341 std::move(a);
1342 }
1343}
1344
1345// If a variable is declared in an if, while or switch statement, the init
1346// statement (for if and switch) is sequenced before the variable declaration,
1347// which in turn is sequenced before the evaluation of the condition. We place
1348// all tests inside a for loop to ensure that the checker understands the
1349// sequencing. If it didn't, then the loop would trigger the "moved twice"
1350// logic.
1351void ifWhileAndSwitchSequenceInitDeclAndCondition() {
1352 for (int i = 0; i < 10; ++i) {
1353 A a1;
1354 if (A a2 = std::move(a1)) {
1355 std::move(a2);
1356 }
1357 }
1358 for (int i = 0; i < 10; ++i) {
1359 A a1;
1360 if (A a2 = std::move(a1); a2) {
1361 std::move(a2);
1362 }
1363 }
1364 for (int i = 0; i < 10; ++i) {
1365 A a1;
1366 if (A a2 = std::move(a1); A a3 = std::move(a2)) {
1367 std::move(a3);
1368 }
1369 }
1370 for (int i = 0; i < 10; ++i) {
1371 // init followed by condition with move, but without variable declaration.
1372 if (A a1; A(std::move(a1)).getInt() > 0) {}
1373 }
1374 for (int i = 0; i < 10; ++i) {
1375 if (A a1; A(std::move(a1)).getInt() > a1.getInt()) {}
1376 // CHECK-NOTES: [[@LINE-1]]:43: warning: 'a1' used after it was moved
1377 // CHECK-NOTES: [[@LINE-2]]:15: note: move occurred here
1378 // CHECK-NOTES: [[@LINE-3]]:43: note: the use and move are unsequenced
1379 }
1380 for (int i = 0; i < 10; ++i) {
1381 A a1;
1382 if (A a2 = std::move(a1); A(a1) > 0) {}
1383 // CHECK-NOTES: [[@LINE-1]]:33: warning: 'a1' used after it was moved
1384 // CHECK-NOTES: [[@LINE-2]]:16: note: move occurred here
1385 }
1386 while (A a = A()) {
1387 std::move(a);
1388 }
1389 for (int i = 0; i < 10; ++i) {
1390 A a1;
1391 switch (A a2 = std::move(a1); a2) {
1392 case true:
1393 std::move(a2);
1394 }
1395 }
1396 for (int i = 0; i < 10; ++i) {
1397 A a1;
1398 switch (A a2 = a1; A a3 = std::move(a2)) {
1399 case true:
1400 std::move(a3);
1401 }
1402 }
1403}
1404
1405// In a function call, the expression that determines the callee is sequenced
1406// before the arguments -- but only in C++17 and later.
1407namespace CalleeSequencedBeforeArguments {
1408int consumeA(std::unique_ptr<A> a);
1409int consumeA(A &&a);
1410
1411void calleeSequencedBeforeArguments() {
1412 {
1413 std::unique_ptr<A> a;
1414 a->bar(i: consumeA(a: std::move(a)));
1415 // CHECK-NOTES-CXX11: [[@LINE-1]]:5: warning: 'a' used after it was moved
1416 // CHECK-NOTES-CXX11: [[@LINE-2]]:21: note: move occurred here
1417 // CHECK-NOTES-CXX11: [[@LINE-3]]:5: note: the use and move are unsequenced
1418 }
1419 {
1420 std::unique_ptr<A> a;
1421 std::unique_ptr<A> getArg(std::unique_ptr<A> a);
1422 getArg(a: std::move(a))->bar(i: a->getInt());
1423 // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved
1424 // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here
1425 // CHECK-NOTES-CXX11: [[@LINE-3]]:31: note: the use and move are unsequenced
1426 }
1427 {
1428 A a;
1429 // Nominally, the callee `a.bar` is evaluated before the argument
1430 // `consumeA(std::move(a))`, but in effect `a` is only accessed after the
1431 // call to `A::bar()` happens, i.e. after the argument has been evaluted.
1432 a.bar(i: consumeA(a: std::move(a)));
1433 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
1434 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
1435 }
1436}
1437} // namespace CalleeSequencedBeforeArguments
1438
1439// Some statements in templates (e.g. null, break and continue statements) may
1440// be shared between the uninstantiated and instantiated versions of the
1441// template and therefore have multiple parents. Make sure the sequencing code
1442// handles this correctly.
1443template <class> void nullStatementSequencesInTemplate() {
1444 int c = 0;
1445 (void)c;
1446 ;
1447 std::move(c);
1448}
1449template void nullStatementSequencesInTemplate<int>();
1450
1451namespace PR33020 {
1452class D {
1453 ~D();
1454};
1455struct A {
1456 D d;
1457};
1458class B {
1459 A a;
1460};
1461template <typename T>
1462class C : T, B {
1463 void m_fn1() {
1464 int a;
1465 std::move(a);
1466 C c;
1467 }
1468};
1469} // namespace PR33020
1470
1471namespace UnevalContext {
1472struct Foo {};
1473void noExcept() {
1474 Foo Bar;
1475 (void) noexcept(Foo{std::move(Bar)});
1476 Foo Other{std::move(Bar)};
1477}
1478void sizeOf() {
1479 Foo Bar;
1480 (void)sizeof(Foo{std::move(Bar)});
1481 Foo Other{std::move(Bar)};
1482}
1483void alignOf() {
1484 Foo Bar;
1485#pragma clang diagnostic push
1486#pragma clang diagnostic ignored "-Wgnu-alignof-expression"
1487 (void)alignof(Foo{std::move(Bar)});
1488#pragma clang diagnostic pop
1489 Foo Other{std::move(Bar)};
1490}
1491void typeId() {
1492 Foo Bar;
1493 // error: you need to include <typeinfo> before using the 'typeid' operator
1494 // (void) typeid(Foo{std::move(Bar)}).name();
1495 Foo Other{std::move(Bar)};
1496}
1497} // namespace UnevalContext
1498
1499class CtorInit {
1500public:
1501 CtorInit(std::string val)
1502 : a{val.empty()}, // fine
1503 s{std::move(val)},
1504 b{val.empty()}
1505 // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved
1506 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here
1507 {}
1508
1509private:
1510 bool a;
1511 std::string s;
1512 bool b;
1513};
1514
1515class CtorInitLambda {
1516public:
1517 CtorInitLambda(std::string val)
1518 : a{val.empty()}, // fine
1519 s{std::move(val)},
1520 b{[&] { return val.empty(); }()},
1521 // CHECK-NOTES: [[@LINE-1]]:12: warning: 'val' used after it was moved
1522 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here
1523 c{[] {
1524 std::string str{};
1525 std::move(str);
1526 return str.empty();
1527 // CHECK-NOTES: [[@LINE-1]]:18: warning: 'str' used after it was moved
1528 // CHECK-NOTES: [[@LINE-3]]:11: note: move occurred here
1529 }()} {
1530 std::move(val);
1531 // CHECK-NOTES: [[@LINE-1]]:15: warning: 'val' used after it was moved
1532 // CHECK-NOTES: [[@LINE-13]]:9: note: move occurred here
1533 std::string val2{};
1534 std::move(val2);
1535 val2.empty();
1536 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'val2' used after it was moved
1537 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
1538 }
1539
1540private:
1541 bool a;
1542 std::string s;
1543 bool b;
1544 bool c;
1545 bool d{};
1546};
1547
1548class CtorInitOrder {
1549public:
1550 CtorInitOrder(std::string val)
1551 : a{val.empty()}, // fine
1552 b{val.empty()},
1553 // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved
1554 s{std::move(val)} {} // wrong order
1555 // CHECK-NOTES: [[@LINE-1]]:9: note: move occurred here
1556
1557private:
1558 bool a;
1559 std::string s;
1560 bool b;
1561};
1562
1563struct Obj {};
1564struct CtorD {
1565 CtorD(Obj b);
1566};
1567
1568struct CtorC {
1569 CtorC(Obj b);
1570};
1571
1572struct CtorB {
1573 CtorB(Obj &b);
1574};
1575
1576struct CtorA : CtorB, CtorC, CtorD {
1577 CtorA(Obj b) : CtorB{b}, CtorC{std::move(b)}, CtorD{b} {}
1578 // CHECK-NOTES: [[@LINE-1]]:55: warning: 'b' used after it was moved
1579 // CHECK-NOTES: [[@LINE-2]]:34: note: move occurred here
1580};
1581
1582struct Base {
1583 Base(Obj b) : bb{std::move(b)} {}
1584 template <typename Call> Base(Call &&c) : bb{c()} {};
1585
1586 Obj bb;
1587};
1588
1589struct Derived : Base, CtorC {
1590 Derived(Obj b)
1591 : Base{[&] mutable { return std::move(b); }()},
1592 // False negative: The lambda/std::move was executed, so it should warn
1593 // below
1594 CtorC{b} {}
1595};
1596
1597struct Derived2 : Base, CtorC {
1598 Derived2(Obj b)
1599 : Base{[&] mutable { return std::move(b); }},
1600 // This was a move, but it doesn't warn below, because it can't know if
1601 // the lambda/std::move was actually called
1602 CtorC{b} {}
1603};
1604
1605struct Derived3 : Base, CtorC {
1606 Derived3(Obj b)
1607 : Base{[c = std::move(b)] mutable { return std::move(c); }}, CtorC{b} {}
1608 // CHECK-NOTES: [[@LINE-1]]:74: warning: 'b' used after it was moved
1609 // CHECK-NOTES: [[@LINE-2]]:19: note: move occurred here
1610};
1611
1612class PR38187 {
1613public:
1614 PR38187(std::string val) : val_(std::move(val)) {
1615 val.empty();
1616 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'val' used after it was moved
1617 // CHECK-NOTES: [[@LINE-3]]:30: note: move occurred here
1618 }
1619
1620private:
1621 std::string val_;
1622};
1623
1624namespace issue82023
1625{
1626
1627struct S {
1628 S();
1629 S(S&&);
1630};
1631
1632void consume(S s);
1633
1634template <typename T>
1635void forward(T&& t) {
1636 consume(std::forward<T>(t));
1637 consume(std::forward<T>(t));
1638 // CHECK-NOTES: [[@LINE-1]]:27: warning: 't' used after it was forwarded
1639 // CHECK-NOTES: [[@LINE-3]]:11: note: forward occurred here
1640}
1641
1642void create() {
1643 S s;
1644 forward(t: std::move(s));
1645}
1646
1647} // namespace issue82023
1648

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/use-after-move.cpp