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 | |