1.. title:: clang-tidy - bugprone-use-after-move 2 3bugprone-use-after-move 4======================= 5 6Warns if an object is used after it has been moved, for example: 7 8.. code-block:: c++ 9 10 std::string str = "Hello, world!\n"; 11 std::vector<std::string> messages; 12 messages.emplace_back(std::move(str)); 13 std::cout << str; 14 15The last line will trigger a warning that ``str`` is used after it has been 16moved. 17 18The check does not trigger a warning if the object is reinitialized after the 19move and before the use. For example, no warning will be output for this code: 20 21.. code-block:: c++ 22 23 messages.emplace_back(std::move(str)); 24 str = "Greetings, stranger!\n"; 25 std::cout << str; 26 27The check takes control flow into account. A warning is only emitted if the use 28can be reached from the move. This means that the following code does not 29produce a warning: 30 31.. code-block:: c++ 32 33 if (condition) { 34 messages.emplace_back(std::move(str)); 35 } else { 36 std::cout << str; 37 } 38 39On the other hand, the following code does produce a warning: 40 41.. code-block:: c++ 42 43 for (int i = 0; i < 10; ++i) { 44 std::cout << str; 45 messages.emplace_back(std::move(str)); 46 } 47 48(The use-after-move happens on the second iteration of the loop.) 49 50In some cases, the check may not be able to detect that two branches are 51mutually exclusive. For example (assuming that ``i`` is an int): 52 53.. code-block:: c++ 54 55 if (i == 1) { 56 messages.emplace_back(std::move(str)); 57 } 58 if (i == 2) { 59 std::cout << str; 60 } 61 62In this case, the check will erroneously produce a warning, even though it is 63not possible for both the move and the use to be executed. 64 65An erroneous warning can be silenced by reinitializing the object after the 66move: 67 68.. code-block:: c++ 69 70 if (i == 1) { 71 messages.emplace_back(std::move(str)); 72 str = ""; 73 } 74 if (i == 2) { 75 std::cout << str; 76 } 77 78Subsections below explain more precisely what exactly the check considers to be 79a move, use, and reinitialization. 80 81Unsequenced moves, uses, and reinitializations 82---------------------------------------------- 83 84In many cases, C++ does not make any guarantees about the order in which 85sub-expressions of a statement are evaluated. This means that in code like the 86following, it is not guaranteed whether the use will happen before or after the 87move: 88 89.. code-block:: c++ 90 91 void f(int i, std::vector<int> v); 92 std::vector<int> v = { 1, 2, 3 }; 93 f(v[1], std::move(v)); 94 95In this kind of situation, the check will note that the use and move are 96unsequenced. 97 98The check will also take sequencing rules into account when reinitializations 99occur in the same statement as moves or uses. A reinitialization is only 100considered to reinitialize a variable if it is guaranteed to be evaluated after 101the move and before the use. 102 103Move 104---- 105 106The check currently only considers calls of ``std::move`` on local variables or 107function parameters. It does not check moves of member variables or global 108variables. 109 110Any call of ``std::move`` on a variable is considered to cause a move of that 111variable, even if the result of ``std::move`` is not passed to an rvalue 112reference parameter. 113 114This means that the check will flag a use-after-move even on a type that does 115not define a move constructor or move assignment operator. This is intentional. 116Developers may use ``std::move`` on such a type in the expectation that the type 117will add move semantics in the future. If such a ``std::move`` has the potential 118to cause a use-after-move, we want to warn about it even if the type does not 119implement move semantics yet. 120 121Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference 122parameter, this will always be considered to cause a move, even if the function 123that consumes this parameter does not move from it, or if it does so only 124conditionally. For example, in the following situation, the check will assume 125that a move always takes place: 126 127.. code-block:: c++ 128 129 std::vector<std::string> messages; 130 void f(std::string &&str) { 131 // Only remember the message if it isn't empty. 132 if (!str.empty()) { 133 messages.emplace_back(std::move(str)); 134 } 135 } 136 std::string str = ""; 137 f(std::move(str)); 138 139The check will assume that the last line causes a move, even though, in this 140particular case, it does not. Again, this is intentional. 141 142When analyzing the order in which moves, uses and reinitializations happen (see 143section `Unsequenced moves, uses, and reinitializations`_), the move is assumed 144to occur in whichever function the result of the ``std::move`` is passed to. 145 146Use 147--- 148 149Any occurrence of the moved variable that is not a reinitialization (see below) 150is considered to be a use. 151 152An exception to this are objects of type ``std::unique_ptr``, 153``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior 154(objects of these classes are guaranteed to be empty after they have been moved 155from). Therefore, an object of these classes will only be considered to be used 156if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]`` 157(in the case of ``std::unique_ptr<T []>``) is called on it. 158 159If multiple uses occur after a move, only the first of these is flagged. 160 161Reinitialization 162---------------- 163 164The check considers a variable to be reinitialized in the following cases: 165 166 - The variable occurs on the left-hand side of an assignment. 167 168 - The variable is passed to a function as a non-const pointer or non-const 169 lvalue reference. (It is assumed that the variable may be an out-parameter 170 for the function.) 171 172 - ``clear()`` or ``assign()`` is called on the variable and the variable is of 173 one of the standard container types ``basic_string``, ``vector``, ``deque``, 174 ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``, 175 ``unordered_set``, ``unordered_map``, ``unordered_multiset``, 176 ``unordered_multimap``. 177 178 - ``reset()`` is called on the variable and the variable is of type 179 ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``. 180 181 - A member function marked with the ``[[clang::reinitializes]]`` attribute is 182 called on the variable. 183 184If the variable in question is a struct and an individual member variable of 185that struct is written to, the check does not consider this to be a 186reinitialization -- even if, eventually, all member variables of the struct are 187written to. For example: 188 189.. code-block:: c++ 190 191 struct S { 192 std::string str; 193 int i; 194 }; 195 S s = { "Hello, world!\n", 42 }; 196 S s_other = std::move(s); 197 s.str = "Lorem ipsum"; 198 s.i = 99; 199 200The check will not consider ``s`` to be reinitialized after the last line; 201instead, the line that assigns to ``s.str`` will be flagged as a use-after-move. 202This is intentional as this pattern of reinitializing a struct is error-prone. 203For example, if an additional member variable is added to ``S``, it is easy to 204forget to add the reinitialization for this additional member. Instead, it is 205safer to assign to the entire struct in one go, and this will also avoid the 206use-after-move warning. 207