llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize-avoid-bind.cpp

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); });
}