338 changes: 338 additions & 0 deletions clang-tools-extra/test/clang-tidy/modernize-use-emplace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
// RUN: %check_clang_tidy %s modernize-use-emplace %t

namespace std {
template <typename T>
class vector {
public:
void push_back(const T &) {}
void push_back(T &&) {}

template <typename... Args>
void emplace_back(Args &&... args){};
};
template <typename T>
class list {
public:
void push_back(const T &) {}
void push_back(T &&) {}

template <typename... Args>
void emplace_back(Args &&... args){};
};

template <typename T>
class deque {
public:
void push_back(const T &) {}
void push_back(T &&) {}

template <typename... Args>
void emplace_back(Args &&... args){};
};

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 <class U1, class U2>
pair(const pair<U1, U2> &p){};
template <class U1, class U2>
pair(pair<U1, U2> &&p){};
};

template <typename T1, typename T2>
pair<T1, T2> make_pair(T1, T2) {
return pair<T1, T2>();
};

template <typename T>
class unique_ptr {
public:
unique_ptr(T *) {}
};
} // namespace std

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

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

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

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

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

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

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(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(ptr);
// Same here
}

void testMakePair() {
std::vector<std::pair<int, int>> v;
// FIXME: add functionality to change calls of std::make_pair
v.push_back(std::make_pair(1, 2));

// FIXME: This is not a bug, but call of make_pair should be removed in the
// future. This one matches because the return type of make_pair is different
// than the pair itself.
v.push_back(std::make_pair(42LL, 13));
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
// CHECK-FIXES: v.emplace_back(std::make_pair(42LL, 13));
}

void testOtherCointainers() {
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);
}

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