1 | // RUN: %check_clang_tidy %s modernize-pass-by-value %t -- -- -fno-delayed-template-parsing |
2 | |
3 | namespace { |
4 | // POD types are trivially move constructible. |
5 | struct POD { |
6 | int a, b, c; |
7 | }; |
8 | |
9 | struct Movable { |
10 | int a, b, c; |
11 | Movable() = default; |
12 | Movable(const Movable &) {} |
13 | Movable(Movable &&) {} |
14 | }; |
15 | |
16 | struct NotMovable { |
17 | NotMovable() = default; |
18 | NotMovable(const NotMovable &) = default; |
19 | NotMovable(NotMovable &&) = delete; |
20 | int a, b, c; |
21 | }; |
22 | } |
23 | |
24 | struct A { |
25 | A(const Movable &M) : M(M) {} |
26 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value] |
27 | // CHECK-FIXES: A(Movable M) : M(std::move(M)) {} |
28 | Movable M; |
29 | }; |
30 | |
31 | // Test that we aren't modifying other things than a parameter. |
32 | Movable GlobalObj; |
33 | struct B { |
34 | B(const Movable &M) : M(GlobalObj) {} |
35 | // CHECK-FIXES: B(const Movable &M) : M(GlobalObj) {} |
36 | Movable M; |
37 | }; |
38 | |
39 | // Test that a parameter with more than one reference to it won't be changed. |
40 | struct C { |
41 | // Tests extra-reference in body. |
42 | C(const Movable &M) : M(M) { this->i = M.a; } |
43 | // CHECK-FIXES: C(const Movable &M) : M(M) { this->i = M.a; } |
44 | |
45 | // Tests extra-reference in init-list. |
46 | C(const Movable &M, int) : M(M), i(M.a) {} |
47 | // CHECK-FIXES: C(const Movable &M, int) : M(M), i(M.a) {} |
48 | Movable M; |
49 | int i; |
50 | }; |
51 | |
52 | // Test that both declaration and definition are updated. |
53 | struct D { |
54 | D(const Movable &M); |
55 | // CHECK-FIXES: D(Movable M); |
56 | Movable M; |
57 | }; |
58 | D::D(const Movable &M) : M(M) {} |
59 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
60 | // CHECK-FIXES: D::D(Movable M) : M(std::move(M)) {} |
61 | |
62 | // Test with default parameter. |
63 | struct E { |
64 | E(const Movable &M = Movable()) : M(M) {} |
65 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
66 | // CHECK-FIXES: E(Movable M = Movable()) : M(std::move(M)) {} |
67 | Movable M; |
68 | }; |
69 | |
70 | // Test with object that can't be moved. |
71 | struct F { |
72 | F(const NotMovable &NM) : NM(NM) {} |
73 | // CHECK-FIXES: F(const NotMovable &NM) : NM(NM) {} |
74 | NotMovable NM; |
75 | }; |
76 | |
77 | // Test unnamed parameter in declaration. |
78 | struct G { |
79 | G(const Movable &); |
80 | // CHECK-FIXES: G(Movable ); |
81 | Movable M; |
82 | }; |
83 | G::G(const Movable &M) : M(M) {} |
84 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
85 | // CHECK-FIXES: G::G(Movable M) : M(std::move(M)) {} |
86 | |
87 | // Test parameter with and without qualifier. |
88 | namespace ns_H { |
89 | typedef ::Movable HMovable; |
90 | } |
91 | struct H { |
92 | H(const ns_H::HMovable &M); |
93 | // CHECK-FIXES: H(ns_H::HMovable M); |
94 | ns_H::HMovable M; |
95 | }; |
96 | using namespace ns_H; |
97 | H::H(const HMovable &M) : M(M) {} |
98 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
99 | // CHECK-FIXES: H(HMovable M) : M(std::move(M)) {} |
100 | |
101 | // Try messing up with macros. |
102 | #define MOVABLE_PARAM(Name) const Movable & Name |
103 | // CHECK-FIXES: #define MOVABLE_PARAM(Name) const Movable & Name |
104 | struct I { |
105 | I(MOVABLE_PARAM(M)) : M(M) {} |
106 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
107 | // CHECK-FIXES: I(MOVABLE_PARAM(M)) : M(M) {} |
108 | Movable M; |
109 | }; |
110 | #undef MOVABLE_PARAM |
111 | |
112 | // Test that templates aren't modified. |
113 | template <typename T> struct J { |
114 | J(const T &M) : M(M) {} |
115 | // CHECK-FIXES: J(const T &M) : M(M) {} |
116 | T M; |
117 | }; |
118 | J<Movable> j1(Movable()); |
119 | J<NotMovable> j2(NotMovable()); |
120 | |
121 | template<class T> |
122 | struct MovableTemplateT |
123 | { |
124 | MovableTemplateT() {} |
125 | MovableTemplateT(const MovableTemplateT& o) { } |
126 | MovableTemplateT(MovableTemplateT&& o) { } |
127 | }; |
128 | |
129 | template <class T> |
130 | struct J2 { |
131 | J2(const MovableTemplateT<T>& A); |
132 | // CHECK-FIXES: J2(const MovableTemplateT<T>& A); |
133 | MovableTemplateT<T> M; |
134 | }; |
135 | |
136 | template <class T> |
137 | J2<T>::J2(const MovableTemplateT<T>& A) : M(A) {} |
138 | // CHECK-FIXES: J2<T>::J2(const MovableTemplateT<T>& A) : M(A) {} |
139 | J2<int> j3(MovableTemplateT<int>{}); |
140 | |
141 | struct K_Movable { |
142 | K_Movable() = default; |
143 | K_Movable(const K_Movable &) = default; |
144 | K_Movable(K_Movable &&o) { dummy = o.dummy; } |
145 | int dummy; |
146 | }; |
147 | |
148 | // Test with movable type with an user defined move constructor. |
149 | struct K { |
150 | K(const K_Movable &M) : M(M) {} |
151 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
152 | // CHECK-FIXES: K(K_Movable M) : M(std::move(M)) {} |
153 | K_Movable M; |
154 | }; |
155 | |
156 | template <typename T> struct L { |
157 | L(const Movable &M) : M(M) {} |
158 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
159 | // CHECK-FIXES: L(Movable M) : M(std::move(M)) {} |
160 | Movable M; |
161 | }; |
162 | L<int> l(Movable()); |
163 | |
164 | // Test with a non-instantiated template class. |
165 | template <typename T> struct N { |
166 | N(const Movable &M) : M(M) {} |
167 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
168 | // CHECK-FIXES: N(Movable M) : M(std::move(M)) {} |
169 | |
170 | Movable M; |
171 | T A; |
172 | }; |
173 | |
174 | // Test with value parameter. |
175 | struct O { |
176 | O(Movable M) : M(M) {} |
177 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
178 | // CHECK-FIXES: O(Movable M) : M(std::move(M)) {} |
179 | Movable M; |
180 | }; |
181 | |
182 | // Test with a const-value parameter. |
183 | struct P { |
184 | P(const Movable M) : M(M) {} |
185 | // CHECK-FIXES: P(const Movable M) : M(M) {} |
186 | Movable M; |
187 | }; |
188 | |
189 | // Test with multiples parameters where some need to be changed and some don't. |
190 | // need to. |
191 | struct Q { |
192 | Q(const Movable &A, const Movable &B, const Movable &C, double D) |
193 | : A(A), B(B), C(C), D(D) {} |
194 | // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: pass by value and use std::move |
195 | // CHECK-MESSAGES: :[[@LINE-3]]:41: warning: pass by value and use std::move |
196 | // CHECK-FIXES: Q(const Movable &A, Movable B, Movable C, double D) |
197 | // CHECK-FIXES: : A(A), B(std::move(B)), C(std::move(C)), D(D) {} |
198 | const Movable &A; |
199 | Movable B; |
200 | Movable C; |
201 | double D; |
202 | }; |
203 | |
204 | // Test that value-parameters with a nested name specifier are left as-is. |
205 | namespace ns_R { |
206 | typedef ::Movable RMovable; |
207 | } |
208 | struct R { |
209 | R(ns_R::RMovable M) : M(M) {} |
210 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
211 | // CHECK-FIXES: R(ns_R::RMovable M) : M(std::move(M)) {} |
212 | ns_R::RMovable M; |
213 | }; |
214 | |
215 | // Test with rvalue parameter. |
216 | struct S { |
217 | S(Movable &&M) : M(M) {} |
218 | // CHECK-FIXES: S(Movable &&M) : M(M) {} |
219 | Movable M; |
220 | }; |
221 | |
222 | template <typename T, int N> struct array { T A[N]; }; |
223 | |
224 | // Test that types that are trivially copyable will not use std::move. This will |
225 | // cause problems with performance-move-const-arg, as it will revert it. |
226 | struct T { |
227 | T(array<int, 10> a) : a_(a) {} |
228 | // CHECK-FIXES: T(array<int, 10> a) : a_(a) {} |
229 | array<int, 10> a_; |
230 | }; |
231 | |
232 | struct U { |
233 | U(const POD &M) : M(M) {} |
234 | // CHECK-FIXES: U(const POD &M) : M(M) {} |
235 | POD M; |
236 | }; |
237 | |
238 | // The rewrite can't look through `typedefs` and `using`. |
239 | // Test that we don't partially rewrite one decl without rewriting the other. |
240 | using MovableConstRef = const Movable &; |
241 | struct V { |
242 | V(MovableConstRef M); |
243 | // CHECK-FIXES: V(MovableConstRef M); |
244 | Movable M; |
245 | }; |
246 | V::V(const Movable &M) : M(M) {} |
247 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
248 | // CHECK-FIXES: V::V(const Movable &M) : M(M) {} |
249 | |
250 | // Test with paired lvalue/rvalue overloads. |
251 | struct W1 { |
252 | W1(const Movable &M) : M(M) {} |
253 | W1(Movable &&M); |
254 | Movable M; |
255 | }; |
256 | struct W2 { |
257 | W2(const Movable &M, int) : M(M) {} |
258 | W2(Movable &&M, int); |
259 | Movable M; |
260 | }; |
261 | struct W3 { |
262 | W3(const W1 &, const Movable &M) : M(M) {} |
263 | W3(W1 &&, Movable &&M); |
264 | Movable M; |
265 | }; |
266 | |