414 lines · cpp
1// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-avoid-bind %t2 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 impl11 12template <typename T>13T ref(T &t);14} // namespace std15 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 boost34 35namespace C {36int add(int x, int y) { return x + y; }37} // namespace C38 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) { return 0; }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 placeholders86 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 std117 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 should154 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 causes163 // CXXBindTemporaryExprs to be created in certain cases. So let's test164 // 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::bind175 // Correctly distinguish data members of other classes176 // 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(x), std::ref(y));194 // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind195 // 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::bind199 // 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::bind203 // CHECK-FIXES: auto CCC = [y, &x] { return add(y, x); };204 205 // Make sure it works with boost::ref() too which has slightly different206 // 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::bind209 // 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::bind213 // 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::bind217 // 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::bind224 // 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::bind232 // 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::bind236 // 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::bind240 // 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::bind244 // 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::bind248 // 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::bind252 // Templated function call operators may be used253 // 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::bind258 // Function objects with constructor arguments should be captured259 // 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::bind266 // Ensure function class template arguments are preserved267 // 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::bind276 // 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::bind280 // 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::bind285 // CHECK-FIXES: auto CCC = std::bind(add, _1, _1);286 287 // When a placeholder is skipped, we always add skipped ones to the lambda as288 // unnamed parameters.289 auto DDD = std::bind(add, _2, 1);290 // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind291 // CHECK-FIXES: auto DDD = [](auto &&, auto && PH2) { return add(std::forward<decltype(PH2)>(PH2), 1); };292 293 // Namespace-qualified placeholders are valid too294 auto EEE = std::bind(add, placeholders::_2, 1);295 // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind296 // 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::bind302 // 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::bind306 // 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::bind311 // 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::bind315 // 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::bind319 // 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(2, 5));328 // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind329 // 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::bind334 // 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::bind339 // Expressions returning references are captured340 // 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) { return 0; }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::bind353 // 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::bind357 // 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::bind361 // 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::bind366 // 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::bind370 // 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::bind374 // 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::bind378 // 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::bind383 // 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::bind387 // 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::bind391 // 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::bind395 // 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::bind399 // 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::bind403 // 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::bind412 // CHECK-FIXES: cb.Reset([t] { UseThing(t); });413}414