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
6long size() { return 294967296l; }
7
8////////////////////////////////////////////////////////////////////////////////
9/// Test cases correctly caught by bugprone-too-small-loop-variable.
10
11void 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
17void 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
23void 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
29void 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
35void 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
41void 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
48void 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
58void 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.
66template <long size>
67void 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.
74template <class T>
75void 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
81void 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())
90void 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
96unsigned int getVal() {
97 return 300;
98}
99
100// The iteration's upper bound has a function declaration.
101void 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.
112void 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.
119void 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.
126void 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.
133void 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.
142void 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.
150void 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.
160void 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
168void 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.
177void 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.
185void voidBadWhileLoop() {
186 short i = 0;
187 while (i < size()) { // missing warning
188 ++i;
189 }
190}
191
192// TODO: handle do-while loop.
193void voidBadDoWhileLoop() {
194 short i = 0;
195 do {
196 ++i;
197 } while (i < size()); // missing warning
198}
199
200// TODO: handle complex loop conditions.
201void 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.
212void 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)
220void 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.
226void 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.
233void voidForLoopWithBigLiteralBound() {
234 for (short i = 0; i < 294967296l; ++i) { // no warning
235 }
236}
237
238enum 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.
245void voidForLoopWithEnumBound() {
246 for (short i = eSizeType::START; i < eSizeType::END; ++i) { // no warning
247 }
248}
249
250enum 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.
258void 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.
264void 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.
272void 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
279void 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
289void 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
301void 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
311void 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
321void 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
331void 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
343void 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
353void 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
364void 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
375void 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
387void goodForLoopWithBitfieldOnUpperBoundOnly() {
388 struct S {
389 int x : 4;
390 } s;
391
392 for (int i = 10; i > s.x; --i) {
393 }
394}
395
396void goodForLoopWithIntegersOnUpperBoundOnly() {
397 struct S {
398 short x;
399 } s;
400
401 for (int i = 10; i > s.x; --i) {
402 }
403}
404

source code of clang-tools-extra/test/clang-tidy/checkers/bugprone/too-small-loop-variable.cpp