xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp (revision 0aaac4fe194ae2249e1a01f9d6f5eac0d75c5bb2)
1 // RUN: %check_clang_tidy %s performance-move-const-arg %t
2 
3 namespace std {
4 template <typename>
5 struct remove_reference;
6 
7 template <typename _Tp>
8 struct remove_reference {
9   typedef _Tp type;
10 };
11 
12 template <typename _Tp>
13 struct remove_reference<_Tp &> {
14   typedef _Tp type;
15 };
16 
17 template <typename _Tp>
18 struct remove_reference<_Tp &&> {
19   typedef _Tp type;
20 };
21 
22 template <typename _Tp>
23 constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) {
24   return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
25 }
26 
27 template <typename _Tp>
28 constexpr _Tp &&
29 forward(typename remove_reference<_Tp>::type &__t) noexcept {
30   return static_cast<_Tp &&>(__t);
31 }
32 
33 } // namespace std
34 
35 class A {
36 public:
37   A() {}
38   A(const A &rhs) {}
39   A(A &&rhs) {}
40 };
41 
42 using AlsoA = A;
43 
44 struct TriviallyCopyable {
45   int i;
46 };
47 
48 using TrivialAlias = TriviallyCopyable;
49 
50 void f(TriviallyCopyable) {}
51 
52 void g() {
53   TriviallyCopyable obj;
54   f(std::move(obj));
55   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: std::move of the variable 'obj' of the trivially-copyable type 'TriviallyCopyable' has no effect; remove std::move() [performance-move-const-arg]
56   // CHECK-FIXES: f(obj);
57 
58   TrivialAlias obj2;
59   f(std::move(obj2));
60   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: std::move of the variable 'obj2' of the trivially-copyable type 'TrivialAlias' (aka 'TriviallyCopyable') has no effect; remove std::move() [performance-move-const-arg]
61   // CHECK-FIXES: f(obj2);
62 }
63 
64 int f1() {
65   return std::move(42);
66   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the expression of the trivially-copyable type 'int' has no effect; remove std::move() [performance-move-const-arg]
67   // CHECK-FIXES: return 42;
68 }
69 
70 int f2(int x2) {
71   return std::move(x2);
72   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x2' of the trivially-copyable type 'int'
73   // CHECK-FIXES: return x2;
74 }
75 
76 int *f3(int *x3) {
77   return std::move(x3);
78   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x3' of the trivially-copyable type 'int *'
79   // CHECK-FIXES: return x3;
80 }
81 
82 A f4(A x4) { return std::move(x4); }
83 
84 AlsoA f4_a(AlsoA x4) { return std::move(x4); }
85 
86 A f5(const A x5) {
87   return std::move(x5);
88   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x5' has no effect; remove std::move() or make the variable non-const [performance-move-const-arg]
89   // CHECK-FIXES: return x5;
90 }
91 
92 AlsoA f5_a(const AlsoA x5) {
93   return std::move(x5);
94   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x5' has no effect; remove std::move() or make the variable non-const [performance-move-const-arg]
95   // CHECK-FIXES: return x5;
96 }
97 
98 template <typename T>
99 T f6(const T x6) {
100   return std::move(x6);
101 }
102 
103 void f7() { int a = f6(10); }
104 
105 #define M1(x) x
106 void f8() {
107   const A a;
108   M1(A b = std::move(a);)
109   // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: std::move of the const variable 'a' has no effect; remove std::move() or make the variable non-const
110   // CHECK-FIXES: M1(A b = a;)
111 }
112 
113 #define M2(x) std::move(x)
114 int f9() { return M2(1); }
115 
116 template <typename T>
117 T f_unknown_target(const int x10) {
118   return std::move(x10);
119 }
120 
121 void f11() {
122   f_unknown_target<int>(1);
123   f_unknown_target<double>(1);
124 }
125 
126 A&& f_return_right_ref() {
127   static A a{};
128   return std::move(a);
129 }
130 
131 class NoMoveSemantics {
132 public:
133   NoMoveSemantics();
134   NoMoveSemantics(const NoMoveSemantics &);
135 
136   NoMoveSemantics &operator=(const NoMoveSemantics &);
137 };
138 
139 using NoMoveSemanticsAlias = NoMoveSemantics;
140 
141 void callByConstRef(const NoMoveSemantics &);
142 void callByConstRef(int i, const NoMoveSemantics &);
143 
144 void moveToConstReferencePositives() {
145   NoMoveSemantics obj;
146 
147   // Basic case.
148   callByConstRef(std::move(obj));
149   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
150   // CHECK-FIXES: callByConstRef(obj);
151 
152   // Also works for second argument.
153   callByConstRef(1, std::move(obj));
154   // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: passing result of std::move() as
155   // CHECK-FIXES: callByConstRef(1, obj);
156 
157   // Works if std::move() applied to a temporary.
158   callByConstRef(std::move(NoMoveSemantics()));
159   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
160   // CHECK-FIXES: callByConstRef(NoMoveSemantics());
161 
162   // Works if calling a copy constructor.
163   NoMoveSemantics other(std::move(obj));
164   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: passing result of std::move() as
165   // CHECK-FIXES: NoMoveSemantics other(obj);
166 
167   // Works if calling assignment operator.
168   other = std::move(obj);
169   // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: passing result of std::move() as
170   // CHECK-FIXES: other = obj;
171 }
172 
173 void moveToConstReferencePositivesAlias() {
174   NoMoveSemanticsAlias obj;
175 
176   // Basic case.
177   callByConstRef(std::move(obj));
178   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
179   // CHECK-FIXES: callByConstRef(obj);
180 
181   // Also works for second argument.
182   callByConstRef(1, std::move(obj));
183   // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
184   // CHECK-FIXES: callByConstRef(1, obj);
185 
186   // Works if std::move() applied to a temporary.
187   callByConstRef(std::move(NoMoveSemanticsAlias()));
188   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
189   // CHECK-FIXES: callByConstRef(NoMoveSemanticsAlias());
190 
191   // Works if calling a copy constructor.
192   NoMoveSemanticsAlias other(std::move(obj));
193   // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
194   // CHECK-FIXES: NoMoveSemanticsAlias other(obj);
195 
196   // Works if calling assignment operator.
197   other = std::move(obj);
198   // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
199   // CHECK-FIXES: other = obj;
200 }
201 
202 class MoveSemantics {
203 public:
204   MoveSemantics();
205   MoveSemantics(MoveSemantics &&);
206 
207   MoveSemantics &operator=(MoveSemantics &&);
208 };
209 
210 using MoveSemanticsAlias = MoveSemantics;
211 
212 void callByValue(MoveSemantics);
213 
214 void callByRValueRef(MoveSemantics &&);
215 
216 template <class T>
217 void templateFunction(T obj) {
218   T other = std::move(obj);
219 }
220 
221 #define M3(T, obj)            \
222   do {                        \
223     T other = std::move(obj); \
224   } while (true)
225 
226 #define CALL(func) (func)()
227 
228 void moveToConstReferenceNegatives() {
229   // No warning when actual move takes place.
230   MoveSemantics move_semantics;
231   callByValue(std::move(move_semantics));
232   callByRValueRef(std::move(move_semantics));
233   MoveSemantics other(std::move(move_semantics));
234   other = std::move(move_semantics);
235 
236   // No warning if std::move() not used.
237   NoMoveSemantics no_move_semantics;
238   callByConstRef(no_move_semantics);
239 
240   // No warning if instantiating a template.
241   templateFunction(no_move_semantics);
242 
243   // No warning inside of macro expansions.
244   M3(NoMoveSemantics, no_move_semantics);
245 
246   // No warning inside of macro expansion, even if the macro expansion is inside
247   // a lambda that is, in turn, an argument to a macro.
248   CALL([no_move_semantics] { M3(NoMoveSemantics, no_move_semantics); });
249 
250   auto lambda = [] {};
251   auto lambda2 = std::move(lambda);
252 }
253 
254 void moveToConstReferenceNegativesAlias() {
255   // No warning when actual move takes place.
256   MoveSemanticsAlias move_semantics;
257   callByValue(std::move(move_semantics));
258   callByRValueRef(std::move(move_semantics));
259   MoveSemanticsAlias other(std::move(move_semantics));
260   other = std::move(move_semantics);
261 
262   // No warning if std::move() not used.
263   NoMoveSemanticsAlias no_move_semantics;
264   callByConstRef(no_move_semantics);
265 
266   // No warning if instantiating a template.
267   templateFunction(no_move_semantics);
268 
269   // No warning inside of macro expansions.
270   M3(NoMoveSemanticsAlias, no_move_semantics);
271 
272   // No warning inside of macro expansion, even if the macro expansion is inside
273   // a lambda that is, in turn, an argument to a macro.
274   CALL([no_move_semantics] { M3(NoMoveSemanticsAlias, no_move_semantics); });
275 
276   auto lambda = [] {};
277   auto lambda2 = std::move(lambda);
278 }
279 
280 class MoveOnly {
281 public:
282   MoveOnly(const MoveOnly &other) = delete;
283   MoveOnly &operator=(const MoveOnly &other) = delete;
284   MoveOnly(MoveOnly &&other) = default;
285   MoveOnly &operator=(MoveOnly &&other) = default;
286 };
287 template <class T>
288 void Q(T);
289 void moveOnlyNegatives(MoveOnly val) {
290   Q(std::move(val));
291 }
292 
293 using MoveOnlyAlias = MoveOnly;
294 
295 void fmovable(MoveSemantics);
296 
297 void lambda1() {
298   auto f = [](MoveSemantics m) {
299     fmovable(std::move(m));
300   };
301   f(MoveSemantics());
302 }
303 
304 template<class T> struct function {};
305 
306 template<typename Result, typename... Args>
307 class function<Result(Args...)> {
308 public:
309   function() = default;
310   void operator()(Args... args) const {
311     fmovable(std::forward<Args>(args)...);
312   }
313 };
314 
315 void functionInvocation() {
316   function<void(MoveSemantics)> callback;
317   MoveSemantics m;
318   callback(std::move(m));
319 }
320 
321 void functionInvocationAlias() {
322   function<void(MoveSemanticsAlias)> callback;
323   MoveSemanticsAlias m;
324   callback(std::move(m));
325 }
326 
327 void lambda2() {
328   function<void(MoveSemantics)> callback;
329 
330   auto f = [callback = std::move(callback)](MoveSemantics m) mutable {
331     // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: std::move of the variable 'callback' of the trivially-copyable type 'function<void (MoveSemantics)>' has no effect; remove std::move()
332     // CHECK-FIXES: auto f = [callback = callback](MoveSemantics m) mutable {
333     callback(std::move(m));
334   };
335   f(MoveSemantics());
336 }
337 
338 void lambda2Alias() {
339   function<void(MoveSemanticsAlias)> callback;
340 
341   auto f = [callback = std::move(callback)](MoveSemanticsAlias m) mutable {
342     // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: std::move of the variable 'callback' of the trivially-copyable type 'function<void (MoveSemanticsAlias)>' (aka 'function<void (MoveSemantics)>') has no effect; remove std::move() [performance-move-const-arg]
343     // CHECK-FIXES: auto f = [callback = callback](MoveSemanticsAlias m) mutable {
344     callback(std::move(m));
345   };
346   f(MoveSemanticsAlias());
347 }
348 
349 void showInt(int &&v);
350 void showInt(int v1, int &&v2);
351 void showPointer(const char *&&s);
352 void showPointer2(const char *const &&s);
353 void showTriviallyCopyable(TriviallyCopyable &&obj);
354 void showTriviallyCopyablePointer(const TriviallyCopyable *&&obj);
355 void testFunctions() {
356   int a = 10;
357   showInt(std::move(a));
358   // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
359   // CHECK-MESSAGES: :[[@LINE-10]]:20: note: consider changing the 1st parameter of 'showInt' from 'int &&' to 'const int &'
360   showInt(int());
361   showInt(a, std::move(a));
362   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
363   // CHECK-MESSAGES: :[[@LINE-13]]:28: note: consider changing the 2nd parameter of 'showInt' from 'int &&' to 'const int &'
364   const char* s = "";
365   showPointer(std::move(s));
366   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: std::move of the variable 's' of the trivially-copyable type 'const char *' has no effect [performance-move-const-arg]
367   // CHECK-MESSAGES: :[[@LINE-16]]:32: note: consider changing the 1st parameter of 'showPointer' from 'const char *&&' to 'const char *'
368   showPointer2(std::move(s));
369   // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: std::move of the variable 's' of the trivially-copyable type 'const char *' has no effect [performance-move-const-arg]
370   // CHECK-MESSAGES: :[[@LINE-18]]:39: note: consider changing the 1st parameter of 'showPointer2' from 'const char *const &&' to 'const char *const'
371   TriviallyCopyable *obj = new TriviallyCopyable();
372   showTriviallyCopyable(std::move(*obj));
373   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: std::move of the expression of the trivially-copyable type 'TriviallyCopyable' has no effect [performance-move-const-arg]
374   // CHECK-MESSAGES: :[[@LINE-21]]:48: note: consider changing the 1st parameter of 'showTriviallyCopyable' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
375   showTriviallyCopyablePointer(std::move(obj));
376   // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: std::move of the variable 'obj' of the trivially-copyable type 'TriviallyCopyable *' has no effect [performance-move-const-arg]
377   // CHECK-MESSAGES: :[[@LINE-23]]:62: note: consider changing the 1st parameter of 'showTriviallyCopyablePointer' from 'const TriviallyCopyable *&&' to 'const TriviallyCopyable *'
378   TrivialAlias* obj2 = new TrivialAlias();
379   showTriviallyCopyable(std::move(*obj));
380   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: std::move of the expression of the trivially-copyable type 'TriviallyCopyable' has no effect [performance-move-const-arg]
381   // CHECK-MESSAGES: :[[@LINE-28]]:48: note: consider changing the 1st parameter of 'showTriviallyCopyable' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
382   showTriviallyCopyablePointer(std::move(obj));
383   // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: std::move of the variable 'obj' of the trivially-copyable type 'TriviallyCopyable *' has no effect [performance-move-const-arg]
384   // CHECK-MESSAGES: :[[@LINE-30]]:62: note: consider changing the 1st parameter of 'showTriviallyCopyablePointer' from 'const TriviallyCopyable *&&' to 'const TriviallyCopyable *'
385 }
386 template <class T>
387 void forwardToShowInt(T && t) {
388   showInt(static_cast<T &&>(t));
389 }
390 void testTemplate() {
391   int a = 10;
392   forwardToShowInt(std::move(a));
393   // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
394 }
395 
396 struct Tmp {
397   Tmp();
398   Tmp(int &&a);
399   Tmp(int v1, int &&a);
400   Tmp(const char *&&s);
401   Tmp(TriviallyCopyable&& obj);
402   Tmp(const TriviallyCopyable *&&obj);
403   void showTmp(TriviallyCopyable&& t);
404   static void showTmpStatic(TriviallyCopyable&& t);
405 };
406 using TmpAlias = Tmp;
407 
408 void testMethods() {
409   Tmp t;
410   int a = 10;
411   Tmp t1(std::move(a));
412   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
413   // CHECK-MESSAGES: :[[@LINE-15]]:13: note: consider changing the 1st parameter of 'Tmp' from 'int &&' to 'const int &'
414   Tmp t2(a, std::move(a));
415   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
416   // CHECK-MESSAGES: :[[@LINE-17]]:21: note: consider changing the 2nd parameter of 'Tmp' from 'int &&' to 'const int &'
417   const char* s = "";
418   Tmp t3(std::move(s));
419   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 's' of the trivially-copyable type 'const char *' has no effect [performance-move-const-arg]
420   // CHECK-MESSAGES: :[[@LINE-20]]:21: note: consider changing the 1st parameter of 'Tmp' from 'const char *&&' to 'const char *'
421   TriviallyCopyable *obj = new TriviallyCopyable();
422   Tmp t4(std::move(*obj));
423   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the expression of the trivially-copyable type 'TriviallyCopyable' has no effect [performance-move-const-arg]
424   // CHECK-MESSAGES: :[[@LINE-23]]:27: note: consider changing the 1st parameter of 'Tmp' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
425   Tmp t5(std::move(obj));
426   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'obj' of the trivially-copyable type 'TriviallyCopyable *' has no effect [performance-move-const-arg]
427   // CHECK-MESSAGES: :[[@LINE-25]]:34: note: consider changing the 1st parameter of 'Tmp' from 'const TriviallyCopyable *&&' to 'const TriviallyCopyable *'
428   t.showTmp(std::move(*obj));
429   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: std::move of the expression of the trivially-copyable type 'TriviallyCopyable' has no effect [performance-move-const-arg]
430   // CHECK-MESSAGES: :[[@LINE-27]]:36: note: consider changing the 1st parameter of 'showTmp' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
431   Tmp::showTmpStatic(std::move(*obj));
432   // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: std::move of the expression of the trivially-copyable type 'TriviallyCopyable' has no effect [performance-move-const-arg]
433   // CHECK-MESSAGES: :[[@LINE-29]]:49: note: consider changing the 1st parameter of 'showTmpStatic' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
434 }
435 
436 void testMethodsAlias() {
437   TmpAlias t;
438   int a = 10;
439   TmpAlias t1(std::move(a));
440   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
441   // CHECK-MESSAGES: :[[@LINE-43]]:13: note: consider changing the 1st parameter of 'Tmp' from 'int &&' to 'const int &'
442   TmpAlias t2(a, std::move(a));
443   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
444   // CHECK-MESSAGES: :[[@LINE-45]]:21: note: consider changing the 2nd parameter of 'Tmp' from 'int &&' to 'const int &'
445   const char* s = "";
446   TmpAlias t3(std::move(s));
447   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: std::move of the variable 's' of the trivially-copyable type 'const char *' has no effect [performance-move-const-arg]
448   // CHECK-MESSAGES: :[[@LINE-48]]:21: note: consider changing the 1st parameter of 'Tmp' from 'const char *&&' to 'const char *'
449   TrivialAlias *obj = new TrivialAlias();
450   TmpAlias t4(std::move(*obj));
451   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: std::move of the expression of the trivially-copyable type 'TrivialAlias' (aka 'TriviallyCopyable') has no effect [performance-move-const-arg]
452   // CHECK-MESSAGES: :[[@LINE-51]]:27: note: consider changing the 1st parameter of 'Tmp' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
453   TmpAlias t5(std::move(obj));
454   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: std::move of the variable 'obj' of the trivially-copyable type 'TrivialAlias *' (aka 'TriviallyCopyable *') has no effect [performance-move-const-arg]
455   // CHECK-MESSAGES: :[[@LINE-53]]:34: note: consider changing the 1st parameter of 'Tmp' from 'const TriviallyCopyable *&&' to 'const TriviallyCopyable *'
456   t.showTmp(std::move(*obj));
457   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: std::move of the expression of the trivially-copyable type 'TrivialAlias' (aka 'TriviallyCopyable') has no effect [performance-move-const-arg]
458   // CHECK-MESSAGES: :[[@LINE-55]]:36: note: consider changing the 1st parameter of 'showTmp' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
459   TmpAlias::showTmpStatic(std::move(*obj));
460   // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: std::move of the expression of the trivially-copyable type 'TrivialAlias' (aka 'TriviallyCopyable') has no effect [performance-move-const-arg]
461   // CHECK-MESSAGES: :[[@LINE-57]]:49: note: consider changing the 1st parameter of 'showTmpStatic' from 'TriviallyCopyable &&' to 'const TriviallyCopyable &'
462 }
463 
464 void showA(A &&v) {}
465 void testA() {
466   A a;
467   showA(std::move(a));
468 }
469 
470 void testAAlias() {
471   AlsoA a;
472   showA(std::move(a));
473 }
474 
475 void testFuncPointer() {
476   int a = 10;
477   void (*choice)(int, int &&);
478   choice = showInt;
479   choice(std::move(a), std::move(a));
480   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect; remove std::move() [performance-move-const-arg]
481   // CHECK-FIXES: choice(a, std::move(a));
482   // CHECK-MESSAGES: :[[@LINE-3]]:24: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg]
483 }
484 
485 namespace issue_62550 {
486 
487 struct NonMoveConstructable {
488   NonMoveConstructable();
489   NonMoveConstructable(const NonMoveConstructable&);
490   NonMoveConstructable& operator=(const NonMoveConstructable&);
491   NonMoveConstructable& operator=(NonMoveConstructable&&);
492 };
493 
494 void testNonMoveConstructible() {
495   NonMoveConstructable t1;
496   NonMoveConstructable t2{std::move(t1)};
497   // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
498   // CHECK-MESSAGES: :[[@LINE-11]]:8: note: 'NonMoveConstructable' is not move constructible
499 }
500 
501 struct NonMoveAssignable {
502   NonMoveAssignable();
503   NonMoveAssignable(const NonMoveAssignable&);
504   NonMoveAssignable(NonMoveAssignable&&);
505 
506   NonMoveAssignable& operator=(const NonMoveAssignable&);
507 };
508 
509 void testNonMoveAssignable() {
510   NonMoveAssignable t1;
511   NonMoveAssignable t2;
512 
513   t2 = std::move(t1);
514   // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
515   // CHECK-MESSAGES: :[[@LINE-14]]:8: note: 'NonMoveAssignable' is not move assignable
516 }
517 
518 struct NonMoveable {
519   NonMoveable();
520   NonMoveable(const NonMoveable&);
521   NonMoveable& operator=(const NonMoveable&);
522 };
523 
524 void testNonMoveable() {
525   NonMoveable t1;
526   NonMoveable t2{std::move(t1)};
527   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
528   // CHECK-MESSAGES: :[[@LINE-10]]:8: note: 'NonMoveable' is not move assignable/constructible
529 
530   t1 = std::move(t2);
531   // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
532   // CHECK-MESSAGES: :[[@LINE-14]]:8: note: 'NonMoveable' is not move assignable/constructible
533 }
534 
535 using AlsoNonMoveable = NonMoveable;
536 
537 void testAlsoNonMoveable() {
538   AlsoNonMoveable t1;
539   AlsoNonMoveable t2{std::move(t1)};
540   // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
541   // CHECK-MESSAGES: :[[@LINE-23]]:8: note: 'NonMoveable' is not move assignable/constructible
542 
543   t1 = std::move(t2);
544   // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
545   // CHECK-MESSAGES: :[[@LINE-27]]:8: note: 'NonMoveable' is not move assignable/constructible
546 }
547 
548 } // namespace issue_62550
549 
550 namespace GH111450 {
551 struct Status;
552 
553 struct Error {
554     Error(const Status& S);
555 };
556 
557 struct Result {
558   Error E;
559   Result(Status&& S) : E(std::move(S)) {}
560   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
561 };
562 } // namespace GH111450
563