blob: a04f66e7f7349c97c84904cf5d4dc5b8c1cd9919 [file] [log] [blame]
// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm -- --std=c++11
namespace std {
typedef int size_t;
template<class E> class initializer_list {
public:
using value_type = E;
using reference = E&;
using const_reference = const E&;
using size_type = size_t;
using iterator = const E*;
using const_iterator = const E*;
initializer_list();
size_t size() const; // number of elements
const E* begin() const; // first element
const E* end() const; // one past the last element
};
// initializer list range access
template<class E> const E* begin(initializer_list<E> il);
template<class E> const E* end(initializer_list<E> il);
template <class T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
explicit vector();
explicit vector(size_type n);
void push_back(const T& val);
template <class... Args> void emplace_back(Args &&... args);
void reserve(size_t n);
void resize(size_t n);
size_t size();
const_reference operator[] (size_type) const;
reference operator[] (size_type);
const_iterator begin() const;
const_iterator end() const;
};
} // namespace std
class Foo {
public:
explicit Foo(int);
};
class Bar {
public:
Bar(int);
};
int Op(int);
void f(std::vector<int>& t) {
{
std::vector<int> v0;
// CHECK-FIXES: v0.reserve(10);
for (int i = 0; i < 10; ++i)
v0.push_back(i);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the vector capacity before the loop
}
{
std::vector<int> v1;
// CHECK-FIXES: v1.reserve(10);
for (int i = 0; i < 10; i++)
v1.push_back(i);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
{
std::vector<int> v2;
// CHECK-FIXES: v2.reserve(10);
for (int i = 0; i < 10; ++i)
v2.push_back(0);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
{
std::vector<int> v3;
// CHECK-FIXES: v3.reserve(5);
for (int i = 0; i < 5; ++i) {
v3.push_back(i);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
// CHECK-FIXES-NOT: v3.reserve(10);
for (int i = 0; i < 10; ++i) {
// No fix for this loop as we encounter the prior loops.
v3.push_back(i);
}
}
{
std::vector<int> v4;
std::vector<int> v5;
v5.reserve(3);
// CHECK-FIXES: v4.reserve(10);
for (int i = 0; i < 10; ++i)
v4.push_back(i);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
{
std::vector<int> v6;
// CHECK-FIXES: v6.reserve(t.size());
for (std::size_t i = 0; i < t.size(); ++i) {
v6.push_back(t[i]);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
}
{
std::vector<int> v7;
// CHECK-FIXES: v7.reserve(t.size() - 1);
for (std::size_t i = 0; i < t.size() - 1; ++i) {
v7.push_back(t[i]);
} // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
{
std::vector<int> v8;
// CHECK-FIXES: v8.reserve(t.size());
for (const auto &e : t) {
v8.push_back(e);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
}
{
std::vector<int> v9;
// CHECK-FIXES: v9.reserve(t.size());
for (const auto &e : t) {
v9.push_back(Op(e));
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
}
{
std::vector<Foo> v10;
// CHECK-FIXES: v10.reserve(t.size());
for (const auto &e : t) {
v10.push_back(Foo(e));
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
}
{
std::vector<Bar> v11;
// CHECK-FIXES: v11.reserve(t.size());
for (const auto &e : t) {
v11.push_back(e);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
}
{
std::vector<Foo> v12;
// CHECK-FIXES: v12.reserve(t.size());
for (const auto &e : t) {
v12.emplace_back(e);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called
}
}
// ---- Non-fixed Cases ----
{
std::vector<int> z0;
z0.reserve(20);
// CHECK-FIXES-NOT: z0.reserve(10);
// There is a "reserve" call already.
for (int i = 0; i < 10; ++i) {
z0.push_back(i);
}
}
{
std::vector<int> z1;
z1.reserve(5);
// CHECK-FIXES-NOT: z1.reserve(10);
// There is a "reserve" call already.
for (int i = 0; i < 10; ++i) {
z1.push_back(i);
}
}
{
std::vector<int> z2;
z2.resize(5);
// CHECK-FIXES-NOT: z2.reserve(10);
// There is a ref usage of v before the loop.
for (int i = 0; i < 10; ++i) {
z2.push_back(i);
}
}
{
std::vector<int> z3;
z3.push_back(0);
// CHECK-FIXES-NOT: z3.reserve(10);
// There is a ref usage of v before the loop.
for (int i = 0; i < 10; ++i) {
z3.push_back(i);
}
}
{
std::vector<int> z4;
f(z4);
// CHECK-FIXES-NOT: z4.reserve(10);
// There is a ref usage of z4 before the loop.
for (int i = 0; i < 10; ++i) {
z4.push_back(i);
}
}
{
std::vector<int> z5(20);
// CHECK-FIXES-NOT: z5.reserve(10);
// z5 is not constructed with default constructor.
for (int i = 0; i < 10; ++i) {
z5.push_back(i);
}
}
{
std::vector<int> z6;
// CHECK-FIXES-NOT: z6.reserve(10);
// For-loop is not started with 0.
for (int i = 1; i < 10; ++i) {
z6.push_back(i);
}
}
{
std::vector<int> z7;
// CHECK-FIXES-NOT: z7.reserve(t.size());
// z7 isn't referenced in for-loop body.
for (std::size_t i = 0; i < t.size(); ++i) {
t.push_back(i);
}
}
{
std::vector<int> z8;
int k;
// CHECK-FIXES-NOT: z8.reserve(10);
// For-loop isn't a fixable loop.
for (std::size_t i = 0; k < 10; ++i) {
z8.push_back(t[i]);
}
}
{
std::vector<int> z9;
// CHECK-FIXES-NOT: z9.reserve(i + 1);
// The loop end expression refers to the loop variable i.
for (int i = 0; i < i + 1; i++)
z9.push_back(i);
}
{
std::vector<int> z10;
int k;
// CHECK-FIXES-NOT: z10.reserve(10);
// For-loop isn't a fixable loop.
for (std::size_t i = 0; i < 10; ++k) {
z10.push_back(t[i]);
}
}
{
std::vector<int> z11;
// initializer_list should not trigger the check.
for (int e : {1, 2, 3, 4, 5}) {
z11.push_back(e);
}
}
{
std::vector<int> z12;
std::vector<int>* z13 = &t;
// We only support detecting the range init expression which references
// container directly.
// Complex range init expressions like `*z13` is not supported.
for (const auto &e : *z13) {
z12.push_back(e);
}
}
}