Clang Project

clang_source_code/test/SemaCXX/warn-return-std-move.cpp
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
5namespace std {
6inline namespace foo {
7template <class T> struct remove_reference { typedef T type; };
8template <class T> struct remove_reference<T&> { typedef T type; };
9template <class T> struct remove_reference<T&&> { typedef T type; };
10
11template <class T> typename remove_reference<T>::type &&move(T &&t);
12} // namespace foo
13} // namespace std
14
15struct Instrument {
16    Instrument() {}
17    Instrument(Instrument&&) { /* MOVE */ }
18    Instrument(const Instrument&) { /* COPY */ }
19};
20struct ConvertFromBase { Instrument i; };
21struct ConvertFromDerived { Instrument i; };
22struct Base {
23    Instrument i;
24    operator ConvertFromBase() const& { return ConvertFromBase{i}; }
25    operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
26};
27struct Derived : public Base {
28    operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
29    operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
30};
31struct ConstructFromBase {
32    Instrument i;
33    ConstructFromBase(const Base& b): i(b.i) {}
34    ConstructFromBase(Base&& b): i(std::move(b.i)) {}
35};
36struct ConstructFromDerived {
37    Instrument i;
38    ConstructFromDerived(const Derived& d): i(d.i) {}
39    ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
40};
41
42struct TrivialInstrument {
43    int i = 42;
44};
45struct ConvertFromTrivialBase { TrivialInstrument i; };
46struct ConvertFromTrivialDerived { TrivialInstrument i; };
47struct TrivialBase {
48    TrivialInstrument i;
49    operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
50    operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
51};
52struct TrivialDerived : public TrivialBase {
53    operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
54    operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
55};
56struct ConstructFromTrivialBase {
57    TrivialInstrument i;
58    ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
59    ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
60};
61struct ConstructFromTrivialDerived {
62    TrivialInstrument i;
63    ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
64    ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
65};
66
67Derived test1() {
68    Derived d1;
69    return d1;  // ok
70}
71Base 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}
78ConstructFromDerived 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}
85ConstructFromBase 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}
92ConvertFromDerived 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}
99ConvertFromBase 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.
108Derived ok1() { Derived d; return d; }
109Base ok2() { Derived d; return static_cast<Derived&&>(d); }
110ConstructFromDerived ok3() { Derived d; return static_cast<Derived&&>(d); }
111ConstructFromBase ok4() { Derived d; return static_cast<Derived&&>(d); }
112ConvertFromDerived ok5() { Derived d; return static_cast<Derived&&>(d); }
113ConvertFromBase 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.
116Derived ok_plvalue1(Derived& d) { return d; }
117Base ok_plvalue2(Derived& d) { return d; }
118ConstructFromDerived ok_plvalue3(const Derived& d) { return d; }
119ConstructFromBase ok_plvalue4(Derived& d) { return d; }
120ConvertFromDerived ok_plvalue5(Derived& d) { return d; }
121ConvertFromBase ok_plvalue6(Derived& d) { return d; }
122
123Derived ok_lvalue1(Derived *p) { Derived& d = *p; return d; }
124Base ok_lvalue2(Derived *p) { Derived& d = *p; return d; }
125ConstructFromDerived ok_lvalue3(Derived *p) { const Derived& d = *p; return d; }
126ConstructFromBase ok_lvalue4(Derived *p) { Derived& d = *p; return d; }
127ConvertFromDerived ok_lvalue5(Derived *p) { Derived& d = *p; return d; }
128ConvertFromBase 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.
131static Derived global_d;
132Derived ok_global1() { return global_d; }
133Base ok_global2() { return global_d; }
134ConstructFromDerived ok_global3() { return global_d; }
135ConstructFromBase ok_global4() { return global_d; }
136ConvertFromDerived ok_global5() { return global_d; }
137ConvertFromBase ok_global6() { return global_d; }
138
139// If the target's copy constructor is trivial, assume the programmer doesn't care.
140TrivialDerived ok_trivial1(TrivialDerived d) { return d; }
141TrivialBase ok_trivial2(TrivialDerived d) { return d; }
142ConstructFromTrivialDerived ok_trivial3(TrivialDerived d) { return d; }
143ConstructFromTrivialBase ok_trivial4(TrivialDerived d) { return d; }
144ConvertFromTrivialDerived ok_trivial5(TrivialDerived d) { return d; }
145ConvertFromTrivialBase ok_trivial6(TrivialDerived d) { return d; }
146
147// If the target is a parameter, do apply the diagnostic.
148Derived testParam1(Derived d) { return d; }
149Base 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}
155ConstructFromDerived 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}
161ConstructFromBase 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}
167ConvertFromDerived 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}
173ConvertFromBase 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.
181Derived 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}
187Base 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}
193ConstructFromDerived 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}
199ConstructFromBase 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}
205ConvertFromDerived 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}
211ConvertFromBase 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.
219Derived& testRetRef1(Derived&& d) { return d; }
220Base& testRetRef2(Derived&& d) { return d; }
221auto&& testRetRef3(Derived&& d) { return d; }
222decltype(auto) testRetRef4(Derived&& d) { return (d); }
223
224// As long as we're checking parentheses, make sure parentheses don't disable the warning.
225Base 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}
232ConstructFromDerived 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.
242void throw_derived();
243Derived 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}
250Base 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}
257ConstructFromDerived 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}
264ConstructFromBase 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}
271ConvertFromDerived 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}
278ConvertFromBase 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.
293Derived ok_REParam1() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
294Base ok_REParam2() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
295ConstructFromDerived ok_REParam3() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
296ConstructFromBase ok_REParam4() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
297ConvertFromDerived ok_REParam5() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
298ConvertFromBase ok_REParam6() { try { throw_derived(); } catch (Derived& d) { return d; } __builtin_unreachable(); }
299
300Derived ok_CEParam1() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); }
301Base ok_CEParam2() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); }
302ConstructFromDerived ok_CEParam3() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); }
303ConstructFromBase ok_CEParam4() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); }
304ConvertFromDerived ok_CEParam5() { try { throw_derived(); } catch (const Derived& d) { return d; } __builtin_unreachable(); }
305ConvertFromBase 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.
309struct TriviallyCopyable {};
310struct OnlyCopyable {
311    OnlyCopyable() = default;
312    OnlyCopyable(const OnlyCopyable&) {}
313};
314
315TriviallyCopyable ok_copy1() { TriviallyCopyable c; return c; }
316OnlyCopyable ok_copy2() { OnlyCopyable c; return c; }
317TriviallyCopyable ok_copyparam1(TriviallyCopyable c) { return c; }
318OnlyCopyable ok_copyparam2(OnlyCopyable c) { return c; }
319
320void 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
327void ok_throw1() { Derived d; throw d; }
328void ok_throw2(Derived d) { throw d; }
329void ok_throw3(Derived& d) { throw d; }
330void ok_throw4(Derived d) { throw std::move(d); }
331void ok_throw5(Derived& d) { throw std::move(d); }
332void ok_throw6(Derived& d) { throw static_cast<Derived&&>(d); }
333void ok_throw7(TriviallyCopyable d) { throw d; }
334void ok_throw8(OnlyCopyable d) { throw d; }
335