1// RUN: %check_clang_tidy %s bugprone-infinite-loop %t \
2// RUN: -- -- -fexceptions -fblocks -fno-delayed-template-parsing
3
4void simple_infinite_loop1() {
5 int i = 0;
6 int j = 0;
7 while (i < 10) {
8 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
9 j++;
10 }
11
12 while (int k = 10) {
13 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]
14 j--;
15 }
16
17 while (int k = 10) {
18 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]
19 k--;
20 }
21
22 do {
23 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
24 j++;
25 } while (i < 10);
26
27 for (i = 0; i < 10; ++j) {
28 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
29 }
30}
31
32void simple_infinite_loop2() {
33 int i = 0;
34 int j = 0;
35 int Limit = 10;
36 while (i < Limit) {
37 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]
38 j++;
39 }
40
41 while (int k = Limit) {
42 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]
43 j--;
44 }
45
46 while (int k = Limit) {
47 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]
48 k--;
49 }
50
51 do {
52 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]
53 j++;
54 } while (i < Limit);
55
56 for (i = 0; i < Limit; ++j) {
57 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]
58 }
59}
60
61void simple_not_infinite1() {
62 int i = 0;
63 int Limit = 100;
64 while (i < Limit) {
65 // Not an error since 'Limit' is updated.
66 Limit--;
67 }
68
69 while (Limit--) {
70 // Not an error since 'Limit' is updated.
71 i++;
72 }
73
74 while ((Limit)--) {
75 // Not an error since 'Limit' is updated.
76 i++;
77 }
78
79 while ((Limit) -= 1) {
80 // Not an error since 'Limit' is updated.
81 }
82
83 while (int k = Limit) {
84 // Not an error since 'Limit' is updated.
85 Limit--;
86 }
87
88 while (int k = Limit) {
89 // Not an error since 'Limit' is updated
90 (Limit)--;
91 }
92
93 while (int k = Limit--) {
94 // Not an error since 'Limit' is updated.
95 i++;
96 }
97
98 do {
99 Limit--;
100 } while (i < Limit);
101
102 for (i = 0; i < Limit; Limit--) {
103 }
104
105 for (i = 0; i < Limit; (Limit) = Limit - 1) {
106 }
107
108 for (i = 0; i < Limit; (Limit) -= 1) {
109 }
110
111 for (i = 0; i < Limit; --(Limit)) {
112 }
113}
114
115void simple_not_infinite2() {
116 for (int i = 10; i-- > 0;) {
117 // Not an error, since loop variable is modified in its condition part.
118 }
119}
120
121int unknown_function();
122
123void function_call() {
124 int i = 0;
125 while (i < unknown_function()) {
126 // Not an error, since the function may return different values.
127 }
128
129 do {
130 // Not an error, since the function may return different values.
131 } while (i < unknown_function());
132
133 for (i = 0; i < unknown_function();) {
134 // Not an error, since the function may return different values.
135 }
136}
137
138void escape_before1() {
139 int i = 0;
140 int Limit = 100;
141 int *p = &i;
142 while (i < Limit) {
143 // Not an error, since *p is alias of i.
144 (*p)++;
145 }
146
147 do {
148 (*p)++;
149 } while (i < Limit);
150
151 for (i = 0; i < Limit; ++(*p)) {
152 }
153}
154
155void escape_before2() {
156 int i = 0;
157 int Limit = 100;
158 int &ii = i;
159 while (i < Limit) {
160 // Not an error, since ii is alias of i.
161 ii++;
162 }
163
164 do {
165 ii++;
166 } while (i < Limit);
167
168 for (i = 0; i < Limit; ++ii) {
169 }
170}
171
172void escape_inside1() {
173 int i = 0;
174 int Limit = 100;
175 int *p = &i;
176 while (i < Limit) {
177 // Not an error, since *p is alias of i.
178 int *p = &i;
179 (*p)++;
180 }
181
182 do {
183 int *p = &i;
184 (*p)++;
185 } while (i < Limit);
186}
187
188void escape_inside2() {
189 int i = 0;
190 int Limit = 100;
191 while (i < Limit) {
192 // Not an error, since ii is alias of i.
193 int &ii = i;
194 ii++;
195 }
196
197 do {
198 int &ii = i;
199 ii++;
200 } while (i < Limit);
201}
202
203void escape_after1() {
204 int i = 0;
205 int j = 0;
206 int Limit = 10;
207
208 while (i < Limit) {
209 // False negative, but difficult to detect without CFG-based analysis
210 }
211 int *p = &i;
212}
213
214void escape_after2() {
215 int i = 0;
216 int j = 0;
217 int Limit = 10;
218
219 while (i < Limit) {
220 // False negative, but difficult to detect without CFG-based analysis
221 }
222 int &ii = i;
223}
224
225int glob;
226
227void global1(int &x) {
228 int i = 0, Limit = 100;
229 while (x < Limit) {
230 // Not an error since 'x' can be an alias of 'glob'.
231 glob++;
232 }
233}
234
235void global2() {
236 int i = 0, Limit = 100;
237 while (glob < Limit) {
238 // Since 'glob' is declared out of the function we do not warn.
239 i++;
240 }
241}
242
243struct X {
244 int m;
245
246 void change_m();
247
248 void member_expr1(int i) {
249 while (i < m) {
250 // False negative: No warning, since skipping the case where a struct or
251 // class can be found in its condition.
252 ;
253 }
254 }
255
256 void member_expr2(int i) {
257 while (i < m) {
258 --m;
259 }
260 }
261
262 void member_expr3(int i) {
263 while (i < m) {
264 change_m();
265 }
266 }
267};
268
269void array_index() {
270 int i = 0;
271 int v[10];
272 while (i < 10) {
273 v[i++] = 0;
274 }
275
276 i = 0;
277 do {
278 v[i++] = 0;
279 } while (i < 9);
280
281 for (i = 0; i < 10;) {
282 v[i++] = 0;
283 }
284
285 for (i = 0; i < 10; v[i++] = 0) {
286 }
287}
288
289void no_loop_variable() {
290 while (0)
291 ;
292}
293
294void volatile_in_condition() {
295 volatile int cond = 0;
296 while (!cond) {
297 }
298}
299
300namespace std {
301template<typename T> class atomic {
302 T val;
303public:
304 atomic(T v): val(v) {};
305 operator T() { return val; };
306};
307}
308
309void atomic_in_condition() {
310 std::atomic<int> cond = 0;
311 while (!cond) {
312 }
313}
314
315void loop_exit1() {
316 int i = 0;
317 while (i) {
318 if (unknown_function())
319 break;
320 }
321}
322
323void loop_exit2() {
324 int i = 0;
325 while (i) {
326 if (unknown_function())
327 return;
328 }
329}
330
331void loop_exit3() {
332 int i = 0;
333 while (i) {
334 if (unknown_function())
335 goto end;
336 }
337 end:
338 ;
339}
340
341void loop_exit4() {
342 int i = 0;
343 while (i) {
344 if (unknown_function())
345 throw 1;
346 }
347}
348
349[[noreturn]] void exit(int);
350
351void loop_exit5() {
352 int i = 0;
353 while (i) {
354 if (unknown_function())
355 exit(1);
356 }
357}
358
359void loop_exit_in_lambda() {
360 int i = 0;
361 while (i) {
362 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
363 auto l = []() { return 0; };
364 }
365}
366
367void lambda_capture() {
368 int i = 0;
369 int Limit = 100;
370 int *p = &i;
371 while (i < Limit) {
372 // Not an error, since i is captured by reference in a lambda.
373 auto l = [&i]() { ++i; };
374 }
375
376 do {
377 int *p = &i;
378 (*p)++;
379 } while (i < Limit);
380}
381
382template <typename T> void accept_callback(T t) {
383 // Potentially call the callback.
384 // Possibly on a background thread or something.
385}
386
387void accept_block(void (^)(void)) {
388 // Potentially call the callback.
389 // Possibly on a background thread or something.
390}
391
392void wait(void) {
393 // Wait for the previously passed callback to be called.
394}
395
396void lambda_capture_from_outside() {
397 bool finished = false;
398 accept_callback(t: [&]() {
399 finished = true;
400 });
401 while (!finished) {
402 wait();
403 }
404}
405
406void lambda_capture_from_outside_by_value() {
407 bool finished = false;
408 accept_callback(t: [finished]() {
409 if (finished) {}
410 });
411 while (!finished) {
412 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
413 wait();
414 }
415}
416
417void lambda_capture_from_outside_but_unchanged() {
418 bool finished = false;
419 accept_callback(t: [&finished]() {
420 if (finished) {}
421 });
422 while (!finished) {
423 // FIXME: Should warn.
424 wait();
425 }
426}
427
428void block_capture_from_outside() {
429 __block bool finished = false;
430 accept_block(^{
431 finished = true;
432 });
433 while (!finished) {
434 wait();
435 }
436}
437
438void block_capture_from_outside_by_value() {
439 bool finished = false;
440 accept_block(^{
441 if (finished) {}
442 });
443 while (!finished) {
444 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
445 wait();
446 }
447}
448
449void block_capture_from_outside_but_unchanged() {
450 __block bool finished = false;
451 accept_block(^{
452 if (finished) {}
453 });
454 while (!finished) {
455 // FIXME: Should warn.
456 wait();
457 }
458}
459
460void finish_at_any_time(bool *finished);
461
462void lambda_capture_with_loop_inside_lambda_bad() {
463 bool finished = false;
464 auto lambda = [=]() {
465 while (!finished) {
466 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
467 wait();
468 }
469 };
470 finish_at_any_time(finished: &finished);
471 lambda();
472}
473
474void lambda_capture_with_loop_inside_lambda_bad_init_capture() {
475 bool finished = false;
476 auto lambda = [captured_finished=finished]() {
477 while (!captured_finished) {
478 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (captured_finished) are updated in the loop body [bugprone-infinite-loop]
479 wait();
480 }
481 };
482 finish_at_any_time(finished: &finished);
483 lambda();
484}
485
486void lambda_capture_with_loop_inside_lambda_good() {
487 bool finished = false;
488 auto lambda = [&]() {
489 while (!finished) {
490 wait(); // No warning: the variable may be updated
491 // from outside the lambda.
492 }
493 };
494 finish_at_any_time(finished: &finished);
495 lambda();
496}
497
498void lambda_capture_with_loop_inside_lambda_good_init_capture() {
499 bool finished = false;
500 auto lambda = [&captured_finished=finished]() {
501 while (!captured_finished) {
502 wait(); // No warning: the variable may be updated
503 // from outside the lambda.
504 }
505 };
506 finish_at_any_time(finished: &finished);
507 lambda();
508}
509
510void block_capture_with_loop_inside_block_bad() {
511 bool finished = false;
512 auto block = ^() {
513 while (!finished) {
514 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
515 wait();
516 }
517 };
518 finish_at_any_time(finished: &finished);
519 block();
520}
521
522void block_capture_with_loop_inside_block_bad_simpler() {
523 bool finished = false;
524 auto block = ^() {
525 while (!finished) {
526 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
527 wait();
528 }
529 };
530 block();
531}
532
533void block_capture_with_loop_inside_block_good() {
534 __block bool finished = false;
535 auto block = ^() {
536 while (!finished) {
537 wait(); // No warning: the variable may be updated
538 // from outside the block.
539 }
540 };
541 finish_at_any_time(&finished);
542 block();
543}
544
545void evaluatable(bool CondVar) {
546 for (; false && CondVar;) {
547 }
548 while (false && CondVar) {
549 }
550 do {
551 } while (false && CondVar);
552}
553
554struct logger {
555 void (*debug)(struct logger *, const char *, ...);
556};
557
558int foo(void) {
559 struct logger *pl = 0;
560 int iterator = 0;
561 while (iterator < 10) {
562 char *l_tmp_msg = 0;
563 pl->debug(pl, "%d: %s\n", iterator, l_tmp_msg);
564 iterator++;
565 }
566 return 0;
567}
568
569struct AggregateWithReference {
570 int &y;
571};
572
573void test_structured_bindings_good() {
574 int x = 0;
575 AggregateWithReference ref { .y: x };
576 auto &[y] = ref;
577 for (; x < 10; ++y) {
578 // No warning. The loop is finite because 'y' is a reference to 'x'.
579 }
580}
581
582struct AggregateWithValue {
583 int y;
584};
585
586void test_structured_bindings_bad() {
587 int x = 0;
588 AggregateWithValue val { .y: x };
589 auto &[y] = val;
590 for (; x < 10; ++y) {
591 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]
592 }
593}
594
595void test_volatile_cast() {
596 // This is a no-op cast. Clang ignores the qualifier, we should too.
597 for (int i = 0; (volatile int)i < 10;) {
598 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
599 }
600}
601
602void test_volatile_concrete_address(int i, int size) {
603 // No warning. The value behind the volatile concrete address
604 // is beyond our control. It may change at any time.
605 for (; *((volatile int *)0x1234) < size;) {
606 }
607
608 for (; *((volatile int *)(0x1234 + i)) < size;) {
609 }
610
611 for (; **((volatile int **)0x1234) < size;) {
612 }
613
614 volatile int *x = (volatile int *)0x1234;
615 for (; *x < 10;) {
616 }
617
618 // FIXME: This one should probably also be suppressed.
619 // Whatever the developer is doing here, they can do that again anywhere else
620 // which basically makes it a global.
621 for (; *(int *)0x1234 < size;) {
622 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (size) are updated in the loop body [bugprone-infinite-loop]
623 }
624}
625
626template <typename T>
627int some_template_fn() { return 1; }
628
629template <typename T>
630void test_dependent_condition() {
631 const int error = some_template_fn<T>();
632 do {
633 } while (false && error == 0);
634
635 const int val = some_template_fn<T>();
636 for (; !(val == 0 || true);) {
637 }
638
639 const int val2 = some_template_fn<T>();
640 for (; !(val2 == 0 || false);) {
641 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val2) are updated in the loop body [bugprone-infinite-loop]
642 }
643
644 const int val3 = some_template_fn<T>();
645 do {
646 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val3) are updated in the loop body [bugprone-infinite-loop]
647 } while (1, (true) && val3 == 1);
648
649 const int val4 = some_template_fn<T>();
650 do {
651 } while (1, (false) && val4 == 1);
652}
653
654void test_typeof() {
655 __typeof__({
656 for (int i = 0; i < 10; ++i) {
657 }
658 0;
659 }) x;
660}
661
662void test_typeof_infinite() {
663 __typeof__({
664 for (int i = 0; i < 10;) {
665 }
666 0;
667 }) x;
668}
669
670void test_typeof_while_infinite() {
671 __typeof__({
672 int i = 0;
673 while (i < 10) {
674 }
675 0;
676 }) x;
677}
678
679void test_typeof_dowhile_infinite() {
680 __typeof__({
681 int i = 0;
682 do {
683
684 } while (i < 10);
685 0;
686 }) x;
687}
688
689void test_local_static_recursion() {
690 static int i = 10;
691 int j = 0;
692
693 i--;
694 while (i >= 0)
695 test_local_static_recursion(); // no warning, recursively decrement i
696 for (; i >= 0;)
697 test_local_static_recursion(); // no warning, recursively decrement i
698 for (; i + j >= 0;)
699 test_local_static_recursion(); // no warning, recursively decrement i
700 for (; i >= 0; i--)
701 ; // no warning, i decrements
702 while (j >= 0)
703 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]
704 test_local_static_recursion();
705
706 int (*p)(int) = 0;
707
708 while (i >= 0)
709 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
710 p = 0;
711 while (i >= 0)
712 p(0); // we don't know what p points to so no warning
713}
714

source code of clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp