xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp (revision d867f668672d634d52eaeae4cdcb7f9740890082)
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 {}
operator boolD46   operator bool() const { return true; }
47 
MemberFunctionD48   void MemberFunction(int x) {}
MemberFunctionWithReturnD49   int MemberFunctionWithReturn(int x) {}
50 
51   static D *create();
52 };
53 
54 struct F {
FF55   F(int x) {}
~FF56   ~F() {}
57 
getF58   int get() { return 42; }
59 };
60 
61 void UseF(F);
62 
63 struct G {
GG64   G() : _member(0) {}
GG65   G(int m) : _member(m) {}
66 
67   template <typename T>
operator ()G68   void operator()(T) const {}
69 
70   int _member;
71 };
72 
73 template <typename T>
74 struct H {
operator ()H75   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 
add(int x,int y)87 int add(int x, int y) { return x + y; }
addThree(int x,int y,int z)88 int addThree(int x, int y, int z) { return x + y + z; }
sub(int & x,int y)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>
functionstd::function114   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 
testCaptureByValueTestCaptureByValueStruct135   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 
testLiteralParameters()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 
testCaptureByReference()190 void testCaptureByReference() {
191   int x = 2;
192   int y = 2;
193   auto AAA = std::bind(add, std::ref(x), std::ref(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(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(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(x), boost::ref(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(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(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 
testCaptureByInitExpression()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 
testFunctionObjects()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>
testMemberFnOfClassTemplate(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 
testPlaceholders()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 
testGlobalFunctions()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 
testCapturedSubexpressions()322 void testCapturedSubexpressions() {
323   int x = 3;
324   int y = 3;
325   int *p = &x;
326 
327   auto AAA = std::bind(add, 1, add(2, 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(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(*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 {
MemberFunctionE344   void MemberFunction(int x) {}
MemberFunctionWithReturnE345   int MemberFunctionWithReturn(int x) {}
operator ()E346   int operator()(int x, int y) const { return x + y; }
347 
testMemberFunctionsE348   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 
testStdFunction(Thing * t)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