| 1 | // RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -verify %s |
| 2 | // RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s |
| 3 | |
| 4 | // definitions for std::move |
| 5 | namespace std { |
| 6 | inline namespace foo { |
| 7 | template <class T> struct remove_reference { typedef T type; }; |
| 8 | template <class T> struct remove_reference<T&> { typedef T type; }; |
| 9 | template <class T> struct remove_reference<T&&> { typedef T type; }; |
| 10 | |
| 11 | template <class T> typename remove_reference<T>::type &&move(T &&t); |
| 12 | } // namespace foo |
| 13 | } // namespace std |
| 14 | |
| 15 | struct Instrument { |
| 16 | Instrument() {} |
| 17 | Instrument(Instrument&&) { /* MOVE */ } |
| 18 | Instrument(const Instrument&) { /* COPY */ } |
| 19 | }; |
| 20 | struct ConvertFromBase { Instrument i; }; |
| 21 | struct ConvertFromDerived { Instrument i; }; |
| 22 | struct Base { |
| 23 | Instrument i; |
| 24 | operator ConvertFromBase() const& { return ConvertFromBase{i}; } |
| 25 | operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; } |
| 26 | }; |
| 27 | struct Derived : public Base { |
| 28 | operator ConvertFromDerived() const& { return ConvertFromDerived{i}; } |
| 29 | operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; } |
| 30 | }; |
| 31 | struct ConstructFromBase { |
| 32 | Instrument i; |
| 33 | ConstructFromBase(const Base& b): i(b.i) {} |
| 34 | ConstructFromBase(Base&& b): i(std::move(b.i)) {} |
| 35 | }; |
| 36 | struct ConstructFromDerived { |
| 37 | Instrument i; |
| 38 | ConstructFromDerived(const Derived& d): i(d.i) {} |
| 39 | ConstructFromDerived(Derived&& d): i(std::move(d.i)) {} |
| 40 | }; |
| 41 | |
| 42 | struct TrivialInstrument { |
| 43 | int i = 42; |
| 44 | }; |
| 45 | struct ConvertFromTrivialBase { TrivialInstrument i; }; |
| 46 | struct ConvertFromTrivialDerived { TrivialInstrument i; }; |
| 47 | struct TrivialBase { |
| 48 | TrivialInstrument i; |
| 49 | operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; } |
| 50 | operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; } |
| 51 | }; |
| 52 | struct TrivialDerived : public TrivialBase { |
| 53 | operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; } |
| 54 | operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; } |
| 55 | }; |
| 56 | struct ConstructFromTrivialBase { |
| 57 | TrivialInstrument i; |
| 58 | ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {} |
| 59 | ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {} |
| 60 | }; |
| 61 | struct ConstructFromTrivialDerived { |
| 62 | TrivialInstrument i; |
| 63 | ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {} |
| 64 | ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {} |
| 65 | }; |
| 66 | |
| 67 | Derived test1() { |
| 68 | Derived d1; |
| 69 | return d1; // ok |
| 70 | } |
| 71 | Base test2() { |
| 72 | Derived d2; |
| 73 | return d2; // e1 |
| 74 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 75 | // expected-note@-2{{to avoid copying}} |
| 76 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)" |
| 77 | } |
| 78 | ConstructFromDerived test3() { |
| 79 | Derived d3; |
| 80 | return d3; // e2-cxx11 |
| 81 | // expected-warning@-1{{would have been copied despite being returned by name}} |
| 82 | // expected-note@-2{{to avoid copying on older compilers}} |
| 83 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)" |
| 84 | } |
| 85 | ConstructFromBase test4() { |
| 86 | Derived d4; |
| 87 | return d4; // e3 |
| 88 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 89 | // expected-note@-2{{to avoid copying}} |
| 90 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)" |
| 91 | } |
| 92 | ConvertFromDerived test5() { |
| 93 | Derived d5; |
| 94 | return d5; // e4 |
| 95 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 96 | // expected-note@-2{{to avoid copying}} |
| 97 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)" |
| 98 | } |
| 99 | ConvertFromBase test6() { |
| 100 | Derived d6; |
| 101 | return d6; // e5 |
| 102 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 103 | // expected-note@-2{{to avoid copying}} |
| 104 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)" |
| 105 | } |
| 106 | |
| 107 | // These test cases should not produce the warning. |
| 108 | Derived ok1() { Derived d; return d; } |
| 109 | Base ok2() { Derived d; return static_cast<Derived&&>(d); } |
| 110 | ConstructFromDerived ok3() { Derived d; return static_cast<Derived&&>(d); } |
| 111 | ConstructFromBase ok4() { Derived d; return static_cast<Derived&&>(d); } |
| 112 | ConvertFromDerived ok5() { Derived d; return static_cast<Derived&&>(d); } |
| 113 | ConvertFromBase ok6() { Derived d; return static_cast<Derived&&>(d); } |
| 114 | |
| 115 | // If the target is an lvalue reference, assume it's not safe to move from. |
| 116 | Derived ok_plvalue1(Derived& d) { return d; } |
| 117 | Base ok_plvalue2(Derived& d) { return d; } |
| 118 | ConstructFromDerived ok_plvalue3(const Derived& d) { return d; } |
| 119 | ConstructFromBase ok_plvalue4(Derived& d) { return d; } |
| 120 | ConvertFromDerived ok_plvalue5(Derived& d) { return d; } |
| 121 | ConvertFromBase ok_plvalue6(Derived& d) { return d; } |
| 122 | |
| 123 | Derived ok_lvalue1(Derived *p) { Derived& d = *p; return d; } |
| 124 | Base ok_lvalue2(Derived *p) { Derived& d = *p; return d; } |
| 125 | ConstructFromDerived ok_lvalue3(Derived *p) { const Derived& d = *p; return d; } |
| 126 | ConstructFromBase ok_lvalue4(Derived *p) { Derived& d = *p; return d; } |
| 127 | ConvertFromDerived ok_lvalue5(Derived *p) { Derived& d = *p; return d; } |
| 128 | ConvertFromBase ok_lvalue6(Derived *p) { Derived& d = *p; return d; } |
| 129 | |
| 130 | // If the target is a global, assume it's not safe to move from. |
| 131 | static Derived global_d; |
| 132 | Derived ok_global1() { return global_d; } |
| 133 | Base ok_global2() { return global_d; } |
| 134 | ConstructFromDerived ok_global3() { return global_d; } |
| 135 | ConstructFromBase ok_global4() { return global_d; } |
| 136 | ConvertFromDerived ok_global5() { return global_d; } |
| 137 | ConvertFromBase ok_global6() { return global_d; } |
| 138 | |
| 139 | // If the target's copy constructor is trivial, assume the programmer doesn't care. |
| 140 | TrivialDerived ok_trivial1(TrivialDerived d) { return d; } |
| 141 | TrivialBase ok_trivial2(TrivialDerived d) { return d; } |
| 142 | ConstructFromTrivialDerived ok_trivial3(TrivialDerived d) { return d; } |
| 143 | ConstructFromTrivialBase ok_trivial4(TrivialDerived d) { return d; } |
| 144 | ConvertFromTrivialDerived ok_trivial5(TrivialDerived d) { return d; } |
| 145 | ConvertFromTrivialBase ok_trivial6(TrivialDerived d) { return d; } |
| 146 | |
| 147 | // If the target is a parameter, do apply the diagnostic. |
| 148 | Derived testParam1(Derived d) { return d; } |
| 149 | Base testParam2(Derived d) { |
| 150 | return d; // e6 |
| 151 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 152 | // expected-note@-2{{to avoid copying}} |
| 153 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 154 | } |
| 155 | ConstructFromDerived testParam3(Derived d) { |
| 156 | return d; // e7-cxx11 |
| 157 | // expected-warning@-1{{would have been copied despite being returned by name}} |
| 158 | // expected-note@-2{{to avoid copying on older compilers}} |
| 159 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 160 | } |
| 161 | ConstructFromBase testParam4(Derived d) { |
| 162 | return d; // e8 |
| 163 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 164 | // expected-note@-2{{to avoid copying}} |
| 165 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 166 | } |
| 167 | ConvertFromDerived testParam5(Derived d) { |
| 168 | return d; // e9 |
| 169 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 170 | // expected-note@-2{{to avoid copying}} |
| 171 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 172 | } |
| 173 | ConvertFromBase testParam6(Derived d) { |
| 174 | return d; // e10 |
| 175 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 176 | // expected-note@-2{{to avoid copying}} |
| 177 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 178 | } |
| 179 | |
| 180 | // If the target is an rvalue reference parameter, do apply the diagnostic. |
| 181 | Derived testRParam1(Derived&& d) { |
| 182 | return d; // e11 |
| 183 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 184 | // expected-note@-2{{to avoid copying}} |
| 185 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 186 | } |
| 187 | Base testRParam2(Derived&& d) { |
| 188 | return d; // e12 |
| 189 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 190 | // expected-note@-2{{to avoid copying}} |
| 191 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 192 | } |
| 193 | ConstructFromDerived testRParam3(Derived&& d) { |
| 194 | return d; // e13 |
| 195 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 196 | // expected-note@-2{{to avoid copying}} |
| 197 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 198 | } |
| 199 | ConstructFromBase testRParam4(Derived&& d) { |
| 200 | return d; // e14 |
| 201 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 202 | // expected-note@-2{{to avoid copying}} |
| 203 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 204 | } |
| 205 | ConvertFromDerived testRParam5(Derived&& d) { |
| 206 | return d; // e15 |
| 207 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 208 | // expected-note@-2{{to avoid copying}} |
| 209 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 210 | } |
| 211 | ConvertFromBase testRParam6(Derived&& d) { |
| 212 | return d; // e16 |
| 213 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 214 | // expected-note@-2{{to avoid copying}} |
| 215 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| 216 | } |
| 217 | |
| 218 | // But if the return type is a reference type, then moving would be wrong. |
| 219 | Derived& testRetRef1(Derived&& d) { return d; } |
| 220 | Base& testRetRef2(Derived&& d) { return d; } |
| 221 | auto&& testRetRef3(Derived&& d) { return d; } |
| 222 | decltype(auto) testRetRef4(Derived&& d) { return (d); } |
| 223 | |
| 224 | // As long as we're checking parentheses, make sure parentheses don't disable the warning. |
| 225 | Base testParens1() { |
| 226 | Derived d; |
| 227 | return (d); // e17 |
| 228 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 229 | // expected-note@-2{{to avoid copying}} |
| 230 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" |
| 231 | } |
| 232 | ConstructFromDerived testParens2() { |
| 233 | Derived d; |
| 234 | return (d); // e18-cxx11 |
| 235 | // expected-warning@-1{{would have been copied despite being returned by name}} |
| 236 | // expected-note@-2{{to avoid copying}} |
| 237 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" |
| 238 | } |
| 239 | |
| 240 | |
| 241 | // If the target is a catch-handler parameter, do apply the diagnostic. |
| 242 | void throw_derived(); |
| 243 | Derived testEParam1() { |
| 244 | try { throw_derived(); } catch (Derived d) { return d; } // e19 |
| 245 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 246 | // expected-note@-2{{to avoid copying}} |
| 247 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 248 | __builtin_unreachable(); |
| 249 | } |
| 250 | Base testEParam2() { |
| 251 | try { throw_derived(); } catch (Derived d) { return d; } // e20 |
| 252 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 253 | // expected-note@-2{{to avoid copying}} |
| 254 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 255 | __builtin_unreachable(); |
| 256 | } |
| 257 | ConstructFromDerived testEParam3() { |
| 258 | try { throw_derived(); } catch (Derived d) { return d; } // e21 |
| 259 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 260 | // expected-note@-2{{to avoid copying}} |
| 261 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 262 | __builtin_unreachable(); |
| 263 | } |
| 264 | ConstructFromBase testEParam4() { |
| 265 | try { throw_derived(); } catch (Derived d) { return d; } // e22 |
| 266 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 267 | // expected-note@-2{{to avoid copying}} |
| 268 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 269 | __builtin_unreachable(); |
| 270 | } |
| 271 | ConvertFromDerived testEParam5() { |
| 272 | try { throw_derived(); } catch (Derived d) { return d; } // e23 |
| 273 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 274 | // expected-note@-2{{to avoid copying}} |
| 275 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 276 | __builtin_unreachable(); |
| 277 | } |
| 278 | ConvertFromBase testEParam6() { |
| 279 | try { throw_derived(); } catch (Derived d) { return d; } // e24 |
| 280 | // expected-warning@-1{{will be copied despite being returned by name}} |
| 281 | // expected-note@-2{{to avoid copying}} |
| 282 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| 283 | __builtin_unreachable(); |
| 284 | } |
| 285 | |
| 286 | // If the exception variable is an lvalue reference, we cannot be sure |
| 287 | // that we own it; it is extremely contrived, but possible, for this to |
| 288 | // be a reference to an exception object that was thrown via |
| 289 | // `std::rethrow_exception(xp)` in Thread A, and meanwhile somebody else |
| 290 | // has got a copy of `xp` in Thread B, so that moving out of this object |
| 291 | // in Thread A would be observable (and racy) with respect to Thread B. |
| 292 | // Therefore assume it's not safe to move from. |
| 293 | Derived ok_REParam1() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 294 | Base ok_REParam2() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 295 | ConstructFromDerived ok_REParam3() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 296 | ConstructFromBase ok_REParam4() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 297 | ConvertFromDerived ok_REParam5() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 298 | ConvertFromBase ok_REParam6() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| 299 | |
| 300 | Derived ok_CEParam1() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 301 | Base ok_CEParam2() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 302 | ConstructFromDerived ok_CEParam3() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 303 | ConstructFromBase ok_CEParam4() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 304 | ConvertFromDerived ok_CEParam5() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 305 | ConvertFromBase ok_CEParam6() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| 306 | |
| 307 | // If rvalue overload resolution would find a copy constructor anyway, |
| 308 | // or if the copy constructor actually selected is trivial, then don't warn. |
| 309 | struct TriviallyCopyable {}; |
| 310 | struct OnlyCopyable { |
| 311 | OnlyCopyable() = default; |
| 312 | OnlyCopyable(const OnlyCopyable&) {} |
| 313 | }; |
| 314 | |
| 315 | TriviallyCopyable ok_copy1() { TriviallyCopyable c; return c; } |
| 316 | OnlyCopyable ok_copy2() { OnlyCopyable c; return c; } |
| 317 | TriviallyCopyable ok_copyparam1(TriviallyCopyable c) { return c; } |
| 318 | OnlyCopyable ok_copyparam2(OnlyCopyable c) { return c; } |
| 319 | |
| 320 | void test_throw1(Derived&& d) { |
| 321 | throw d; // e25 |
| 322 | // expected-warning@-1{{will be copied despite being thrown by name}} |
| 323 | // expected-note@-2{{to avoid copying}} |
| 324 | // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:12}:"std::move(d)" |
| 325 | } |
| 326 | |
| 327 | void ok_throw1() { Derived d; throw d; } |
| 328 | void ok_throw2(Derived d) { throw d; } |
| 329 | void ok_throw3(Derived& d) { throw d; } |
| 330 | void ok_throw4(Derived d) { throw std::move(d); } |
| 331 | void ok_throw5(Derived& d) { throw std::move(d); } |
| 332 | void ok_throw6(Derived& d) { throw static_cast<Derived&&>(d); } |
| 333 | void ok_throw7(TriviallyCopyable d) { throw d; } |
| 334 | void ok_throw8(OnlyCopyable d) { throw d; } |
| 335 | |