forked from OSchip/llvm-project
335 lines
15 KiB
C++
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; }
|