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