1 | // RUN: %check_clang_tidy %s bugprone-too-small-loop-variable %t -- \ |
2 | // RUN: -config="{CheckOptions: \ |
3 | // RUN: {bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit: 1024}}" \ |
4 | // RUN: -- --target=x86_64-linux |
5 | |
6 | long size() { return 294967296l; } |
7 | |
8 | //////////////////////////////////////////////////////////////////////////////// |
9 | /// Test cases correctly caught by bugprone-too-small-loop-variable. |
10 | |
11 | void voidBadForLoop() { |
12 | for (int i = 0; i < size(); ++i) { |
13 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
14 | } |
15 | } |
16 | |
17 | void voidBadForLoop2() { |
18 | for (int i = 0; i < size() + 10; ++i) { |
19 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
20 | } |
21 | } |
22 | |
23 | void voidBadForLoop3() { |
24 | for (int i = 0; i <= size() - 1; ++i) { |
25 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
26 | } |
27 | } |
28 | |
29 | void voidBadForLoop4() { |
30 | for (int i = 0; size() > i; ++i) { |
31 | // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
32 | } |
33 | } |
34 | |
35 | void voidBadForLoop5() { |
36 | for (int i = 0; size() - 1 >= i; ++i) { |
37 | // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
38 | } |
39 | } |
40 | |
41 | void voidBadForLoop6() { |
42 | int i = 0; |
43 | for (; i < size(); ++i) { |
44 | // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
45 | } |
46 | } |
47 | |
48 | void voidBadForLoop7() { |
49 | struct Int { |
50 | int value; |
51 | } i; |
52 | |
53 | for (i.value = 0; i.value < size(); ++i.value) { |
54 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
55 | } |
56 | } |
57 | |
58 | void voidForLoopUnsignedBound() { |
59 | unsigned size = 3147483647; |
60 | for (int i = 0; i < size; ++i) { |
61 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'unsigned int' [bugprone-too-small-loop-variable] |
62 | } |
63 | } |
64 | |
65 | // The iteration's upper bound has a template dependent value. |
66 | template <long size> |
67 | void doSomething() { |
68 | for (short i = 0; i < size; ++i) { |
69 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
70 | } |
71 | } |
72 | |
73 | // The iteration's upper bound has a template dependent type. |
74 | template <class T> |
75 | void doSomething() { |
76 | for (T i = 0; i < size(); ++i) { |
77 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
78 | } |
79 | } |
80 | |
81 | void voidForLoopInstantiation() { |
82 | // This line does not trigger the warning. |
83 | doSomething<long>(); |
84 | // This one triggers the warning. |
85 | doSomething<short>(); |
86 | } |
87 | |
88 | // A suspicious function used in a macro. |
89 | #define SUSPICIOUS_SIZE (size()) |
90 | void voidBadForLoopWithMacroBound() { |
91 | for (short i = 0; i < SUSPICIOUS_SIZE; ++i) { |
92 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] |
93 | } |
94 | } |
95 | |
96 | unsigned int getVal() { |
97 | return 300; |
98 | } |
99 | |
100 | // The iteration's upper bound has a function declaration. |
101 | void voidBadForLoop8() { |
102 | const unsigned int l = getVal(); |
103 | for (unsigned char i = 0; i < l; ++i) { |
104 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: loop variable has narrower type 'unsigned char' than iteration's upper bound 'const unsigned int' [bugprone-too-small-loop-variable] |
105 | } |
106 | } |
107 | |
108 | //////////////////////////////////////////////////////////////////////////////// |
109 | /// Correct loops: we should not warn here. |
110 | |
111 | // A simple use case when both expressions have the same type. |
112 | void voidGoodForLoop() { |
113 | for (long i = 0; i < size(); ++i) { // no warning |
114 | } |
115 | } |
116 | |
117 | // Other use case where both expressions have the same type, |
118 | // but short expressions are converted to int by the compare operator. |
119 | void voidGoodForLoop2() { |
120 | short loopCond = 10; |
121 | for (short i = 0; i < loopCond; ++i) { // no warning |
122 | } |
123 | } |
124 | |
125 | // Because of the integer literal, the iteration's upper bound is int, but we suppress the warning here. |
126 | void voidForLoopShortPlusLiteral() { |
127 | short size = 30000; |
128 | for (short i = 0; i <= (size - 1); ++i) { // no warning |
129 | } |
130 | } |
131 | |
132 | // Addition of two short variables results in an int value, but we suppress this to avoid false positives. |
133 | void voidForLoopShortPlusShort() { |
134 | short size = 256; |
135 | short increment = 14; |
136 | for (short i = 0; i < size + increment; ++i) { // no warning |
137 | } |
138 | } |
139 | |
140 | // In this test case we have different integer types, but here the loop variable has the bigger type. |
141 | // The iteration's bound is cast implicitly, not the loop variable. |
142 | void voidForLoopBoundImplicitCast() { |
143 | short start = 256; |
144 | short end = 14; |
145 | for (int i = start; i >= end; --i) { // no warning |
146 | } |
147 | } |
148 | |
149 | // Range based loop and other iterator based loops are ignored by this check. |
150 | void voidRangeBasedForLoop() { |
151 | int array[] = {1, 2, 3, 4, 5}; |
152 | for (const int &i : array) { // no warning |
153 | } |
154 | } |
155 | |
156 | //////////////////////////////////////////////////////////////////////////////// |
157 | /// Future possibilites to improve the check. |
158 | |
159 | // False positive: because of the int literal, iteration's upper bound has int type. |
160 | void voidForLoopFalsePositive() { |
161 | short size = 30000; |
162 | bool cond = false; |
163 | for (short i = 0; i < (cond ? 0 : size); ++i) { |
164 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] |
165 | } |
166 | } |
167 | |
168 | void voidForLoopFalsePositive2() { |
169 | short size = 30000; |
170 | bool cond = false; |
171 | for (short i = 0; i < (!cond ? size : 0); ++i) { |
172 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] |
173 | } |
174 | } |
175 | |
176 | // False positive: The loop bound expression contains nested binary operators. |
177 | void voidForLoopFalsePositive3() { |
178 | short number = 30000; |
179 | for (short i = 0; i < ((number & 0x7f) + 1); ++i) { |
180 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] |
181 | } |
182 | } |
183 | |
184 | // TODO: handle while loop. |
185 | void voidBadWhileLoop() { |
186 | short i = 0; |
187 | while (i < size()) { // missing warning |
188 | ++i; |
189 | } |
190 | } |
191 | |
192 | // TODO: handle do-while loop. |
193 | void voidBadDoWhileLoop() { |
194 | short i = 0; |
195 | do { |
196 | ++i; |
197 | } while (i < size()); // missing warning |
198 | } |
199 | |
200 | // TODO: handle complex loop conditions. |
201 | void voidComplexForCond() { |
202 | bool additionalCond = true; |
203 | for (int i = 0; i < size() && additionalCond; ++i) { // missing warning |
204 | } |
205 | } |
206 | |
207 | //////////////////////////////////////////////////////////////////////////////// |
208 | /// Suspicious test cases ingored by this check. |
209 | |
210 | // Test case with a reverse iteration. |
211 | // This is caught by -Wimplicit-int-conversion. |
212 | void voidReverseForLoop() { |
213 | for (short i = size() - 1; i >= 0; --i) { // no warning |
214 | } |
215 | } |
216 | |
217 | // Macro defined literals are used inside the loop condition. |
218 | #define SIZE 125 |
219 | #define SIZE2 (SIZE + 1) |
220 | void voidForLoopWithMacroBound() { |
221 | for (short i = 0; i < SIZE2; ++i) { // no warning |
222 | } |
223 | } |
224 | |
225 | // A suspicious loop is not caught if the iteration's upper bound is a literal. |
226 | void voidForLoopWithLiteralBound() { |
227 | for (short i = 0; i < 125; ++i) { // no warning |
228 | } |
229 | } |
230 | |
231 | // The used literal leads to an infinite loop. |
232 | // This is caught by -Wtautological-constant-out-of-range-compare. |
233 | void voidForLoopWithBigLiteralBound() { |
234 | for (short i = 0; i < 294967296l; ++i) { // no warning |
235 | } |
236 | } |
237 | |
238 | enum eSizeType { |
239 | START, |
240 | Y, |
241 | END |
242 | }; |
243 | |
244 | // A suspicious loop is not caught if the iteration's upper bound is an enum value. |
245 | void voidForLoopWithEnumBound() { |
246 | for (short i = eSizeType::START; i < eSizeType::END; ++i) { // no warning |
247 | } |
248 | } |
249 | |
250 | enum eSizeType2 : long { |
251 | START2 = 294967296l, |
252 | Y2, |
253 | END2 |
254 | }; |
255 | |
256 | // The used enum value leads to an infinite loop. |
257 | // This is caught by -Wtautological-constant-out-of-range-compare. |
258 | void voidForLoopWithBigEnumBound() { |
259 | for (short i = eSizeType2::START2; i < eSizeType2::END2; ++i) { // no warning |
260 | } |
261 | } |
262 | |
263 | // A suspicious loop is not caught if the iteration's upper bound is a constant variable. |
264 | void voidForLoopWithConstBound() { |
265 | const long size = 252l; |
266 | for (short i = 0; i < size; ++i) { // no warning |
267 | } |
268 | } |
269 | |
270 | // The used constant variable leads to an infinite loop. |
271 | // This is caught by -Wtautological-constant-out-of-range-compare. |
272 | void voidForLoopWithBigConstBound() { |
273 | const long size = 294967296l; |
274 | for (short i = 0; i < size; ++i) { // no warning |
275 | } |
276 | } |
277 | |
278 | // Should detect proper size of upper bound bitfield |
279 | void voidForLoopWithBitfieldOnUpperBound() { |
280 | struct StructWithBitField { |
281 | unsigned bitfield : 5; |
282 | } value = {}; |
283 | |
284 | for(unsigned char i = 0U; i < value.bitfield; ++i) { // no warning |
285 | } |
286 | } |
287 | |
288 | // Should detect proper size of loop variable bitfield |
289 | void voidForLoopWithBitfieldOnLoopVar() { |
290 | struct StructWithBitField { |
291 | unsigned bitfield : 9; |
292 | } value = {}; |
293 | |
294 | unsigned char upperLimit = 100U; |
295 | |
296 | for(value.bitfield = 0U; value.bitfield < upperLimit; ++value.bitfield) { |
297 | } |
298 | } |
299 | |
300 | // Should detect proper size of loop variable and upper bound |
301 | void voidForLoopWithBitfieldOnLoopVarAndUpperBound() { |
302 | struct StructWithBitField { |
303 | unsigned var : 5, limit : 4; |
304 | } value = {}; |
305 | |
306 | for(value.var = 0U; value.var < value.limit; ++value.var) { |
307 | } |
308 | } |
309 | |
310 | // Should detect proper size of loop variable and upper bound on integers |
311 | void voidForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt() { |
312 | struct StructWithBitField { |
313 | unsigned var : 5; |
314 | int limit : 6; |
315 | } value = {}; |
316 | |
317 | for(value.var = 0U; value.var < value.limit; ++value.var) { |
318 | } |
319 | } |
320 | |
321 | void badForLoopWithBitfieldOnUpperBound() { |
322 | struct StructWithBitField { |
323 | unsigned bitfield : 9; |
324 | } value = {}; |
325 | |
326 | for(unsigned char i = 0U; i < value.bitfield; ++i) { |
327 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: loop variable has narrower type 'unsigned char' than iteration's upper bound 'unsigned int:9' [bugprone-too-small-loop-variable] |
328 | } |
329 | } |
330 | |
331 | void badForLoopWithBitfieldOnLoopVar() { |
332 | struct StructWithBitField { |
333 | unsigned bitfield : 7; |
334 | } value = {}; |
335 | |
336 | unsigned char upperLimit = 100U; |
337 | |
338 | for(value.bitfield = 0U; value.bitfield < upperLimit; ++value.bitfield) { |
339 | // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: loop variable has narrower type 'unsigned int:7' than iteration's upper bound 'unsigned char' [bugprone-too-small-loop-variable] |
340 | } |
341 | } |
342 | |
343 | void badForLoopWithBitfieldOnLoopVarAndUpperBound() { |
344 | struct StructWithBitField { |
345 | unsigned var : 5, limit : 6; |
346 | } value = {}; |
347 | |
348 | for(value.var = 0U; value.var < value.limit; ++value.var) { |
349 | // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: loop variable has narrower type 'unsigned int:5' than iteration's upper bound 'unsigned int:6' [bugprone-too-small-loop-variable] |
350 | } |
351 | } |
352 | |
353 | void badForLoopWithBitfieldOnLoopVarOnIntAndUpperBound() { |
354 | struct StructWithBitField { |
355 | int var : 5; |
356 | unsigned limit : 5; |
357 | } value = {}; |
358 | |
359 | for(value.var = 0U; value.var < value.limit; ++value.var) { |
360 | // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: loop variable has narrower type 'int:5' than iteration's upper bound 'unsigned int:5' [bugprone-too-small-loop-variable] |
361 | } |
362 | } |
363 | |
364 | void badForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt() { |
365 | struct StructWithBitField { |
366 | unsigned var : 5; |
367 | int limit : 7; |
368 | } value = {}; |
369 | |
370 | for(value.var = 0U; value.var < value.limit; ++value.var) { |
371 | // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: loop variable has narrower type 'unsigned int:5' than iteration's upper bound 'int:7' [bugprone-too-small-loop-variable] |
372 | } |
373 | } |
374 | |
375 | void badForLoopWithBitfieldOnLoopVarAndUpperBoundOnPtr() { |
376 | struct StructWithBitField { |
377 | unsigned var : 5, limit : 6; |
378 | } value = {}; |
379 | |
380 | StructWithBitField* ptr = &value; |
381 | |
382 | for(ptr->var = 0U; ptr->var < ptr->limit; ++ptr->var) { |
383 | // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: loop variable has narrower type 'unsigned int:5' than iteration's upper bound 'unsigned int:6' [bugprone-too-small-loop-variable] |
384 | } |
385 | } |
386 | |
387 | void goodForLoopWithBitfieldOnUpperBoundOnly() { |
388 | struct S { |
389 | int x : 4; |
390 | } s; |
391 | |
392 | for (int i = 10; i > s.x; --i) { |
393 | } |
394 | } |
395 | |
396 | void goodForLoopWithIntegersOnUpperBoundOnly() { |
397 | struct S { |
398 | short x; |
399 | } s; |
400 | |
401 | for (int i = 10; i > s.x; --i) { |
402 | } |
403 | } |
404 | |