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