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