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