xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/bugprone/too-small-loop-variable.cpp (revision e8a3ddafe063c970df9c23e803812369abde4c82)
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 ////////////////////////////////////////////////////////////////////////////////
97 /// Correct loops: we should not warn here.
98 
99 // A simple use case when both expressions have the same type.
100 void voidGoodForLoop() {
101   for (long i = 0; i < size(); ++i) { // no warning
102   }
103 }
104 
105 // Other use case where both expressions have the same type,
106 // but short expressions are converted to int by the compare operator.
107 void voidGoodForLoop2() {
108   short loopCond = 10;
109   for (short i = 0; i < loopCond; ++i) { // no warning
110   }
111 }
112 
113 // Because of the integer literal, the iteration's upper bound is int, but we suppress the warning here.
114 void voidForLoopShortPlusLiteral() {
115   short size = 30000;
116   for (short i = 0; i <= (size - 1); ++i) { // no warning
117   }
118 }
119 
120 // Addition of two short variables results in an int value, but we suppress this to avoid false positives.
121 void voidForLoopShortPlusShort() {
122   short size = 256;
123   short increment = 14;
124   for (short i = 0; i < size + increment; ++i) { // no warning
125   }
126 }
127 
128 // In this test case we have different integer types, but here the loop variable has the bigger type.
129 // The iteration's bound is cast implicitly, not the loop variable.
130 void voidForLoopBoundImplicitCast() {
131   short start = 256;
132   short end = 14;
133   for (int i = start; i >= end; --i) { // no warning
134   }
135 }
136 
137 // Range based loop and other iterator based loops are ignored by this check.
138 void voidRangeBasedForLoop() {
139   int array[] = {1, 2, 3, 4, 5};
140   for (const int &i : array) { // no warning
141   }
142 }
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 /// Future possibilites to improve the check.
146 
147 // False positive: because of the int literal, iteration's upper bound has int type.
148 void voidForLoopFalsePositive() {
149   short size = 30000;
150   bool cond = false;
151   for (short i = 0; i < (cond ? 0 : size); ++i) {
152     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
153   }
154 }
155 
156 void voidForLoopFalsePositive2() {
157   short size = 30000;
158   bool cond = false;
159   for (short i = 0; i < (!cond ? size : 0); ++i) {
160     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
161   }
162 }
163 
164 // False positive: The loop bound expression contains nested binary operators.
165 void voidForLoopFalsePositive3() {
166   short number = 30000;
167   for (short i = 0; i < ((number & 0x7f) + 1); ++i) {
168     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
169   }
170 }
171 
172 // TODO: handle while loop.
173 void voidBadWhileLoop() {
174   short i = 0;
175   while (i < size()) { // missing warning
176     ++i;
177   }
178 }
179 
180 // TODO: handle do-while loop.
181 void voidBadDoWhileLoop() {
182   short i = 0;
183   do {
184     ++i;
185   } while (i < size()); // missing warning
186 }
187 
188 // TODO: handle complex loop conditions.
189 void voidComplexForCond() {
190   bool additionalCond = true;
191   for (int i = 0; i < size() && additionalCond; ++i) { // missing warning
192   }
193 }
194 
195 ////////////////////////////////////////////////////////////////////////////////
196 /// Suspicious test cases ingored by this check.
197 
198 // Test case with a reverse iteration.
199 // This is caught by -Wimplicit-int-conversion.
200 void voidReverseForLoop() {
201   for (short i = size() - 1; i >= 0; --i) { // no warning
202   }
203 }
204 
205 // Macro defined literals are used inside the loop condition.
206 #define SIZE 125
207 #define SIZE2 (SIZE + 1)
208 void voidForLoopWithMacroBound() {
209   for (short i = 0; i < SIZE2; ++i) { // no warning
210   }
211 }
212 
213 // A suspicious loop is not caught if the iteration's upper bound is a literal.
214 void voidForLoopWithLiteralBound() {
215   for (short i = 0; i < 125; ++i) { // no warning
216   }
217 }
218 
219 // The used literal leads to an infinite loop.
220 // This is caught by -Wtautological-constant-out-of-range-compare.
221 void voidForLoopWithBigLiteralBound() {
222   for (short i = 0; i < 294967296l; ++i) { // no warning
223   }
224 }
225 
226 enum eSizeType {
227   START,
228   Y,
229   END
230 };
231 
232 // A suspicious loop is not caught if the iteration's upper bound is an enum value.
233 void voidForLoopWithEnumBound() {
234   for (short i = eSizeType::START; i < eSizeType::END; ++i) { // no warning
235   }
236 }
237 
238 enum eSizeType2 : long {
239   START2 = 294967296l,
240   Y2,
241   END2
242 };
243 
244 // The used enum value leads to an infinite loop.
245 // This is caught by -Wtautological-constant-out-of-range-compare.
246 void voidForLoopWithBigEnumBound() {
247   for (short i = eSizeType2::START2; i < eSizeType2::END2; ++i) { // no warning
248   }
249 }
250 
251 // A suspicious loop is not caught if the iteration's upper bound is a constant variable.
252 void voidForLoopWithConstBound() {
253   const long size = 252l;
254   for (short i = 0; i < size; ++i) { // no warning
255   }
256 }
257 
258 // The used constant variable leads to an infinite loop.
259 // This is caught by -Wtautological-constant-out-of-range-compare.
260 void voidForLoopWithBigConstBound() {
261   const long size = 294967296l;
262   for (short i = 0; i < size; ++i) { // no warning
263   }
264 }
265 
266 // Should detect proper size of upper bound bitfield
267 void voidForLoopWithBitfieldOnUpperBound() {
268   struct StructWithBitField {
269       unsigned bitfield : 5;
270   } value = {};
271 
272   for(unsigned char i = 0U; i < value.bitfield; ++i) { // no warning
273   }
274 }
275 
276 // Should detect proper size of loop variable bitfield
277 void voidForLoopWithBitfieldOnLoopVar() {
278   struct StructWithBitField {
279       unsigned bitfield : 9;
280   } value = {};
281 
282   unsigned char upperLimit = 100U;
283 
284   for(value.bitfield = 0U; value.bitfield < upperLimit; ++value.bitfield) {
285   }
286 }
287 
288 // Should detect proper size of loop variable and upper bound
289 void voidForLoopWithBitfieldOnLoopVarAndUpperBound() {
290   struct StructWithBitField {
291       unsigned var : 5, limit : 4;
292   } value = {};
293 
294   for(value.var = 0U; value.var < value.limit; ++value.var) {
295   }
296 }
297 
298 // Should detect proper size of loop variable and upper bound on integers
299 void voidForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt() {
300   struct StructWithBitField {
301       unsigned var : 5;
302       int limit : 6;
303   } value = {};
304 
305   for(value.var = 0U; value.var < value.limit; ++value.var) {
306   }
307 }
308 
309 void badForLoopWithBitfieldOnUpperBound() {
310   struct StructWithBitField {
311       unsigned bitfield : 9;
312   } value = {};
313 
314   for(unsigned char i = 0U; i < value.bitfield; ++i) {
315   // 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]
316   }
317 }
318 
319 void badForLoopWithBitfieldOnLoopVar() {
320   struct StructWithBitField {
321       unsigned bitfield : 7;
322   } value = {};
323 
324   unsigned char upperLimit = 100U;
325 
326   for(value.bitfield = 0U; value.bitfield < upperLimit; ++value.bitfield) {
327   // 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]
328   }
329 }
330 
331 void badForLoopWithBitfieldOnLoopVarAndUpperBound() {
332   struct StructWithBitField {
333       unsigned var : 5, limit : 6;
334   } value = {};
335 
336   for(value.var = 0U; value.var < value.limit; ++value.var) {
337   // 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]
338   }
339 }
340 
341 void badForLoopWithBitfieldOnLoopVarOnIntAndUpperBound() {
342   struct StructWithBitField {
343       int var : 5;
344       unsigned limit : 5;
345   } value = {};
346 
347   for(value.var = 0U; value.var < value.limit; ++value.var) {
348   // 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]
349   }
350 }
351 
352 void badForLoopWithBitfieldOnLoopVarAndUpperBoundOnInt() {
353   struct StructWithBitField {
354       unsigned var : 5;
355       int limit : 7;
356   } value = {};
357 
358   for(value.var = 0U; value.var < value.limit; ++value.var) {
359   // 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]
360   }
361 }
362 
363 void badForLoopWithBitfieldOnLoopVarAndUpperBoundOnPtr() {
364   struct StructWithBitField {
365       unsigned var : 5, limit : 6;
366   } value = {};
367 
368   StructWithBitField* ptr = &value;
369 
370   for(ptr->var = 0U; ptr->var < ptr->limit; ++ptr->var) {
371   // 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]
372   }
373 }
374 
375 void goodForLoopWithBitfieldOnUpperBoundOnly() {
376   struct S {
377     int x : 4;
378   } s;
379 
380   for (int i = 10; i > s.x; --i) {
381   }
382 }
383 
384 void goodForLoopWithIntegersOnUpperBoundOnly() {
385   struct S {
386     short x;
387   } s;
388 
389   for (int i = 10; i > s.x; --i) {
390   }
391 }
392