// 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: }" -- -std=c++11 namespace std { template class initializer_list { public: initializer_list() noexcept {} }; template class vector { public: vector() = default; vector(initializer_list) {} void push_back(const T &) {} void push_back(T &&) {} template void emplace_back(Args &&... args){}; ~vector(); }; template class list { public: void push_back(const T &) {} void push_back(T &&) {} template void emplace_back(Args &&... args){}; ~list(); }; template class deque { public: void push_back(const T &) {} void push_back(T &&) {} template void emplace_back(Args &&... args){}; ~deque(); }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template class pair { public: pair() = default; pair(const pair &) = default; pair(pair &&) = default; pair(const T1 &, const T2 &) {} pair(T1 &&, T2 &&) {} template pair(const pair &){}; template pair(pair &&){}; }; template pair::type, typename remove_reference::type> make_pair(T1 &&, T2 &&) { return {}; }; template class tuple { public: tuple() = default; tuple(const tuple &) = default; tuple(tuple &&) = default; tuple(const Ts &...) {} tuple(Ts &&...) {} template tuple(const tuple &){}; template tuple(tuple &&) {} template tuple(const pair &) { static_assert(sizeof...(Ts) == 2, "Wrong tuple size"); }; template tuple(pair &&) { static_assert(sizeof...(Ts) == 2, "Wrong tuple size"); }; }; template tuple::type...> make_tuple(Ts &&...) { return {}; } template class unique_ptr { public: explicit unique_ptr(T *) {} ~unique_ptr(); }; } // namespace std namespace llvm { template class LikeASmallVector { public: void push_back(const T &) {} void push_back(T &&) {} template void emplace_back(Args &&... args){}; }; } // llvm void testInts() { std::vector 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 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 void dependOnElem() { std::vector v; v.push_back(ElemType(42)); } template void dependOnContainer() { ContainerType v; v.push_back(Something(42)); } void callDependent() { dependOnElem(); dependOnContainer>(); } void test2() { std::vector 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 getPair(); }; void testPair() { std::vector> v; v.push_back(std::pair(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> v2; v2.push_back(std::pair(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> v; v.push_back(std::tuple(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{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> 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 v; v.push_back(Derived(nullptr, Something{})); } void testNewExpr() { std::vector v; v.push_back(Derived(new int, Something{})); } void testSpaces() { std::vector 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 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 v; v.push_back(new int(5)); std::vector> v2; v2.push_back(std::unique_ptr(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(ptr)); // Same here } void testMakePair() { std::vector> 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(0, 3)); // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back // CHECK-FIXES: v.emplace_back(std::make_pair(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(Something(), 2)); // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back // CHECK-FIXES: v.emplace_back(std::make_pair(Something(), 2)); struct X { X(std::pair) {} }; std::vector 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&&) {} }; std::vector 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> 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(3LL, 1, 0)); // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back // CHECK-FIXES: v.emplace_back(std::make_tuple(3LL, 1, 0)); // make_tuple is not removed when there are explicit template // arguments provided. } namespace test { template struct Single { Single() = default; Single(const Single &) = default; Single(Single &&) = default; Single(const T &) {} Single(T &&) {} template Single(const Single &) {} template Single(Single &&) {} template Single(const std::tuple &) {} template Single(std::tuple &&) {} }; template Single::type> MakeSingle(T &&) { return {}; } } // namespace test void testOtherTuples() { std::vector> v; v.push_back(test::Single(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(4)); // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back // CHECK-FIXES: v.emplace_back(test::MakeSingle(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 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 d; d.push_back(Something(42)); // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back // CHECK-FIXES: d.emplace_back(42); llvm::LikeASmallVector 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 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 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 v; v.push_back(A({1, 2})); std::vector vb; vb.push_back(B({10, 42})); } struct Bitfield { unsigned bitfield : 1; unsigned notBitfield; }; void testBitfields() { std::vector 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 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 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> v; v.push_back(std::vector({1})); // Test against the bug reported in PR32896. v.push_back({{2}}); using PairIntVector = std::pair>; std::vector x; x.push_back(PairIntVector(3, {4})); x.push_back({5, {6}}); }