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 | |
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(val: 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(val: 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(val: 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(val: 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(val: i); |
125 | } |
126 | } |
127 | { |
128 | std::vector<int> v4; |
129 | std::vector<int> v5; |
130 | v5.reserve(n: 3); |
131 | // CHECK-FIXES: v4.reserve(10); |
132 | for (int i = 0; i < 10; ++i) |
133 | v4.push_back(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(args: 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(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(n: 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(val: i); |
209 | } |
210 | } |
211 | { |
212 | std::vector<int> z1; |
213 | z1.reserve(n: 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(val: i); |
218 | } |
219 | } |
220 | { |
221 | std::vector<int> z2; |
222 | z2.resize(n: 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(val: i); |
227 | } |
228 | } |
229 | { |
230 | std::vector<int> z3; |
231 | z3.push_back(val: 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(val: i); |
236 | } |
237 | } |
238 | { |
239 | std::vector<int> z4; |
240 | f(t&: 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(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(val: 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(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(x: i); |
327 | foo.add_x(x: i); |
328 | } |
329 | } |
330 | { |
331 | FooProto foo; |
332 | // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); |
333 | foo.add_x(x: -1); |
334 | for (int i = 0; i < 5; i++) { |
335 | foo.add_x(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(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; |
367 | 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(val: Number); |
372 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called |
373 | } |
374 | return Result; |
375 | } |
376 | }; |
377 | |
378 | StructWithFieldContainer getStructWithField(); |
379 | |
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(val: 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(val: Number); |
390 | } |
391 | } |
392 | |
393 | namespace gh95596 { |
394 | |
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(val: 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(val: 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(val: 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(val: 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 | |