llvm-project/clang-tools-extra/test/clang-tidy/modernize-use-emplace.cpp

608 lines
16 KiB
C++

// RUN: %check_clang_tidy %s modernize-use-emplace %t -- \
// RUN: -config="{CheckOptions: \
// RUN: [{key: modernize-use-emplace.ContainersWithPushBack, \
// RUN: value: '::std::vector; ::std::list; ::std::deque; llvm::LikeASmallVector'}, \
// RUN: {key: modernize-use-emplace.TupleTypes, \
// RUN: value: '::std::pair; std::tuple; ::test::Single'}, \
// RUN: {key: modernize-use-emplace.TupleMakeFunctions, \
// RUN: value: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}] \
// RUN: }"
namespace std {
template <typename>
class initializer_list
{
public:
initializer_list() noexcept {}
};
template <typename T>
class vector {
public:
vector() = default;
vector(initializer_list<T>) {}
void push_back(const T &) {}
void push_back(T &&) {}
template <typename... Args>
void emplace_back(Args &&... args){};
~vector();
};
template <typename T>
class list {
public:
void push_back(const T &) {}
void push_back(T &&) {}
template <typename... Args>
void emplace_back(Args &&... args){};
~list();
};
template <typename T>
class deque {
public:
void push_back(const T &) {}
void push_back(T &&) {}
template <typename... Args>
void emplace_back(Args &&... args){};
~deque();
};
template <typename T> struct remove_reference { using type = T; };
template <typename T> struct remove_reference<T &> { using type = T; };
template <typename T> struct remove_reference<T &&> { using type = T; };
template <typename T1, typename T2> class pair {
public:
pair() = default;
pair(const pair &) = default;
pair(pair &&) = default;
pair(const T1 &, const T2 &) {}
pair(T1 &&, T2 &&) {}
template <typename U1, typename U2> pair(const pair<U1, U2> &){};
template <typename U1, typename U2> pair(pair<U1, U2> &&){};
};
template <typename T1, typename T2>
pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
make_pair(T1 &&, T2 &&) {
return {};
};
template <typename... Ts> class tuple {
public:
tuple() = default;
tuple(const tuple &) = default;
tuple(tuple &&) = default;
tuple(const Ts &...) {}
tuple(Ts &&...) {}
template <typename... Us> tuple(const tuple<Us...> &){};
template <typename... Us> tuple(tuple<Us...> &&) {}
template <typename U1, typename U2> tuple(const pair<U1, U2> &) {
static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
};
template <typename U1, typename U2> tuple(pair<U1, U2> &&) {
static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
};
};
template <typename... Ts>
tuple<typename remove_reference<Ts>::type...> make_tuple(Ts &&...) {
return {};
}
template <typename T>
class unique_ptr {
public:
explicit unique_ptr(T *) {}
~unique_ptr();
};
} // namespace std
namespace llvm {
template <typename T>
class LikeASmallVector {
public:
void push_back(const T &) {}
void push_back(T &&) {}
template <typename... Args>
void emplace_back(Args &&... args){};
};
} // llvm
void testInts() {
std::vector<int> v;
v.push_back(42);
v.push_back(int(42));
v.push_back(int{42});
v.push_back(42.0);
int z;
v.push_back(z);
}
struct Something {
Something(int a, int b = 41) {}
Something() {}
void push_back(Something);
int getInt() { return 42; }
};
struct Convertable {
operator Something() { return Something{}; }
};
struct Zoz {
Zoz(Something, int = 42) {}
};
Zoz getZoz(Something s) { return Zoz(s); }
void test_Something() {
std::vector<Something> v;
v.push_back(Something(1, 2));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back instead of push_back [modernize-use-emplace]
// CHECK-FIXES: v.emplace_back(1, 2);
v.push_back(Something{1, 2});
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2);
v.push_back(Something());
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back();
v.push_back(Something{});
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back();
Something Different;
v.push_back(Something(Different.getInt(), 42));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(Different.getInt(), 42);
v.push_back(Different.getInt());
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(Different.getInt());
v.push_back(42);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(42);
Something temporary(42, 42);
temporary.push_back(temporary);
v.push_back(temporary);
v.push_back(Convertable());
v.push_back(Convertable{});
Convertable s;
v.push_back(s);
}
template <typename ElemType>
void dependOnElem() {
std::vector<ElemType> v;
v.push_back(ElemType(42));
}
template <typename ContainerType>
void dependOnContainer() {
ContainerType v;
v.push_back(Something(42));
}
void callDependent() {
dependOnElem<Something>();
dependOnContainer<std::vector<Something>>();
}
void test2() {
std::vector<Zoz> v;
v.push_back(Zoz(Something(21, 37)));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(Something(21, 37));
v.push_back(Zoz(Something(21, 37), 42));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(Something(21, 37), 42);
v.push_back(getZoz(Something(1, 2)));
}
struct GetPair {
std::pair<int, long> getPair();
};
void testPair() {
std::vector<std::pair<int, int>> v;
v.push_back(std::pair<int, int>(1, 2));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2);
GetPair g;
v.push_back(g.getPair());
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(g.getPair());
std::vector<std::pair<Something, Zoz>> v2;
v2.push_back(std::pair<Something, Zoz>(Something(42, 42), Zoz(Something(21, 37))));
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
// CHECK-FIXES: v2.emplace_back(Something(42, 42), Zoz(Something(21, 37)));
}
void testTuple() {
std::vector<std::tuple<bool, char, int>> v;
v.push_back(std::tuple<bool, char, int>(false, 'x', 1));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(false, 'x', 1);
v.push_back(std::tuple<bool, char, int>{false, 'y', 2});
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(false, 'y', 2);
v.push_back({true, 'z', 3});
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(true, 'z', 3);
std::vector<std::tuple<int, bool>> x;
x.push_back(std::make_pair(1, false));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: x.emplace_back(1, false);
x.push_back(std::make_pair(2LL, 1));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: x.emplace_back(2LL, 1);
}
struct Base {
Base(int, int *, int = 42);
};
struct Derived : Base {
Derived(int *, Something) : Base(42, nullptr) {}
};
void testDerived() {
std::vector<Base> v;
v.push_back(Derived(nullptr, Something{}));
}
void testNewExpr() {
std::vector<Derived> v;
v.push_back(Derived(new int, Something{}));
}
void testSpaces() {
std::vector<Something> v;
// clang-format off
v.push_back(Something(1, //arg1
2 // arg2
) // Something
);
// CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, //arg1
// CHECK-FIXES: 2 // arg2
// CHECK-FIXES: // Something
// CHECK-FIXES: );
v.push_back( Something (1, 2) );
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2 );
v.push_back( Something {1, 2} );
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2 );
v.push_back( Something {} );
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back( );
v.push_back(
Something(1, 2) );
// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2 );
std::vector<Base> v2;
v2.push_back(
Base(42, nullptr));
// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: use emplace_back
// CHECK-FIXES: v2.emplace_back(42, nullptr);
// clang-format on
}
void testPointers() {
std::vector<int *> v;
v.push_back(new int(5));
std::vector<std::unique_ptr<int>> v2;
v2.push_back(std::unique_ptr<int>(new int(42)));
// This call can't be replaced with emplace_back.
// If emplacement will fail (not enough memory to add to vector)
// we will have leak of int because unique_ptr won't be constructed
// (and destructed) as in push_back case.
auto *ptr = new int;
v2.push_back(std::unique_ptr<int>(ptr));
// Same here
}
void testMakePair() {
std::vector<std::pair<int, int>> v;
v.push_back(std::make_pair(1, 2));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, 2);
v.push_back(std::make_pair(42LL, 13));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(42LL, 13);
v.push_back(std::make_pair<char, char>(0, 3));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(std::make_pair<char, char>(0, 3));
//
// Even though the call above could be turned into v.emplace_back(0, 3),
// we don't eliminate the make_pair call here, because of the explicit
// template parameters provided. make_pair's arguments can be convertible
// to its explicitly provided template parameter, but not to the pair's
// element type. The examples below illustrate the problem.
struct D {
D(...) {}
operator char() const { return 0; }
};
v.push_back(std::make_pair<D, int>(Something(), 2));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(std::make_pair<D, int>(Something(), 2));
struct X {
X(std::pair<int, int>) {}
};
std::vector<X> x;
x.push_back(std::make_pair(1, 2));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: x.emplace_back(std::make_pair(1, 2));
// make_pair cannot be removed here, as X is not constructible with two ints.
struct Y {
Y(std::pair<int, int>&&) {}
};
std::vector<Y> y;
y.push_back(std::make_pair(2, 3));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: y.emplace_back(std::make_pair(2, 3));
// make_pair cannot be removed here, as Y is not constructible with two ints.
}
void testMakeTuple() {
std::vector<std::tuple<int, bool, char>> v;
v.push_back(std::make_tuple(1, true, 'v'));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1, true, 'v');
v.push_back(std::make_tuple(2ULL, 1, 0));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(2ULL, 1, 0);
v.push_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
// make_tuple is not removed when there are explicit template
// arguments provided.
}
namespace test {
template <typename T> struct Single {
Single() = default;
Single(const Single &) = default;
Single(Single &&) = default;
Single(const T &) {}
Single(T &&) {}
template <typename U> Single(const Single<U> &) {}
template <typename U> Single(Single<U> &&) {}
template <typename U> Single(const std::tuple<U> &) {}
template <typename U> Single(std::tuple<U> &&) {}
};
template <typename T>
Single<typename std::remove_reference<T>::type> MakeSingle(T &&) {
return {};
}
} // namespace test
void testOtherTuples() {
std::vector<test::Single<int>> v;
v.push_back(test::Single<int>(1));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(1);
v.push_back({2});
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(2);
v.push_back(test::MakeSingle(3));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(3);
v.push_back(test::MakeSingle<long long>(4));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(test::MakeSingle<long long>(4));
// We don't remove make functions with explicit template parameters.
v.push_back(test::MakeSingle(5LL));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(5LL);
v.push_back(std::make_tuple(6));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(6);
v.push_back(std::make_tuple(7LL));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(7LL);
}
void testOtherContainers() {
std::list<Something> l;
l.push_back(Something(42, 41));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: l.emplace_back(42, 41);
std::deque<Something> d;
d.push_back(Something(42));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: d.emplace_back(42);
llvm::LikeASmallVector<Something> ls;
ls.push_back(Something(42));
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
// CHECK-FIXES: ls.emplace_back(42);
}
class IntWrapper {
public:
IntWrapper(int x) : value(x) {}
IntWrapper operator+(const IntWrapper other) const {
return IntWrapper(value + other.value);
}
private:
int value;
};
void testMultipleOpsInPushBack() {
std::vector<IntWrapper> v;
v.push_back(IntWrapper(42) + IntWrapper(27));
}
// Macro tests.
#define PUSH_BACK_WHOLE(c, x) c.push_back(x)
#define PUSH_BACK_NAME push_back
#define PUSH_BACK_ARG(x) (x)
#define SOME_OBJ Something(10)
#define MILLION 3
#define SOME_WEIRD_PUSH(v) v.push_back(Something(
#define OPEN (
#define CLOSE )
void macroTest() {
std::vector<Something> v;
Something s;
PUSH_BACK_WHOLE(v, Something(5, 6));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use emplace_back
v.PUSH_BACK_NAME(Something(5));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
v.push_back PUSH_BACK_ARG(Something(5, 6));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
v.push_back(SOME_OBJ);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
v.push_back(Something(MILLION));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(MILLION);
// clang-format off
v.push_back( Something OPEN 3 CLOSE );
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// clang-format on
PUSH_BACK_WHOLE(s, Something(1));
}
struct A {
int value1, value2;
};
struct B {
B(A) {}
};
struct C {
int value1, value2, value3;
};
void testAggregation() {
// This should not be noticed or fixed; after the correction, the code won't
// compile.
std::vector<A> v;
v.push_back(A({1, 2}));
std::vector<B> vb;
vb.push_back(B({10, 42}));
}
struct Bitfield {
unsigned bitfield : 1;
unsigned notBitfield;
};
void testBitfields() {
std::vector<Something> v;
Bitfield b;
v.push_back(Something(42, b.bitfield));
v.push_back(Something(b.bitfield));
v.push_back(Something(42, b.notBitfield));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(42, b.notBitfield);
int var;
v.push_back(Something(42, var));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(42, var);
}
class PrivateCtor {
PrivateCtor(int z);
public:
void doStuff() {
std::vector<PrivateCtor> v;
// This should not change it because emplace back doesn't have permission.
// Check currently doesn't support friend declarations because pretty much
// nobody would want to be friend with std::vector :(.
v.push_back(PrivateCtor(42));
}
};
struct WithDtor {
WithDtor(int) {}
~WithDtor();
};
void testWithDtor() {
std::vector<WithDtor> v;
v.push_back(WithDtor(42));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(42);
}
void testInitializerList() {
std::vector<std::vector<int>> v;
v.push_back(std::vector<int>({1}));
// Test against the bug reported in PR32896.
v.push_back({{2}});
using PairIntVector = std::pair<int, std::vector<int>>;
std::vector<PairIntVector> x;
x.push_back(PairIntVector(3, {4}));
x.push_back({5, {6}});
}