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 
size()7 long size() { return 294967296l; }
8 
9 ////////////////////////////////////////////////////////////////////////////////
10 /// Test cases correctly caught by bugprone-too-small-loop-variable.
11 
voidBadForLoop()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 
voidBadForLoop2()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 
voidBadForLoop3()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 
voidBadForLoop4()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 
voidBadForLoop5()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 
voidBadForLoop6()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 
voidForLoopUnsignedBound()49 void voidForLoopUnsignedBound() {
50   unsigned size = 3147483647;
51   for (int i = 0; i < size; ++i) {
52     // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'unsigned int' [bugprone-too-small-loop-variable]
53   }
54 }
55 
56 // The iteration's upper bound has a template dependent value.
57 template <long size>
doSomething()58 void doSomething() {
59   for (short i = 0; i < size; ++i) {
60     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable]
61   }
62 }
63 
64 // The iteration's upper bound has a template dependent type.
65 template <class T>
doSomething()66 void doSomething() {
67   for (T i = 0; i < size(); ++i) {
68     // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable]
69   }
70 }
71 
voidForLoopInstantiation()72 void voidForLoopInstantiation() {
73   // This line does not trigger the warning.
74   doSomething<long>();
75   // This one triggers the warning.
76   doSomething<short>();
77 }
78 
79 // A suspicious function used in a macro.
80 #define SUSPICIOUS_SIZE (size())
voidBadForLoopWithMacroBound()81 void voidBadForLoopWithMacroBound() {
82   for (short i = 0; i < SUSPICIOUS_SIZE; ++i) {
83     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable]
84   }
85 }
86 
87 ////////////////////////////////////////////////////////////////////////////////
88 /// Correct loops: we should not warn here.
89 
90 // A simple use case when both expressions have the same type.
voidGoodForLoop()91 void voidGoodForLoop() {
92   for (long i = 0; i < size(); ++i) { // no warning
93   }
94 }
95 
96 // Other use case where both expressions have the same type,
97 // but short expressions are converted to int by the compare operator.
voidGoodForLoop2()98 void voidGoodForLoop2() {
99   short loopCond = 10;
100   for (short i = 0; i < loopCond; ++i) { // no warning
101   }
102 }
103 
104 // Because of the integer literal, the iteration's upper bound is int, but we suppress the warning here.
voidForLoopShortPlusLiteral()105 void voidForLoopShortPlusLiteral() {
106   short size = 30000;
107   for (short i = 0; i <= (size - 1); ++i) { // no warning
108   }
109 }
110 
111 // Addition of two short variables results in an int value, but we suppress this to avoid false positives.
voidForLoopShortPlusShort()112 void voidForLoopShortPlusShort() {
113   short size = 256;
114   short increment = 14;
115   for (short i = 0; i < size + increment; ++i) { // no warning
116   }
117 }
118 
119 // In this test case we have different integer types, but here the loop variable has the bigger type.
120 // The iteration's bound is cast implicitly, not the loop variable.
voidForLoopBoundImplicitCast()121 void voidForLoopBoundImplicitCast() {
122   short start = 256;
123   short end = 14;
124   for (int i = start; i >= end; --i) { // no warning
125   }
126 }
127 
128 // Range based loop and other iterator based loops are ignored by this check.
voidRangeBasedForLoop()129 void voidRangeBasedForLoop() {
130   int array[] = {1, 2, 3, 4, 5};
131   for (const int &i : array) { // no warning
132   }
133 }
134 
135 ////////////////////////////////////////////////////////////////////////////////
136 /// Future possibilites to improve the check.
137 
138 // False positive: because of the int literal, iteration's upper bound has int type.
voidForLoopFalsePositive()139 void voidForLoopFalsePositive() {
140   short size = 30000;
141   bool cond = false;
142   for (short i = 0; i < (cond ? 0 : size); ++i) {
143     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
144   }
145 }
146 
voidForLoopFalsePositive2()147 void voidForLoopFalsePositive2() {
148   short size = 30000;
149   bool cond = false;
150   for (short i = 0; i < (!cond ? size : 0); ++i) {
151     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
152   }
153 }
154 
155 // False positive: The loop bound expression contains nested binary operators.
voidForLoopFalsePositive3()156 void voidForLoopFalsePositive3() {
157   short number = 30000;
158   for (short i = 0; i < ((number & 0x7f) + 1); ++i) {
159     // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable]
160   }
161 }
162 
163 // TODO: handle while loop.
voidBadWhileLoop()164 void voidBadWhileLoop() {
165   short i = 0;
166   while (i < size()) { // missing warning
167     ++i;
168   }
169 }
170 
171 // TODO: handle do-while loop.
voidBadDoWhileLoop()172 void voidBadDoWhileLoop() {
173   short i = 0;
174   do {
175     ++i;
176   } while (i < size()); // missing warning
177 }
178 
179 // TODO: handle complex loop conditions.
voidComplexForCond()180 void voidComplexForCond() {
181   bool additionalCond = true;
182   for (int i = 0; i < size() && additionalCond; ++i) { // missing warning
183   }
184 }
185 
186 ////////////////////////////////////////////////////////////////////////////////
187 /// Suspicious test cases ingored by this check.
188 
189 // Test case with a reverse iteration.
190 // This is caught by -Wimplicit-int-conversion.
voidReverseForLoop()191 void voidReverseForLoop() {
192   for (short i = size() - 1; i >= 0; --i) { // no warning
193   }
194 }
195 
196 // Macro defined literals are used inside the loop condition.
197 #define SIZE 125
198 #define SIZE2 (SIZE + 1)
voidForLoopWithMacroBound()199 void voidForLoopWithMacroBound() {
200   for (short i = 0; i < SIZE2; ++i) { // no warning
201   }
202 }
203 
204 // A suspicious loop is not caught if the iteration's upper bound is a literal.
voidForLoopWithLiteralBound()205 void voidForLoopWithLiteralBound() {
206   for (short i = 0; i < 125; ++i) { // no warning
207   }
208 }
209 
210 // The used literal leads to an infinite loop.
211 // This is caught by -Wtautological-constant-out-of-range-compare.
voidForLoopWithBigLiteralBound()212 void voidForLoopWithBigLiteralBound() {
213   for (short i = 0; i < 294967296l; ++i) { // no warning
214   }
215 }
216 
217 enum eSizeType {
218   START,
219   Y,
220   END
221 };
222 
223 // A suspicious loop is not caught if the iteration's upper bound is an enum value.
voidForLoopWithEnumBound()224 void voidForLoopWithEnumBound() {
225   for (short i = eSizeType::START; i < eSizeType::END; ++i) { // no warning
226   }
227 }
228 
229 enum eSizeType2 : long {
230   START2 = 294967296l,
231   Y2,
232   END2
233 };
234 
235 // The used enum value leads to an infinite loop.
236 // This is caught by -Wtautological-constant-out-of-range-compare.
voidForLoopWithBigEnumBound()237 void voidForLoopWithBigEnumBound() {
238   for (short i = eSizeType2::START2; i < eSizeType2::END2; ++i) { // no warning
239   }
240 }
241 
242 // A suspicious loop is not caught if the iteration's upper bound is a constant variable.
voidForLoopWithConstBound()243 void voidForLoopWithConstBound() {
244   const long size = 252l;
245   for (short i = 0; i < size; ++i) { // no warning
246   }
247 }
248 
249 // The used constant variable leads to an infinite loop.
250 // This is caught by -Wtautological-constant-out-of-range-compare.
voidForLoopWithBigConstBound()251 void voidForLoopWithBigConstBound() {
252   const long size = 294967296l;
253   for (short i = 0; i < size; ++i) { // no warning
254   }
255 }
256