forked from OSchip/llvm-project
373 lines
13 KiB
C++
373 lines
13 KiB
C++
// 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); });
|
|
}
|