1 | // RUN: %check_clang_tidy -std=c++14-or-later %s modernize-avoid-bind %t |
2 | |
3 | namespace std { |
4 | inline namespace impl { |
5 | template <class Fp, class... Arguments> |
6 | class bind_rt {}; |
7 | |
8 | template <class Fp, class... Arguments> |
9 | bind_rt<Fp, Arguments...> bind(Fp &&, Arguments &&...); |
10 | } // namespace impl |
11 | |
12 | template <typename T> |
13 | T ref(T &t); |
14 | } // namespace std |
15 | |
16 | namespace boost { |
17 | template <class Fp, class... Arguments> |
18 | class bind_rt {}; |
19 | |
20 | template <class Fp, class... Arguments> |
21 | bind_rt<Fp, Arguments...> bind(const Fp &, Arguments...); |
22 | |
23 | template <class T> |
24 | struct reference_wrapper { |
25 | explicit reference_wrapper(T &t) {} |
26 | }; |
27 | |
28 | template <class T> |
29 | reference_wrapper<T> const ref(T &t) { |
30 | return reference_wrapper<T>(t); |
31 | } |
32 | |
33 | } // namespace boost |
34 | |
35 | namespace C { |
36 | int add(int x, int y) { return x + y; } |
37 | } // namespace C |
38 | |
39 | struct Foo { |
40 | static int add(int x, int y) { return x + y; } |
41 | }; |
42 | |
43 | struct D { |
44 | D() = default; |
45 | void operator()(int x, int y) const {} |
46 | operator bool() const { return true; } |
47 | |
48 | void MemberFunction(int x) {} |
49 | int MemberFunctionWithReturn(int x) {} |
50 | |
51 | static D *create(); |
52 | }; |
53 | |
54 | struct F { |
55 | F(int x) {} |
56 | ~F() {} |
57 | |
58 | int get() { return 42; } |
59 | }; |
60 | |
61 | void UseF(F); |
62 | |
63 | struct G { |
64 | G() : _member(0) {} |
65 | G(int m) : _member(m) {} |
66 | |
67 | template <typename T> |
68 | void operator()(T) const {} |
69 | |
70 | int _member; |
71 | }; |
72 | |
73 | template <typename T> |
74 | struct H { |
75 | void operator()(T) const {}; |
76 | }; |
77 | |
78 | struct placeholder {}; |
79 | placeholder _1; |
80 | placeholder _2; |
81 | |
82 | namespace placeholders { |
83 | using ::_1; |
84 | using ::_2; |
85 | } // namespace placeholders |
86 | |
87 | int add(int x, int y) { return x + y; } |
88 | int addThree(int x, int y, int z) { return x + y + z; } |
89 | void sub(int &x, int y) { x += y; } |
90 | |
91 | // Let's fake a minimal std::function-like facility. |
92 | namespace std { |
93 | template <typename _Tp> |
94 | _Tp declval(); |
95 | |
96 | template <typename _Functor, typename... _ArgTypes> |
97 | struct __res { |
98 | template <typename... _Args> |
99 | static decltype(declval<_Functor>()(_Args()...)) _S_test(int); |
100 | |
101 | template <typename...> |
102 | static void _S_test(...); |
103 | |
104 | using type = decltype(_S_test<_ArgTypes...>(0)); |
105 | }; |
106 | |
107 | template <typename> |
108 | struct function; |
109 | |
110 | template <typename... _ArgTypes> |
111 | struct function<void(_ArgTypes...)> { |
112 | template <typename _Functor, |
113 | typename = typename __res<_Functor, _ArgTypes...>::type> |
114 | function(_Functor) {} |
115 | }; |
116 | } // namespace std |
117 | |
118 | struct Thing {}; |
119 | void UseThing(Thing *); |
120 | |
121 | struct Callback { |
122 | Callback(); |
123 | Callback(std::function<void()>); |
124 | void Reset(std::function<void()>); |
125 | }; |
126 | |
127 | int GlobalVariable = 42; |
128 | |
129 | struct TestCaptureByValueStruct { |
130 | int MemberVariable; |
131 | static int StaticMemberVariable; |
132 | F MemberStruct; |
133 | G MemberStructWithData; |
134 | |
135 | void testCaptureByValue(int Param, F f) { |
136 | int x = 3; |
137 | int y = 4; |
138 | auto AAA = std::bind(add, x, y); |
139 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
140 | // CHECK-FIXES: auto AAA = [x, y] { return add(x, y); }; |
141 | |
142 | // When the captured variable is repeated, it should only appear in the capture list once. |
143 | auto BBB = std::bind(add, x, x); |
144 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
145 | // CHECK-FIXES: auto BBB = [x] { return add(x, x); }; |
146 | |
147 | int LocalVariable; |
148 | // Global variables shouldn't be captured at all, and members should be captured through this. |
149 | auto CCC = std::bind(add, MemberVariable, GlobalVariable); |
150 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
151 | // CHECK-FIXES: auto CCC = [this] { return add(MemberVariable, GlobalVariable); }; |
152 | |
153 | // Static member variables shouldn't be captured, but locals should |
154 | auto DDD = std::bind(add, TestCaptureByValueStruct::StaticMemberVariable, LocalVariable); |
155 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
156 | // CHECK-FIXES: auto DDD = [LocalVariable] { return add(TestCaptureByValueStruct::StaticMemberVariable, LocalVariable); }; |
157 | |
158 | auto EEE = std::bind(add, Param, Param); |
159 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
160 | // CHECK-FIXES: auto EEE = [Param] { return add(Param, Param); }; |
161 | |
162 | // The signature of boost::bind() is different, and causes |
163 | // CXXBindTemporaryExprs to be created in certain cases. So let's test |
164 | // those here. |
165 | auto FFF = boost::bind(UseF, f); |
166 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to boost::bind [modernize-avoid-bind] |
167 | // CHECK-FIXES: auto FFF = [f] { UseF(f); }; |
168 | |
169 | auto GGG = boost::bind(UseF, MemberStruct); |
170 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to boost::bind [modernize-avoid-bind] |
171 | // CHECK-FIXES: auto GGG = [this] { UseF(MemberStruct); }; |
172 | |
173 | auto HHH = std::bind(add, MemberStructWithData._member, 1); |
174 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
175 | // Correctly distinguish data members of other classes |
176 | // CHECK-FIXES: auto HHH = [capture0 = MemberStructWithData._member] { return add(capture0, 1); }; |
177 | } |
178 | }; |
179 | |
180 | void testLiteralParameters() { |
181 | auto AAA = std::bind(add, 2, 2); |
182 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
183 | // CHECK-FIXES: auto AAA = [] { return add(2, 2); }; |
184 | |
185 | auto BBB = std::bind(addThree, 2, 3, 4); |
186 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind [modernize-avoid-bind] |
187 | // CHECK-FIXES: auto BBB = [] { return addThree(2, 3, 4); }; |
188 | } |
189 | |
190 | void testCaptureByReference() { |
191 | int x = 2; |
192 | int y = 2; |
193 | auto AAA = std::bind(add, std::ref(t&: x), std::ref(t&: y)); |
194 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
195 | // CHECK-FIXES: auto AAA = [&x, &y] { return add(x, y); }; |
196 | |
197 | auto BBB = std::bind(add, std::ref(t&: x), y); |
198 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
199 | // CHECK-FIXES: auto BBB = [&x, y] { return add(x, y); }; |
200 | |
201 | auto CCC = std::bind(add, y, std::ref(t&: x)); |
202 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
203 | // CHECK-FIXES: auto CCC = [y, &x] { return add(y, x); }; |
204 | |
205 | // Make sure it works with boost::ref() too which has slightly different |
206 | // semantics. |
207 | auto DDD = boost::bind(add, boost::ref(t&: x), boost::ref(t&: y)); |
208 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to boost::bind |
209 | // CHECK-FIXES: auto DDD = [&x, &y] { return add(x, y); }; |
210 | |
211 | auto EEE = boost::bind(add, boost::ref(t&: x), y); |
212 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to boost::bind |
213 | // CHECK-FIXES: auto EEE = [&x, y] { return add(x, y); }; |
214 | |
215 | auto FFF = boost::bind(add, y, boost::ref(t&: x)); |
216 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to boost::bind |
217 | // CHECK-FIXES: auto FFF = [y, &x] { return add(y, x); }; |
218 | } |
219 | |
220 | void testCaptureByInitExpression() { |
221 | int x = 42; |
222 | auto AAA = std::bind(add, x, F(x).get()); |
223 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
224 | // CHECK-FIXES: auto AAA = [x, capture0 = F(x).get()] { return add(x, capture0); }; |
225 | } |
226 | |
227 | void testFunctionObjects() { |
228 | D d; |
229 | D *e = nullptr; |
230 | auto AAA = std::bind(d, 1, 2); |
231 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
232 | // CHECK-FIXES: auto AAA = [d] { d(1, 2); } |
233 | |
234 | auto BBB = std::bind(*e, 1, 2); |
235 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
236 | // CHECK-FIXES: auto BBB = [e] { (*e)(1, 2); } |
237 | |
238 | auto CCC = std::bind(D{}, 1, 2); |
239 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
240 | // CHECK-FIXES: auto CCC = [] { D{}(1, 2); } |
241 | |
242 | auto DDD = std::bind(D(), 1, 2); |
243 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
244 | // CHECK-FIXES: auto DDD = [] { D()(1, 2); } |
245 | |
246 | auto EEE = std::bind(*D::create(), 1, 2); |
247 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
248 | // CHECK-FIXES: auto EEE = [Func = *D::create()] { Func(1, 2); }; |
249 | |
250 | auto FFF = std::bind(G(), 1); |
251 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
252 | // Templated function call operators may be used |
253 | // CHECK-FIXES: auto FFF = [] { G()(1); }; |
254 | |
255 | int CTorArg = 42; |
256 | auto GGG = std::bind(G(CTorArg), 1); |
257 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
258 | // Function objects with constructor arguments should be captured |
259 | // CHECK-FIXES: auto GGG = [Func = G(CTorArg)] { Func(1); }; |
260 | } |
261 | |
262 | template <typename T> |
263 | void testMemberFnOfClassTemplate(T) { |
264 | auto HHH = std::bind(H<T>(), 42); |
265 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
266 | // Ensure function class template arguments are preserved |
267 | // CHECK-FIXES: auto HHH = [] { H<T>()(42); }; |
268 | } |
269 | |
270 | template void testMemberFnOfClassTemplate(int); |
271 | |
272 | void testPlaceholders() { |
273 | int x = 2; |
274 | auto AAA = std::bind(add, x, _1); |
275 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
276 | // CHECK-FIXES: auto AAA = [x](auto && PH1) { return add(x, std::forward<decltype(PH1)>(PH1)); }; |
277 | |
278 | auto BBB = std::bind(add, _2, _1); |
279 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
280 | // CHECK-FIXES: auto BBB = [](auto && PH1, auto && PH2) { return add(std::forward<decltype(PH2)>(PH2), std::forward<decltype(PH1)>(PH1)); }; |
281 | |
282 | // No fix is applied for reused placeholders. |
283 | auto CCC = std::bind(add, _1, _1); |
284 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
285 | // CHECK-FIXES: auto CCC = std::bind(add, _1, _1); |
286 | |
287 | // When a placeholder is skipped, we always add skipped ones to the lambda as |
288 | // unnamed parameters. |
289 | auto DDD = std::bind(add, _2, 1); |
290 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
291 | // CHECK-FIXES: auto DDD = [](auto &&, auto && PH2) { return add(std::forward<decltype(PH2)>(PH2), 1); }; |
292 | |
293 | // Namespace-qualified placeholders are valid too |
294 | auto EEE = std::bind(add, placeholders::_2, 1); |
295 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
296 | // CHECK-FIXES: auto EEE = [](auto &&, auto && PH2) { return add(std::forward<decltype(PH2)>(PH2), 1); }; |
297 | } |
298 | |
299 | void testGlobalFunctions() { |
300 | auto AAA = std::bind(C::add, 1, 1); |
301 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
302 | // CHECK-FIXES: auto AAA = [] { return C::add(1, 1); }; |
303 | |
304 | auto BBB = std::bind(Foo::add, 1, 1); |
305 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
306 | // CHECK-FIXES: auto BBB = [] { return Foo::add(1, 1); }; |
307 | |
308 | // The & should get removed inside of the lambda body. |
309 | auto CCC = std::bind(&C::add, 1, 1); |
310 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
311 | // CHECK-FIXES: auto CCC = [] { return C::add(1, 1); }; |
312 | |
313 | auto DDD = std::bind(&Foo::add, 1, 1); |
314 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
315 | // CHECK-FIXES: auto DDD = [] { return Foo::add(1, 1); }; |
316 | |
317 | auto EEE = std::bind(&add, 1, 1); |
318 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
319 | // CHECK-FIXES: auto EEE = [] { return add(1, 1); }; |
320 | } |
321 | |
322 | void testCapturedSubexpressions() { |
323 | int x = 3; |
324 | int y = 3; |
325 | int *p = &x; |
326 | |
327 | auto AAA = std::bind(add, 1, add(x: 2, y: 5)); |
328 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
329 | // Results of nested calls are captured by value. |
330 | // CHECK-FIXES: auto AAA = [capture0 = add(2, 5)] { return add(1, capture0); }; |
331 | |
332 | auto BBB = std::bind(add, x, add(x: y, y: 5)); |
333 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
334 | // Results of nested calls are captured by value. |
335 | // CHECK-FIXES: auto BBB = [x, capture0 = add(y, 5)] { return add(x, capture0); }; |
336 | |
337 | auto CCC = std::bind(sub, std::ref(t&: *p), _1); |
338 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
339 | // Expressions returning references are captured |
340 | // CHECK-FIXES: auto CCC = [&capture0 = *p](auto && PH1) { sub(capture0, std::forward<decltype(PH1)>(PH1)); }; |
341 | } |
342 | |
343 | struct E { |
344 | void MemberFunction(int x) {} |
345 | int MemberFunctionWithReturn(int x) {} |
346 | int operator()(int x, int y) const { return x + y; } |
347 | |
348 | void testMemberFunctions() { |
349 | D *d; |
350 | D dd; |
351 | auto AAA = std::bind(&D::MemberFunction, d, 1); |
352 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
353 | // CHECK-FIXES: auto AAA = [d] { d->MemberFunction(1); }; |
354 | |
355 | auto BBB = std::bind(&D::MemberFunction, &dd, 1); |
356 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
357 | // CHECK-FIXES: auto BBB = [ObjectPtr = &dd] { ObjectPtr->MemberFunction(1); }; |
358 | |
359 | auto CCC = std::bind(&E::MemberFunction, this, 1); |
360 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
361 | // CHECK-FIXES: auto CCC = [this] { MemberFunction(1); }; |
362 | |
363 | // Test what happens when the object pointer is itself a placeholder. |
364 | auto DDD = std::bind(&D::MemberFunction, _1, 1); |
365 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
366 | // CHECK-FIXES: auto DDD = [](auto && PH1) { PH1->MemberFunction(1); }; |
367 | |
368 | auto EEE = std::bind(&D::MemberFunctionWithReturn, d, 1); |
369 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
370 | // CHECK-FIXES: auto EEE = [d] { return d->MemberFunctionWithReturn(1); }; |
371 | |
372 | auto FFF = std::bind(&D::MemberFunctionWithReturn, &dd, 1); |
373 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
374 | // CHECK-FIXES: auto FFF = [ObjectPtr = &dd] { return ObjectPtr->MemberFunctionWithReturn(1); }; |
375 | |
376 | auto GGG = std::bind(&E::MemberFunctionWithReturn, this, 1); |
377 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
378 | // CHECK-FIXES: auto GGG = [this] { return MemberFunctionWithReturn(1); }; |
379 | |
380 | // Test what happens when the object pointer is itself a placeholder. |
381 | auto HHH = std::bind(&D::MemberFunctionWithReturn, _1, 1); |
382 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
383 | // CHECK-FIXES: auto HHH = [](auto && PH1) { return PH1->MemberFunctionWithReturn(1); }; |
384 | |
385 | auto III = std::bind(&D::operator(), d, 1, 2); |
386 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
387 | // CHECK-FIXES: auto III = [d] { (*d)(1, 2); } |
388 | |
389 | auto JJJ = std::bind(&D::operator(), &dd, 1, 2); |
390 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
391 | // CHECK-FIXES: auto JJJ = [ObjectPtr = &dd] { (*ObjectPtr)(1, 2); } |
392 | |
393 | auto KKK = std::bind(&D::operator(), _1, 1, 2); |
394 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
395 | // CHECK-FIXES: auto KKK = [](auto && PH1) { (*PH1)(1, 2); }; |
396 | |
397 | auto LLL = std::bind(&D::operator bool, d); |
398 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
399 | // CHECK-FIXES: auto LLL = [d] { return d->operator bool(); } |
400 | |
401 | auto MMM = std::bind(&E::operator(), this, 1, 2); |
402 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind |
403 | // CHECK-FIXES: auto MMM = [this] { return (*this)(1, 2); } |
404 | } |
405 | }; |
406 | |
407 | void testStdFunction(Thing *t) { |
408 | Callback cb; |
409 | if (t) |
410 | cb.Reset(std::bind(UseThing, t)); |
411 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind |
412 | // CHECK-FIXES: cb.Reset([t] { UseThing(t); }); |
413 | } |
414 | |