llvm-project/clang/test/SemaCXX/warn-return-std-move.cpp

335 lines
15 KiB
C++

// 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; }