1 // RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- \
2 // RUN: -format-style=llvm \
3 // RUN: -config='{CheckOptions: \
4 // RUN:  {performance-inefficient-vector-operation.EnableProto: true}}'
5 
6 namespace std {
7 
8 typedef decltype(sizeof 0) size_t;
9 
10 template<class E> class initializer_list {
11 public:
12   using value_type = E;
13   using reference = E&;
14   using const_reference = const E&;
15   using size_type = size_t;
16   using iterator = const E*;
17   using const_iterator = const E*;
18   iterator p;
19   size_t sz;
20   initializer_list();
21   size_t size() const; // number of elements
22   const E* begin() const; // first element
23   const E* end() const; // one past the last element
24 };
25 
26 // initializer list range access
27 template<class E> const E* begin(initializer_list<E> il);
28 template<class E> const E* end(initializer_list<E> il);
29 
30 template <class T>
31 class vector {
32  public:
33   typedef T* iterator;
34   typedef const T* const_iterator;
35   typedef T& reference;
36   typedef const T& const_reference;
37   typedef size_t size_type;
38 
39   explicit vector();
40   explicit vector(size_type n);
41 
42   void push_back(const T& val);
43 
44   template <class... Args> void emplace_back(Args &&... args);
45 
46   void reserve(size_t n);
47   void resize(size_t n);
48 
49   size_t size() const;
50   const_reference operator[] (size_type) const;
51   reference operator[] (size_type);
52 
53   const_iterator begin() const;
54   const_iterator end() const;
55 };
56 } // namespace std
57 
58 class Foo {
59  public:
60   explicit Foo(int);
61 };
62 
63 class Bar {
64  public:
65   Bar(int);
66 };
67 
68 int Op(int);
69 
70 namespace proto2 {
71 class MessageLite {};
72 class Message : public MessageLite {};
73 } // namespace proto2
74 
75 class FooProto : public proto2::Message {
76  public:
77   int *add_x();  // repeated int x;
78   void add_x(int x);
79   void mutable_x();
80   void mutable_y();
81   int add_z() const; // optional int add_z;
82 };
83 
84 class BarProto : public proto2::Message {
85  public:
86   int *add_x();
87   void add_x(int x);
88   void mutable_x();
89   void mutable_y();
90 };
91 
f(std::vector<int> & t)92 void f(std::vector<int>& t) {
93   {
94     std::vector<int> v0;
95     // CHECK-FIXES: v0.reserve(10);
96     for (int i = 0; i < 10; ++i)
97       v0.push_back(i);
98       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop
99   }
100   {
101     std::vector<int> v1;
102     // CHECK-FIXES: v1.reserve(10);
103     for (int i = 0; i < 10; i++)
104       v1.push_back(i);
105       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
106   }
107   {
108     std::vector<int> v2;
109     // CHECK-FIXES: v2.reserve(10);
110     for (int i = 0; i < 10; ++i)
111       v2.push_back(0);
112       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
113   }
114   {
115     std::vector<int> v3;
116     // CHECK-FIXES: v3.reserve(5);
117     for (int i = 0; i < 5; ++i) {
118       v3.push_back(i);
119       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
120     }
121     // CHECK-FIXES-NOT: v3.reserve(10);
122     for (int i = 0; i < 10; ++i) {
123       // No fix for this loop as we encounter the prior loops.
124       v3.push_back(i);
125     }
126   }
127   {
128     std::vector<int> v4;
129     std::vector<int> v5;
130     v5.reserve(3);
131     // CHECK-FIXES: v4.reserve(10);
132     for (int i = 0; i < 10; ++i)
133       v4.push_back(i);
134       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
135   }
136   {
137     std::vector<int> v6;
138     // CHECK-FIXES: v6.reserve(t.size());
139     for (std::size_t i = 0; i < t.size(); ++i) {
140       v6.push_back(t[i]);
141       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
142     }
143   }
144   {
145     std::vector<int> v7;
146     // CHECK-FIXES: v7.reserve(t.size() - 1);
147     for (std::size_t i = 0; i < t.size() - 1; ++i) {
148       v7.push_back(t[i]);
149     } // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
150   }
151   {
152     std::vector<int> v8;
153     // CHECK-FIXES: v8.reserve(t.size());
154     for (const auto &e : t) {
155       v8.push_back(e);
156       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
157     }
158   }
159   {
160     std::vector<int> v9;
161     // CHECK-FIXES: v9.reserve(t.size());
162     for (const auto &e : t) {
163       v9.push_back(Op(e));
164       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
165     }
166   }
167   {
168     std::vector<Foo> v10;
169     // CHECK-FIXES: v10.reserve(t.size());
170     for (const auto &e : t) {
171       v10.push_back(Foo(e));
172       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
173     }
174   }
175   {
176     std::vector<Bar> v11;
177     // CHECK-FIXES: v11.reserve(t.size());
178     for (const auto &e : t) {
179       v11.push_back(e);
180       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
181     }
182   }
183   {
184     std::vector<Foo> v12;
185     // CHECK-FIXES: v12.reserve(t.size());
186     for (const auto &e : t) {
187       v12.emplace_back(e);
188       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called
189     }
190   }
191 
192   {
193     FooProto foo;
194     // CHECK-FIXES: foo.mutable_x()->Reserve(5);
195     for (int i = 0; i < 5; i++) {
196       foo.add_x(i);
197       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'add_x' is called inside a loop; consider pre-allocating the container capacity before the loop
198     }
199   }
200 
201   // ---- Non-fixed Cases ----
202   {
203     std::vector<int> z0;
204     z0.reserve(20);
205     // CHECK-FIXES-NOT: z0.reserve(10);
206     // There is a "reserve" call already.
207     for (int i = 0; i < 10; ++i) {
208       z0.push_back(i);
209     }
210   }
211   {
212     std::vector<int> z1;
213     z1.reserve(5);
214     // CHECK-FIXES-NOT: z1.reserve(10);
215     // There is a "reserve" call already.
216     for (int i = 0; i < 10; ++i) {
217       z1.push_back(i);
218     }
219   }
220   {
221     std::vector<int> z2;
222     z2.resize(5);
223     // CHECK-FIXES-NOT: z2.reserve(10);
224     // There is a ref usage of v before the loop.
225     for (int i = 0; i < 10; ++i) {
226       z2.push_back(i);
227     }
228   }
229   {
230     std::vector<int> z3;
231     z3.push_back(0);
232     // CHECK-FIXES-NOT: z3.reserve(10);
233     // There is a ref usage of v before the loop.
234     for (int i = 0; i < 10; ++i) {
235       z3.push_back(i);
236     }
237   }
238   {
239     std::vector<int> z4;
240     f(z4);
241     // CHECK-FIXES-NOT: z4.reserve(10);
242     // There is a ref usage of z4 before the loop.
243     for (int i = 0; i < 10; ++i) {
244       z4.push_back(i);
245     }
246   }
247   {
248     std::vector<int> z5(20);
249     // CHECK-FIXES-NOT: z5.reserve(10);
250     // z5 is not constructed with default constructor.
251     for (int i = 0; i < 10; ++i) {
252       z5.push_back(i);
253     }
254   }
255   {
256     std::vector<int> z6;
257     // CHECK-FIXES-NOT: z6.reserve(10);
258     // For-loop is not started with 0.
259     for (int i = 1; i < 10; ++i) {
260       z6.push_back(i);
261     }
262   }
263   {
264     std::vector<int> z7;
265     // CHECK-FIXES-NOT: z7.reserve(t.size());
266     // z7 isn't referenced in for-loop body.
267     for (std::size_t i = 0; i < t.size(); ++i) {
268       t.push_back(i);
269     }
270   }
271   {
272     std::vector<int> z8;
273     int k;
274     // CHECK-FIXES-NOT: z8.reserve(10);
275     // For-loop isn't a fixable loop.
276     for (std::size_t i = 0; k < 10; ++i) {
277       z8.push_back(t[i]);
278     }
279   }
280   {
281     std::vector<int> z9;
282     // CHECK-FIXES-NOT: z9.reserve(i + 1);
283     // The loop end expression refers to the loop variable i.
284     for (int i = 0; i < i + 1; i++)
285       z9.push_back(i);
286   }
287   {
288     std::vector<int> z10;
289     int k;
290     // CHECK-FIXES-NOT: z10.reserve(10);
291     // For-loop isn't a fixable loop.
292     for (std::size_t i = 0; i < 10; ++k) {
293       z10.push_back(t[i]);
294     }
295   }
296   {
297     std::vector<int> z11;
298     // initializer_list should not trigger the check.
299     for (int e : {1, 2, 3, 4, 5}) {
300       z11.push_back(e);
301     }
302   }
303   {
304     std::vector<int> z12;
305     std::vector<int>* z13 = &t;
306     // We only support detecting the range init expression which references
307     // container directly.
308     // Complex range init expressions like `*z13` is not supported.
309     for (const auto &e : *z13) {
310       z12.push_back(e);
311     }
312   }
313 
314   {
315     FooProto foo;
316     foo.mutable_x();
317     // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5);
318     for (int i = 0; i < 5; i++) {
319       foo.add_x(i);
320     }
321   }
322   {
323     FooProto foo;
324     // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5);
325     for (int i = 0; i < 5; i++) {
326       foo.add_x(i);
327       foo.add_x(i);
328     }
329   }
330   {
331     FooProto foo;
332     // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5);
333     foo.add_x(-1);
334     for (int i = 0; i < 5; i++) {
335       foo.add_x(i);
336     }
337   }
338   {
339     FooProto foo;
340     BarProto bar;
341     bar.mutable_x();
342     // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5);
343     for (int i = 0; i < 5; i++) {
344       foo.add_x();
345       bar.add_x();
346     }
347   }
348   {
349     FooProto foo;
350     foo.mutable_y();
351     // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5);
352     for (int i = 0; i < 5; i++) {
353       foo.add_x(i);
354     }
355   }
356   {
357     FooProto foo;
358     // CHECK-FIXES-NOT: foo.mutable_z()->Reserve(5);
359     for (int i = 0; i < 5; i++) {
360       foo.add_z();
361     }
362   }
363 }
364 
365 struct StructWithFieldContainer {
366   std::vector<int> Numbers;
getNumbersStructWithFieldContainer367   std::vector<int> getNumbers() const {
368     std::vector<int> Result;
369     // CHECK-FIXES: Result.reserve(Numbers.size());
370     for (auto Number : Numbers) {
371       Result.push_back(Number);
372       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
373     }
374     return Result;
375   }
376 };
377 
378 StructWithFieldContainer getStructWithField();
379 
foo(const StructWithFieldContainer & Src)380 void foo(const StructWithFieldContainer &Src) {
381   std::vector<int> A;
382   // CHECK-FIXES: A.reserve(Src.Numbers.size());
383   for (auto Number : Src.Numbers) {
384     A.push_back(Number);
385     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'push_back' is called
386   }
387   std::vector<int> B;
388   for (auto Number : getStructWithField().Numbers) {
389     B.push_back(Number);
390   }
391 }
392 
393 namespace gh95596 {
394 
f(std::vector<int> & t)395 void f(std::vector<int>& t) {
396   {
397     std::vector<int> gh95596_0;
398     // CHECK-FIXES: gh95596_0.reserve(10);
399     for (unsigned i = 0; i < 10; ++i)
400       gh95596_0.push_back(i);
401       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop
402   }
403   {
404     std::vector<int> gh95596_1;
405     // CHECK-FIXES: gh95596_1.reserve(10);
406     for (int i = 0U; i < 10; ++i)
407       gh95596_1.push_back(i);
408       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop
409   }
410   {
411     std::vector<int> gh95596_2;
412     // CHECK-FIXES: gh95596_2.reserve(10);
413     for (unsigned i = 0U; i < 10; ++i)
414       gh95596_2.push_back(i);
415       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop
416   }
417   {
418     std::vector<int> gh95596_3;
419     // CHECK-FIXES: gh95596_3.reserve(10U);
420     for (int i = 0; i < 10U; ++i)
421       gh95596_3.push_back(i);
422       // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop
423   }
424 }
425 
426 } // namespace gh95596
427