| // RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -verify %s |
| // 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 |
| |
| // definitions for std::move |
| namespace std { |
| inline namespace foo { |
| template <class T> struct remove_reference { typedef T type; }; |
| template <class T> struct remove_reference<T&> { typedef T type; }; |
| template <class T> struct remove_reference<T&&> { typedef T type; }; |
| |
| template <class T> typename remove_reference<T>::type &&move(T &&t); |
| } // namespace foo |
| } // namespace std |
| |
| struct Instrument { |
| Instrument() {} |
| Instrument(Instrument&&) { /* MOVE */ } |
| Instrument(const Instrument&) { /* COPY */ } |
| }; |
| struct ConvertFromBase { Instrument i; }; |
| struct ConvertFromDerived { Instrument i; }; |
| struct Base { |
| Instrument i; |
| operator ConvertFromBase() const& { return ConvertFromBase{i}; } |
| operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; } |
| }; |
| struct Derived : public Base { |
| operator ConvertFromDerived() const& { return ConvertFromDerived{i}; } |
| operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; } |
| }; |
| struct ConstructFromBase { |
| Instrument i; |
| ConstructFromBase(const Base& b): i(b.i) {} |
| ConstructFromBase(Base&& b): i(std::move(b.i)) {} |
| }; |
| struct ConstructFromDerived { |
| Instrument i; |
| ConstructFromDerived(const Derived& d): i(d.i) {} |
| ConstructFromDerived(Derived&& d): i(std::move(d.i)) {} |
| }; |
| |
| struct TrivialInstrument { |
| int i = 42; |
| }; |
| struct ConvertFromTrivialBase { TrivialInstrument i; }; |
| struct ConvertFromTrivialDerived { TrivialInstrument i; }; |
| struct TrivialBase { |
| TrivialInstrument i; |
| operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; } |
| operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; } |
| }; |
| struct TrivialDerived : public TrivialBase { |
| operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; } |
| operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; } |
| }; |
| struct ConstructFromTrivialBase { |
| TrivialInstrument i; |
| ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {} |
| ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {} |
| }; |
| struct ConstructFromTrivialDerived { |
| TrivialInstrument i; |
| ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {} |
| ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {} |
| }; |
| |
| Derived test1() { |
| Derived d1; |
| return d1; // ok |
| } |
| Base test2() { |
| Derived d2; |
| return d2; // e1 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)" |
| } |
| ConstructFromDerived test3() { |
| Derived d3; |
| return d3; // e2-cxx11 |
| // expected-warning@-1{{would have been copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying on older compilers}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)" |
| } |
| ConstructFromBase test4() { |
| Derived d4; |
| return d4; // e3 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)" |
| } |
| ConvertFromDerived test5() { |
| Derived d5; |
| return d5; // e4 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)" |
| } |
| ConvertFromBase test6() { |
| Derived d6; |
| return d6; // e5 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)" |
| } |
| |
| // These test cases should not produce the warning. |
| Derived ok1() { Derived d; return d; } |
| Base ok2() { Derived d; return static_cast<Derived&&>(d); } |
| ConstructFromDerived ok3() { Derived d; return static_cast<Derived&&>(d); } |
| ConstructFromBase ok4() { Derived d; return static_cast<Derived&&>(d); } |
| ConvertFromDerived ok5() { Derived d; return static_cast<Derived&&>(d); } |
| ConvertFromBase ok6() { Derived d; return static_cast<Derived&&>(d); } |
| |
| // If the target is an lvalue reference, assume it's not safe to move from. |
| Derived ok_plvalue1(Derived& d) { return d; } |
| Base ok_plvalue2(Derived& d) { return d; } |
| ConstructFromDerived ok_plvalue3(const Derived& d) { return d; } |
| ConstructFromBase ok_plvalue4(Derived& d) { return d; } |
| ConvertFromDerived ok_plvalue5(Derived& d) { return d; } |
| ConvertFromBase ok_plvalue6(Derived& d) { return d; } |
| |
| Derived ok_lvalue1(Derived *p) { Derived& d = *p; return d; } |
| Base ok_lvalue2(Derived *p) { Derived& d = *p; return d; } |
| ConstructFromDerived ok_lvalue3(Derived *p) { const Derived& d = *p; return d; } |
| ConstructFromBase ok_lvalue4(Derived *p) { Derived& d = *p; return d; } |
| ConvertFromDerived ok_lvalue5(Derived *p) { Derived& d = *p; return d; } |
| ConvertFromBase ok_lvalue6(Derived *p) { Derived& d = *p; return d; } |
| |
| // If the target is a global, assume it's not safe to move from. |
| static Derived global_d; |
| Derived ok_global1() { return global_d; } |
| Base ok_global2() { return global_d; } |
| ConstructFromDerived ok_global3() { return global_d; } |
| ConstructFromBase ok_global4() { return global_d; } |
| ConvertFromDerived ok_global5() { return global_d; } |
| ConvertFromBase ok_global6() { return global_d; } |
| |
| // If the target's copy constructor is trivial, assume the programmer doesn't care. |
| TrivialDerived ok_trivial1(TrivialDerived d) { return d; } |
| TrivialBase ok_trivial2(TrivialDerived d) { return d; } |
| ConstructFromTrivialDerived ok_trivial3(TrivialDerived d) { return d; } |
| ConstructFromTrivialBase ok_trivial4(TrivialDerived d) { return d; } |
| ConvertFromTrivialDerived ok_trivial5(TrivialDerived d) { return d; } |
| ConvertFromTrivialBase ok_trivial6(TrivialDerived d) { return d; } |
| |
| // If the target is a parameter, do apply the diagnostic. |
| Derived testParam1(Derived d) { return d; } |
| Base testParam2(Derived d) { |
| return d; // e6 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConstructFromDerived testParam3(Derived d) { |
| return d; // e7-cxx11 |
| // expected-warning@-1{{would have been copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying on older compilers}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConstructFromBase testParam4(Derived d) { |
| return d; // e8 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConvertFromDerived testParam5(Derived d) { |
| return d; // e9 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConvertFromBase testParam6(Derived d) { |
| return d; // e10 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| |
| // If the target is an rvalue reference parameter, do apply the diagnostic. |
| Derived testRParam1(Derived&& d) { |
| return d; // e11 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| Base testRParam2(Derived&& d) { |
| return d; // e12 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConstructFromDerived testRParam3(Derived&& d) { |
| return d; // e13 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConstructFromBase testRParam4(Derived&& d) { |
| return d; // e14 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConvertFromDerived testRParam5(Derived&& d) { |
| return d; // e15 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| ConvertFromBase testRParam6(Derived&& d) { |
| return d; // e16 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" |
| } |
| |
| // But if the return type is a reference type, then moving would be wrong. |
| Derived& testRetRef1(Derived&& d) { return d; } |
| Base& testRetRef2(Derived&& d) { return d; } |
| auto&& testRetRef3(Derived&& d) { return d; } |
| decltype(auto) testRetRef4(Derived&& d) { return (d); } |
| |
| // As long as we're checking parentheses, make sure parentheses don't disable the warning. |
| Base testParens1() { |
| Derived d; |
| return (d); // e17 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" |
| } |
| ConstructFromDerived testParens2() { |
| Derived d; |
| return (d); // e18-cxx11 |
| // expected-warning@-1{{would have been copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" |
| } |
| |
| |
| // If the target is a catch-handler parameter, do apply the diagnostic. |
| void throw_derived(); |
| Derived testEParam1() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e19 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| Base testEParam2() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e20 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| ConstructFromDerived testEParam3() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e21 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| ConstructFromBase testEParam4() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e22 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| ConvertFromDerived testEParam5() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e23 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| ConvertFromBase testEParam6() { |
| try { throw_derived(); } catch (Derived d) { return d; } // e24 |
| // expected-warning@-1{{will be copied despite being returned by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:57-[[@LINE-3]]:58}:"std::move(d)" |
| __builtin_unreachable(); |
| } |
| |
| // If the exception variable is an lvalue reference, we cannot be sure |
| // that we own it; it is extremely contrived, but possible, for this to |
| // be a reference to an exception object that was thrown via |
| // `std::rethrow_exception(xp)` in Thread A, and meanwhile somebody else |
| // has got a copy of `xp` in Thread B, so that moving out of this object |
| // in Thread A would be observable (and racy) with respect to Thread B. |
| // Therefore assume it's not safe to move from. |
| Derived ok_REParam1() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| Base ok_REParam2() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| ConstructFromDerived ok_REParam3() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| ConstructFromBase ok_REParam4() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| ConvertFromDerived ok_REParam5() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| ConvertFromBase ok_REParam6() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); } |
| |
| Derived ok_CEParam1() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| Base ok_CEParam2() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| ConstructFromDerived ok_CEParam3() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| ConstructFromBase ok_CEParam4() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| ConvertFromDerived ok_CEParam5() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| ConvertFromBase ok_CEParam6() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); } |
| |
| // If rvalue overload resolution would find a copy constructor anyway, |
| // or if the copy constructor actually selected is trivial, then don't warn. |
| struct TriviallyCopyable {}; |
| struct OnlyCopyable { |
| OnlyCopyable() = default; |
| OnlyCopyable(const OnlyCopyable&) {} |
| }; |
| |
| TriviallyCopyable ok_copy1() { TriviallyCopyable c; return c; } |
| OnlyCopyable ok_copy2() { OnlyCopyable c; return c; } |
| TriviallyCopyable ok_copyparam1(TriviallyCopyable c) { return c; } |
| OnlyCopyable ok_copyparam2(OnlyCopyable c) { return c; } |
| |
| void test_throw1(Derived&& d) { |
| throw d; // e25 |
| // expected-warning@-1{{will be copied despite being thrown by name}} |
| // expected-note@-2{{to avoid copying}} |
| // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:12}:"std::move(d)" |
| } |
| |
| void ok_throw1() { Derived d; throw d; } |
| void ok_throw2(Derived d) { throw d; } |
| void ok_throw3(Derived& d) { throw d; } |
| void ok_throw4(Derived d) { throw std::move(d); } |
| void ok_throw5(Derived& d) { throw std::move(d); } |
| void ok_throw6(Derived& d) { throw static_cast<Derived&&>(d); } |
| void ok_throw7(TriviallyCopyable d) { throw d; } |
| void ok_throw8(OnlyCopyable d) { throw d; } |