| 1 | // RUN: %check_clang_tidy -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \ |
| 2 | // RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: true}}" -- -fno-delayed-template-parsing |
| 3 | // RUN: %check_clang_tidy -check-suffix=,CXX14 -std=c++14 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \ |
| 4 | // RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: true}}" -- -fno-delayed-template-parsing |
| 5 | // RUN: %check_clang_tidy -check-suffix=,NOSUBEXPR -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \ |
| 6 | // RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: false, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: true}}" -- -fno-delayed-template-parsing |
| 7 | // RUN: %check_clang_tidy -check-suffix=,UNNAMED -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \ |
| 8 | // RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: false, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: true}}" -- -fno-delayed-template-parsing |
| 9 | // RUN: %check_clang_tidy -check-suffix=,NONDEDUCED -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \ |
| 10 | // RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: true, cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: false}}" -- -fno-delayed-template-parsing |
| 11 | |
| 12 | // NOLINTBEGIN |
| 13 | namespace std { |
| 14 | template <typename> |
| 15 | struct remove_reference; |
| 16 | |
| 17 | template <typename _Tp> struct remove_reference { typedef _Tp type; }; |
| 18 | template <typename _Tp> struct remove_reference<_Tp&> { typedef _Tp type; }; |
| 19 | template <typename _Tp> struct remove_reference<_Tp&&> { typedef _Tp type; }; |
| 20 | |
| 21 | template <typename _Tp> |
| 22 | constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept; |
| 23 | |
| 24 | template <typename _Tp> |
| 25 | constexpr _Tp && |
| 26 | forward(typename remove_reference<_Tp>::type &__t) noexcept; |
| 27 | |
| 28 | } |
| 29 | // NOLINTEND |
| 30 | |
| 31 | struct Obj { |
| 32 | Obj(); |
| 33 | Obj(const Obj&); |
| 34 | Obj& operator=(const Obj&); |
| 35 | Obj(Obj&&); |
| 36 | Obj& operator=(Obj&&); |
| 37 | void member() const; |
| 38 | }; |
| 39 | |
| 40 | void consumes_object(Obj); |
| 41 | |
| 42 | void never_moves_param(Obj&& o) { |
| 43 | // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 44 | o.member(); |
| 45 | } |
| 46 | |
| 47 | void just_a_declaration(Obj&& o); |
| 48 | |
| 49 | void copies_object(Obj&& o) { |
| 50 | // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 51 | Obj copy = o; |
| 52 | } |
| 53 | |
| 54 | template <typename T> |
| 55 | void never_moves_param_template(Obj&& o, T t) { |
| 56 | // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 57 | o.member(); |
| 58 | } |
| 59 | |
| 60 | void never_moves_params(Obj&& o1, Obj&& o2) { |
| 61 | // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 62 | // CHECK-MESSAGES: :[[@LINE-2]]:41: warning: rvalue reference parameter 'o2' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 63 | } |
| 64 | |
| 65 | void never_moves_some_params(Obj&& o1, Obj&& o2) { |
| 66 | // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 67 | |
| 68 | Obj other{std::move(o2)}; |
| 69 | } |
| 70 | |
| 71 | void never_moves_unnamed(Obj&&) {} |
| 72 | // CHECK-MESSAGES-UNNAMED: :[[@LINE-1]]:31: warning: rvalue reference parameter '' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 73 | |
| 74 | void never_moves_mixed(Obj o1, Obj&& o2) { |
| 75 | // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: rvalue reference parameter 'o2' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 76 | } |
| 77 | |
| 78 | void lambda_captures_parameter_as_value(Obj&& o) { |
| 79 | auto f = [o]() { |
| 80 | consumes_object(std::move(o)); |
| 81 | }; |
| 82 | // CHECK-MESSAGES: :[[@LINE-4]]:47: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 83 | } |
| 84 | |
| 85 | void lambda_captures_parameter_as_value_nested(Obj&& o) { |
| 86 | // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 87 | auto f = [&o]() { |
| 88 | auto f_nested = [o]() { |
| 89 | consumes_object(std::move(o)); |
| 90 | }; |
| 91 | }; |
| 92 | auto f2 = [o]() { |
| 93 | auto f_nested = [&o]() { |
| 94 | consumes_object(std::move(o)); |
| 95 | }; |
| 96 | }; |
| 97 | auto f3 = [o]() { |
| 98 | auto f_nested = [&o]() { |
| 99 | auto f_nested_inner = [&o]() { |
| 100 | consumes_object(std::move(o)); |
| 101 | }; |
| 102 | }; |
| 103 | }; |
| 104 | auto f4 = [&o]() { |
| 105 | auto f_nested = [&o]() { |
| 106 | auto f_nested_inner = [o]() { |
| 107 | consumes_object(std::move(o)); |
| 108 | }; |
| 109 | }; |
| 110 | }; |
| 111 | } |
| 112 | |
| 113 | void misc_lambda_checks() { |
| 114 | auto never_moves = [](Obj&& o1) { |
| 115 | Obj other{o1}; |
| 116 | }; |
| 117 | // CHECK-MESSAGES: :[[@LINE-3]]:31: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 118 | |
| 119 | #if __cplusplus >= 201402L |
| 120 | auto never_moves_with_auto_param = [](Obj&& o1, auto& v) { |
| 121 | Obj other{o1}; |
| 122 | }; |
| 123 | // CHECK-MESSAGES-CXX14: :[[@LINE-3]]:47: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 124 | #endif |
| 125 | } |
| 126 | |
| 127 | template <typename T> |
| 128 | void forwarding_ref(T&& t) { |
| 129 | t.member(); |
| 130 | } |
| 131 | |
| 132 | template <typename T> |
| 133 | void forwarding_ref_forwarded(T&& t) { |
| 134 | forwarding_ref(std::forward<T>(t)); |
| 135 | } |
| 136 | |
| 137 | template <typename... Ts> |
| 138 | void type_pack(Ts&&... ts) { |
| 139 | (forwarding_ref(std::forward<Ts>(ts)), ...); |
| 140 | } |
| 141 | |
| 142 | void call_forwarding_functions() { |
| 143 | Obj o; |
| 144 | |
| 145 | forwarding_ref(t: Obj{}); |
| 146 | type_pack(ts: Obj{}); |
| 147 | type_pack(ts: Obj{}, ts&: o); |
| 148 | type_pack(ts: Obj{}, ts: Obj{}); |
| 149 | } |
| 150 | |
| 151 | void moves_parameter(Obj&& o) { |
| 152 | Obj moved = std::move(o); |
| 153 | } |
| 154 | |
| 155 | void (Obj&& o) { |
| 156 | Obj moved = std::move((o)); |
| 157 | } |
| 158 | |
| 159 | void does_not_move_in_evaluated(Obj&& o) { |
| 160 | // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 161 | using result_t = decltype(std::move(o)); |
| 162 | unsigned size = sizeof(std::move(o)); |
| 163 | Obj moved = o; |
| 164 | } |
| 165 | |
| 166 | template <typename T1, typename T2> |
| 167 | struct mypair { |
| 168 | T1 first; |
| 169 | T2 second; |
| 170 | }; |
| 171 | |
| 172 | void moves_member_of_parameter(mypair<Obj, Obj>&& pair) { |
| 173 | // CHECK-MESSAGES-NOSUBEXPR: :[[@LINE-1]]:51: warning: rvalue reference parameter 'pair' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 174 | Obj a = std::move(pair.first); |
| 175 | Obj b = std::move(pair.second); |
| 176 | } |
| 177 | |
| 178 | template <typename T> |
| 179 | struct myoptional { |
| 180 | T& operator*() &; |
| 181 | T&& operator*() &&; |
| 182 | }; |
| 183 | |
| 184 | void moves_deref_optional(myoptional<Obj>&& opt) { |
| 185 | // CHECK-MESSAGES-NOSUBEXPR: :[[@LINE-1]]:45: warning: rvalue reference parameter 'opt' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 186 | Obj other = std::move(*opt); |
| 187 | } |
| 188 | |
| 189 | void moves_optional_then_deref_resulting_rvalue(myoptional<Obj>&& opt) { |
| 190 | Obj other = *std::move(opt); |
| 191 | } |
| 192 | |
| 193 | void pass_by_lvalue_reference(Obj& o) { |
| 194 | o.member(); |
| 195 | } |
| 196 | |
| 197 | void pass_by_value(Obj o) { |
| 198 | o.member(); |
| 199 | } |
| 200 | |
| 201 | void pass_by_const_lvalue_reference(const Obj& o) { |
| 202 | o.member(); |
| 203 | } |
| 204 | |
| 205 | void pass_by_const_lvalue_reference(const Obj&& o) { |
| 206 | o.member(); |
| 207 | } |
| 208 | |
| 209 | void lambda_captures_parameter_as_reference(Obj&& o) { |
| 210 | auto f = [&o]() { |
| 211 | consumes_object(std::move(o)); |
| 212 | }; |
| 213 | } |
| 214 | |
| 215 | void lambda_captures_parameter_as_reference_nested(Obj&& o) { |
| 216 | auto f = [&o]() { |
| 217 | auto f_nested = [&o]() { |
| 218 | auto f_nested2 = [&o]() { |
| 219 | consumes_object(std::move(o)); |
| 220 | }; |
| 221 | }; |
| 222 | }; |
| 223 | } |
| 224 | |
| 225 | #if __cplusplus >= 201402L |
| 226 | void lambda_captures_parameter_generalized(Obj&& o) { |
| 227 | auto f = [o = std::move(o)]() { |
| 228 | consumes_object(std::move(o)); |
| 229 | }; |
| 230 | } |
| 231 | #endif |
| 232 | |
| 233 | void negative_lambda_checks() { |
| 234 | auto never_moves_nested = [](Obj&& o1) { |
| 235 | auto nested = [&]() { |
| 236 | Obj other{std::move(o1)}; |
| 237 | }; |
| 238 | }; |
| 239 | |
| 240 | #if __cplusplus >= 201402L |
| 241 | auto auto_lvalue_ref_param = [](auto& o1) { |
| 242 | Obj other{o1}; |
| 243 | }; |
| 244 | |
| 245 | auto auto_forwarding_ref_param = [](auto&& o1) { |
| 246 | Obj other{o1}; |
| 247 | }; |
| 248 | |
| 249 | auto does_move_auto_rvalue_ref_param = [](auto&& o1) { |
| 250 | Obj other{std::forward(o1)}; |
| 251 | }; |
| 252 | #endif |
| 253 | |
| 254 | auto does_move = [](Obj&& o1) { |
| 255 | Obj other{std::move(o1)}; |
| 256 | }; |
| 257 | |
| 258 | auto not_rvalue_ref = [](Obj& o1) { |
| 259 | Obj other{std::move(o1)}; |
| 260 | }; |
| 261 | |
| 262 | Obj local; |
| 263 | auto captures = [local]() { }; |
| 264 | } |
| 265 | |
| 266 | struct AClass { |
| 267 | void member_with_lambda_no_move(Obj&& o) { |
| 268 | // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 269 | auto captures_this = [=, this]() { |
| 270 | Obj other = std::move(o); |
| 271 | }; |
| 272 | } |
| 273 | void member_with_lambda_that_moves(Obj&& o) { |
| 274 | auto captures_this = [&, this]() { |
| 275 | Obj other = std::move(o); |
| 276 | }; |
| 277 | } |
| 278 | }; |
| 279 | |
| 280 | void useless_move(Obj&& o) { |
| 281 | // FIXME - The object is not actually moved from - this should probably be |
| 282 | // flagged by *some* check. Which one? |
| 283 | std::move(o); |
| 284 | } |
| 285 | |
| 286 | template <typename> |
| 287 | class TemplatedClass; |
| 288 | |
| 289 | template <typename T> |
| 290 | void unresolved_lookup(TemplatedClass<T>&& o) { |
| 291 | TemplatedClass<T> moved = std::move(o); |
| 292 | } |
| 293 | |
| 294 | struct DefinesMove { |
| 295 | DefinesMove(DefinesMove&& rhs) : o(std::move(rhs.o)) { } |
| 296 | DefinesMove& operator=(DefinesMove&& rhs) { |
| 297 | if (this != &rhs) { |
| 298 | o = std::move(rhs.o); |
| 299 | } |
| 300 | return *this; |
| 301 | } |
| 302 | Obj o; |
| 303 | }; |
| 304 | |
| 305 | struct DeclaresMove { |
| 306 | DeclaresMove(DeclaresMove&& rhs); |
| 307 | DeclaresMove& operator=(DeclaresMove&& rhs); |
| 308 | }; |
| 309 | |
| 310 | struct AnotherObj { |
| 311 | AnotherObj(Obj&& o) : o(std::move(o)) {} |
| 312 | AnotherObj(Obj&& o, int) { o = std::move(o); } |
| 313 | Obj o; |
| 314 | }; |
| 315 | |
| 316 | template <class T> |
| 317 | struct AClassTemplate { |
| 318 | AClassTemplate(T&& t) {} |
| 319 | // CHECK-MESSAGES-NONDEDUCED: :[[@LINE-1]]:22: warning: rvalue reference parameter 't' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 320 | |
| 321 | void moves(T&& t) { |
| 322 | T other = std::move(t); |
| 323 | } |
| 324 | void never_moves(T&& t) {} |
| 325 | // CHECK-MESSAGES-NONDEDUCED: :[[@LINE-1]]:24: warning: rvalue reference parameter 't' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 326 | }; |
| 327 | |
| 328 | void instantiate_a_class_template() { |
| 329 | Obj o; |
| 330 | AClassTemplate<Obj> withObj{std::move(o)}; |
| 331 | withObj.never_moves(t: std::move(o)); |
| 332 | |
| 333 | AClassTemplate<Obj&> withObjRef(o); |
| 334 | withObjRef.never_moves(t&: o); |
| 335 | } |
| 336 | |
| 337 | namespace gh68209 { |
| 338 | void f1([[maybe_unused]] int&& x) {} |
| 339 | |
| 340 | void f2(__attribute__((unused)) int&& x) {} |
| 341 | |
| 342 | void f3(int&& x) {} |
| 343 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: rvalue reference parameter 'x' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 344 | |
| 345 | template <typename T> |
| 346 | void f4([[maybe_unused]] T&& x) {} |
| 347 | |
| 348 | template <typename T> |
| 349 | void f5(__attribute((unused)) T&& x) {} |
| 350 | |
| 351 | template<typename T> |
| 352 | void f6(T&& x) {} |
| 353 | |
| 354 | void f7([[maybe_unused]] int&& x) { x += 1; } |
| 355 | // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: rvalue reference parameter 'x' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 356 | |
| 357 | void f8(__attribute__((unused)) int&& x) { x += 1; } |
| 358 | // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: rvalue reference parameter 'x' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved] |
| 359 | } // namespace gh68209 |
| 360 | |
| 361 | namespace gh69412 { |
| 362 | struct S |
| 363 | { |
| 364 | S(const int&); |
| 365 | S(int&&) = delete; |
| 366 | |
| 367 | void foo(int&&) = delete; |
| 368 | }; |
| 369 | } // namespace gh69412 |
| 370 | |