1648 lines · cpp
1// RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX11 %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing2// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing3 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 std141 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 moved179 // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here180}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 moved196 // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here197 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 moved207 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here208 }209 // This is also true if the move itself turns into the use on the second loop210 // 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 moved216 // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here217 // CHECK-NOTES: [[@LINE-3]]:17: note: the use happens in a later loop218 }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 moved227 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here228}229 230void standardSmartPtr() {231 // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to232 // be null after a std::move. So the check only flags accesses that would233 // 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 moved241 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here242 }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 moved248 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here249 }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 moved255 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here256 }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 moved264 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here265 }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 moved271 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here272 }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 moved279 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here280 }281 {282 // std::weak_ptr<> cannot be dereferenced directly, so we only check that283 // 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're289 // 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 more303 // 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 moved334 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here335 }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 moved345 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here346 }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 moved355 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here356}357 358// We see the std::move if it's inside an initializer list. Initializer lists359// are a special case because they cause ASTContext::getParents() to return360// multiple parents for certain nodes in their subtree. This is because361// RecursiveASTVisitor visits both the syntactic and semantic forms of362// InitListExpr, and the parent-child relationships are different between the363// two forms.364void moveInInitList() {365 struct S {366 A a;367 };368 A a;369 S s{std::move(a)};370 a.foo();371 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved372 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here373}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 moved383 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here384 };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 moved393 // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here394 };395 }396 // But don't warn if the move happened inside the lambda but the use happened397 // outside -- because398 // - the 'a' inside the lambda is a copy, and399 // - we don't know when the lambda will get called anyway400 {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 lambda408 {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 moved425 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here426 }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 moved433 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here434 }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 moved441 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here442 }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 moved448 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here449 }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 moved478 // CHECK-NOTES: [[@LINE-3]]:24: note: move occurred here479 }480}481 482// Use-after-moves are detected in uninstantiated templates if the moved type483// 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 moved491 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here492}493 494// And if the moved type is a dependent type, the use-after-move is detected if495// 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 moved502 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here503}504template void movedTypeIsDependentType<A>();505 506// We handle the case correctly where the move consists of an implicit call507// 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(std::move(convertible));516 convertible;517 // CHECK-NOTES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved518 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here519}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 in539 // Google Test.540 A a;541 ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a));542 }543 {544 A a;545 functionTemplate<sizeof(A(std::move(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(1, std::move(a));576 a.foo();577 }578 {579 std::unordered_map<int, A> amap;580 A a;581 amap.try_emplace(1, 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 than591 // 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 moved597 // CHECK-NOTES: [[@LINE+2]]:7: note: move occurred here598 // CHECK-NOTES: [[@LINE-3]]:7: note: the use happens in a later loop599 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 moved609 // CHECK-NOTES: [[@LINE+3]]:9: note: move occurred here610 // CHECK-NOTES: [[@LINE-3]]:9: note: the use happens in a later loop611 if (i < 10)612 std::move(a);613 }614 }615 // However, this case shouldn't be flagged -- the scope of the declaration of616 // '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 in625 // the same declaration as 'a'. This case is interesting because it tests that626 // the synthetic DeclStmts generated by the CFG are sequenced correctly627 // 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 moved689 // CHECK-NOTES: [[@LINE-4]]:7: note: move occurred here690 break;691 }692 }693}694 695// False positive: A use-after-move is flagged even though the "if (b)" and696// "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 moved705 // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here706 }707}708 709// Destructors marked [[noreturn]] are handled correctly in the control flow710// 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 cause724 // a use-after-move because the second iteration of the loop would be using a725 // variable that had been moved from in the first iteration. Check that the726 // CFG knows that the second iteration of the loop is never reached because727 // the FailureLogger destructor is marked [[noreturn]].728 ASSERT(operationOnA(std::move(a)));729}730#undef ASSERT731 732////////////////////////////////////////////////////////////////////////////////733// Tests for reinitializations734 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 the743 // 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't751 // all happen in the same block (but the assignment is still guaranteed to752 // 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 a774 // reinitialization. (std::move() may be called on built-in types in template775 // code.)776 {777 int a1 = 1, a2 = 2;778 swap(a1, 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 moved788 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here789 }790 // Report a use-after-move if we can't be sure that the variable was assigned791 // 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 moved801 // CHECK-NOTES: [[@LINE-7]]:5: note: move occurred here802 }803 }804}805 806// Passing the object to a function through a non-const pointer or reference807// 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 the832 // AST -- we wouldn't want the check to rely solely on that to detect a833 // 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 moved838 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here839 }840 const A a;841 std::move(a);842 passByConstReference(a);843 // CHECK-NOTES: [[@LINE-1]]:24: warning: 'a' used after it was moved844 // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here845}846 847// Clearing a standard container using clear() is treated as a848// 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 was950 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here951 }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 was959 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here960 }961}962 963// Clearing a standard container using assign() is treated as a964// 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 was1005 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here1006 }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 was1014 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here1015 }1016}1017 1018// Resetting the standard smart owning types using reset() is treated as a1019// re-initialization. (We don't test std::weak_ptr<> because it can't be1020// dereferenced directly.)1021void resetIsReinit() {1022 {1023 std::unique_ptr<A> ptr;1024 std::move(ptr);1025 ptr.reset(new A);1026 *ptr;1027 }1028 {1029 std::shared_ptr<A> ptr;1030 std::move(ptr);1031 ptr.reset(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 was1056 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here1057 }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 not1066 // 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 was1072 // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here1073 }1074}1075 1076////////////////////////////////////////////////////////////////////////////////1077// Tests related to order of evaluation within expressions1078 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 inside1088 // passByRvalueReference(). At this point, a.getInt() is guaranteed to have1089 // been evaluated.1090 {1091 A a;1092 passByRvalueReference(a.getInt(), std::move(a));1093 }1094 // However, if we pass by value, the move happens when the move constructor is1095 // called to create a temporary, and this happens before the call to1096 // passByValue(). Because the order in which arguments are evaluated isn't1097 // 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 of1100 // a.getInt() and std::move(a), independent of the order in which the1101 // arguments happen to get evaluated by the compiler.1102 {1103 A a;1104 passByValue(a.getInt(), std::move(a));1105 // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved1106 // CHECK-NOTES: [[@LINE-2]]:29: note: move occurred here1107 // CHECK-NOTES: [[@LINE-3]]:17: note: the use and move are unsequenced1108 }1109 {1110 A a;1111 passByValue(std::move(a), a.getInt());1112 // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved1113 // CHECK-NOTES: [[@LINE-2]]:17: note: move occurred here1114 // CHECK-NOTES: [[@LINE-3]]:31: note: the use and move are unsequenced1115 }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 moved1121 // CHECK-NOTES: [[@LINE-2]]:27: note: move occurred here1122 // CHECK-NOTES: [[@LINE-3]]:9: note: the use and move are unsequenced1123 // CHECK-NOTES: [[@LINE-4]]:29: warning: 'a' used after it was moved1124 // CHECK-NOTES: [[@LINE-5]]:7: note: move occurred here1125 // CHECK-NOTES: [[@LINE-6]]:29: note: the use and move are unsequenced1126 }1127 // This case is fine because the actual move only happens inside the call to1128 // 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, and1135 // 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 moved1141 // CHECK-NOTES: [[@LINE-2]]:21: note: move occurred here1142 // CHECK-NOTES: [[@LINE-3]]:7: note: the use and move are unsequenced1143 }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 moved1149 // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here1150 // CHECK-NOTES: [[@LINE-3]]:44: note: the use and move are unsequenced1151 }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 indeterminately1161 // sequenced). Again, check that we warn for both orderings.1162 {1163 A a;1164 passByValue(std::move(a), (a = A()));1165 a.foo();1166 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved1167 // CHECK-NOTES: [[@LINE-3]]:17: note: move occurred here1168 }1169 {1170 A a;1171 passByValue((a = A()), std::move(a));1172 a.foo();1173 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved1174 // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here1175 }1176 // Common usage pattern: Move the object to a function that mutates it in some1177 // way, then reassign the result to the object. This pattern is fine.1178 {1179 A a;1180 a = MutateA(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 is1188// used.1189void sequencingOfReinitAndUse() {1190 // Reinitialization and use in function arguments. Again, check both possible1191 // orderings.1192 {1193 A a;1194 std::move(a);1195 passByValue(a.getInt(), (a = A()));1196 // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved1197 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here1198 }1199 {1200 A a;1201 std::move(a);1202 passByValue((a = A()), a.getInt());1203 // CHECK-NOTES: [[@LINE-1]]:28: warning: 'a' used after it was moved1204 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here1205 }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 moved1221 // CHECK-NOTES: [[@LINE-3]]:16: note: move occurred here1222 }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{a.getInt(), 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{std::move(a), a.getInt()};1256 // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved1257 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here1258 }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 moved1263 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here1264 }1265 {1266 // Check the case where the constructed type has a constructor and the1267 // initializer list therefore manifests as a `CXXConstructExpr` instead of1268 // 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 moved1276 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here1277 }1278}1279 1280} // namespace InitializerListSequences1281 1282// A declaration statement containing multiple declarations sequences the1283// 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 moved1293 // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here1294 }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 down1306 // 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 moved1317 // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here1318 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 moved1331 // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here1332 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 init1346// statement (for if and switch) is sequenced before the variable declaration,1347// which in turn is sequenced before the evaluation of the condition. We place1348// all tests inside a for loop to ensure that the checker understands the1349// 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 moved1377 // CHECK-NOTES: [[@LINE-2]]:15: note: move occurred here1378 // CHECK-NOTES: [[@LINE-3]]:43: note: the use and move are unsequenced1379 }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 moved1384 // CHECK-NOTES: [[@LINE-2]]:16: note: move occurred here1385 }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 sequenced1406// 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(consumeA(std::move(a)));1415 // CHECK-NOTES-CXX11: [[@LINE-1]]:5: warning: 'a' used after it was moved1416 // CHECK-NOTES-CXX11: [[@LINE-2]]:21: note: move occurred here1417 // CHECK-NOTES-CXX11: [[@LINE-3]]:5: note: the use and move are unsequenced1418 }1419 {1420 std::unique_ptr<A> a;1421 std::unique_ptr<A> getArg(std::unique_ptr<A> a);1422 getArg(std::move(a))->bar(a->getInt());1423 // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved1424 // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here1425 // CHECK-NOTES-CXX11: [[@LINE-3]]:31: note: the use and move are unsequenced1426 }1427 {1428 A a;1429 // Nominally, the callee `a.bar` is evaluated before the argument1430 // `consumeA(std::move(a))`, but in effect `a` is only accessed after the1431 // call to `A::bar()` happens, i.e. after the argument has been evaluted.1432 a.bar(consumeA(std::move(a)));1433 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved1434 // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here1435 }1436}1437} // namespace CalleeSequencedBeforeArguments1438 1439// Some statements in templates (e.g. null, break and continue statements) may1440// be shared between the uninstantiated and instantiated versions of the1441// template and therefore have multiple parents. Make sure the sequencing code1442// 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 PR330201470 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 push1486#pragma clang diagnostic ignored "-Wgnu-alignof-expression"1487 (void)alignof(Foo{std::move(Bar)});1488#pragma clang diagnostic pop1489 Foo Other{std::move(Bar)};1490}1491void typeId() {1492 Foo Bar;1493 // error: you need to include <typeinfo> before using the 'typeid' operator1494 // (void) typeid(Foo{std::move(Bar)}).name();1495 Foo Other{std::move(Bar)};1496}1497} // namespace UnevalContext1498 1499class CtorInit {1500public:1501 CtorInit(std::string val)1502 : a{val.empty()}, // fine1503 s{std::move(val)},1504 b{val.empty()}1505 // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved1506 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here1507 {}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()}, // fine1519 s{std::move(val)},1520 b{[&] { return val.empty(); }()},1521 // CHECK-NOTES: [[@LINE-1]]:12: warning: 'val' used after it was moved1522 // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here1523 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 moved1528 // CHECK-NOTES: [[@LINE-3]]:11: note: move occurred here1529 }()} {1530 std::move(val);1531 // CHECK-NOTES: [[@LINE-1]]:15: warning: 'val' used after it was moved1532 // CHECK-NOTES: [[@LINE-13]]:9: note: move occurred here1533 std::string val2{};1534 std::move(val2);1535 val2.empty();1536 // CHECK-NOTES: [[@LINE-1]]:5: warning: 'val2' used after it was moved1537 // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here1538 }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()}, // fine1552 b{val.empty()},1553 // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved1554 s{std::move(val)} {} // wrong order1555 // CHECK-NOTES: [[@LINE-1]]:9: note: move occurred here1556 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 moved1579 // CHECK-NOTES: [[@LINE-2]]:34: note: move occurred here1580};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 warn1593 // below1594 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 if1601 // the lambda/std::move was actually called1602 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 moved1609 // CHECK-NOTES: [[@LINE-2]]:19: note: move occurred here1610};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 moved1617 // CHECK-NOTES: [[@LINE-3]]:30: note: move occurred here1618 }1619 1620private:1621 std::string val_;1622};1623 1624namespace issue820231625{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 forwarded1639 // CHECK-NOTES: [[@LINE-3]]:11: note: forward occurred here1640}1641 1642void create() {1643 S s;1644 forward(std::move(s));1645}1646 1647} // namespace issue820231648