xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/bugprone/too-small-loop-variable.cpp (revision a7520d9727d2638047e5c464b2937581f64e2ce5)
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 
size()6 long size() { return 294967296l; }
7 
8 ////////////////////////////////////////////////////////////////////////////////
9 /// Test cases correctly caught by bugprone-too-small-loop-variable.
10 
voidBadForLoop()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 
voidBadForLoop2()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 
voidBadForLoop3()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 
voidBadForLoop4()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 
voidBadForLoop5()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 
voidBadForLoop6()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 
voidBadForLoop7()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 
voidForLoopUnsignedBound()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>
doSomething()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>
doSomething()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 
voidForLoopInstantiation()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())
voidBadForLoopWithMacroBound()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 
getVal()96 unsigned int getVal() {
97     return 300;
98 }
99 
100 // The iteration's upper bound has a function declaration.
voidBadForLoop8()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.
voidGoodForLoop()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.
voidGoodForLoop2()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.
voidForLoopShortPlusLiteral()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.
voidForLoopShortPlusShort()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.
voidForLoopBoundImplicitCast()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.
voidRangeBasedForLoop()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.
voidForLoopFalsePositive()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 
voidForLoopFalsePositive2()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.
voidForLoopFalsePositive3()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.
voidBadWhileLoop()185 void voidBadWhileLoop() {
186   short i = 0;
187   while (i < size()) { // missing warning
188     ++i;
189   }
190 }
191 
192 // TODO: handle do-while loop.
voidBadDoWhileLoop()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.
voidComplexForCond()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.
voidReverseForLoop()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)
voidForLoopWithMacroBound()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.
voidForLoopWithLiteralBound()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.
voidForLoopWithBigLiteralBound()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.
voidForLoopWithEnumBound()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.
voidForLoopWithBigEnumBound()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.
voidForLoopWithConstBound()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.
voidForLoopWithBigConstBound()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
voidForLoopWithBitfieldOnUpperBound()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
voidForLoopWithBitfieldOnLoopVar()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
voidForLoopWithBitfieldOnLoopVarAndUpperBound()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
voidForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt()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 
badForLoopWithBitfieldOnUpperBound()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 
badForLoopWithBitfieldOnLoopVar()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 
badForLoopWithBitfieldOnLoopVarAndUpperBound()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 
badForLoopWithBitfieldOnLoopVarOnIntAndUpperBound()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 
badForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt()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 
badForLoopWithBitfieldOnLoopVarAndUpperBoundOnPtr()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 
goodForLoopWithBitfieldOnUpperBoundOnly()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 
goodForLoopWithIntegersOnUpperBoundOnly()396 void goodForLoopWithIntegersOnUpperBoundOnly() {
397   struct S {
398     short x;
399   } s;
400 
401   for (int i = 10; i > s.x; --i) {
402   }
403 }
404