1// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-avoid-bind %t
2
3namespace std {
4inline namespace impl {
5template <class Fp, class... Arguments>
6class bind_rt {};
7
8template <class Fp, class... Arguments>
9bind_rt<Fp, Arguments...> bind(Fp &&, Arguments &&...);
10} // namespace impl
11
12template <typename T>
13T ref(T &t);
14} // namespace std
15
16namespace boost {
17template <class Fp, class... Arguments>
18class bind_rt {};
19
20template <class Fp, class... Arguments>
21bind_rt<Fp, Arguments...> bind(const Fp &, Arguments...);
22
23template <class T>
24struct reference_wrapper {
25 explicit reference_wrapper(T &t) {}
26};
27
28template <class T>
29reference_wrapper<T> const ref(T &t) {
30 return reference_wrapper<T>(t);
31}
32
33} // namespace boost
34
35namespace C {
36int add(int x, int y) { return x + y; }
37} // namespace C
38
39struct Foo {
40 static int add(int x, int y) { return x + y; }
41};
42
43struct 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
54struct F {
55 F(int x) {}
56 ~F() {}
57
58 int get() { return 42; }
59};
60
61void UseF(F);
62
63struct 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
73template <typename T>
74struct H {
75 void operator()(T) const {};
76};
77
78struct placeholder {};
79placeholder _1;
80placeholder _2;
81
82namespace placeholders {
83using ::_1;
84using ::_2;
85} // namespace placeholders
86
87int add(int x, int y) { return x + y; }
88int addThree(int x, int y, int z) { return x + y + z; }
89void sub(int &x, int y) { x += y; }
90
91// Let's fake a minimal std::function-like facility.
92namespace std {
93template <typename _Tp>
94_Tp declval();
95
96template <typename _Functor, typename... _ArgTypes>
97struct __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
107template <typename>
108struct function;
109
110template <typename... _ArgTypes>
111struct function<void(_ArgTypes...)> {
112 template <typename _Functor,
113 typename = typename __res<_Functor, _ArgTypes...>::type>
114 function(_Functor) {}
115};
116} // namespace std
117
118struct Thing {};
119void UseThing(Thing *);
120
121struct Callback {
122 Callback();
123 Callback(std::function<void()>);
124 void Reset(std::function<void()>);
125};
126
127int GlobalVariable = 42;
128
129struct 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
180void 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
190void 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
220void 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
227void 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
262template <typename T>
263void 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
270template void testMemberFnOfClassTemplate(int);
271
272void 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
299void 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
322void 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
343struct 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
407void 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

source code of clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp