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