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