// RUN: %check_clang_tidy %s bugprone-infinite-loop %t \ // RUN: -- -- -fexceptions -fblocks -fno-delayed-template-parsing void simple_infinite_loop1() { int i = 0; int j = 0; while (i < 10) { // 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] j++; } while (int k = 10) { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop] j--; } while (int k = 10) { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop] k--; } do { // 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] j++; } while (i < 10); for (i = 0; i < 10; ++j) { // 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] } } void simple_infinite_loop2() { int i = 0; int j = 0; int Limit = 10; while (i < Limit) { // 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] j++; } while (int k = Limit) { // 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] j--; } while (int k = Limit) { // 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] k--; } do { // 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] j++; } while (i < Limit); for (i = 0; i < Limit; ++j) { // 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] } } void simple_not_infinite1() { int i = 0; int Limit = 100; while (i < Limit) { // Not an error since 'Limit' is updated. Limit--; } while (Limit--) { // Not an error since 'Limit' is updated. i++; } while ((Limit)--) { // Not an error since 'Limit' is updated. i++; } while ((Limit) -= 1) { // Not an error since 'Limit' is updated. } while (int k = Limit) { // Not an error since 'Limit' is updated. Limit--; } while (int k = Limit) { // Not an error since 'Limit' is updated (Limit)--; } while (int k = Limit--) { // Not an error since 'Limit' is updated. i++; } do { Limit--; } while (i < Limit); for (i = 0; i < Limit; Limit--) { } for (i = 0; i < Limit; (Limit) = Limit - 1) { } for (i = 0; i < Limit; (Limit) -= 1) { } for (i = 0; i < Limit; --(Limit)) { } } void simple_not_infinite2() { for (int i = 10; i-- > 0;) { // Not an error, since loop variable is modified in its condition part. } } int unknown_function(); void function_call() { int i = 0; while (i < unknown_function()) { // Not an error, since the function may return different values. } do { // Not an error, since the function may return different values. } while (i < unknown_function()); for (i = 0; i < unknown_function();) { // Not an error, since the function may return different values. } } void escape_before1() { int i = 0; int Limit = 100; int *p = &i; while (i < Limit) { // Not an error, since *p is alias of i. (*p)++; } do { (*p)++; } while (i < Limit); for (i = 0; i < Limit; ++(*p)) { } } void escape_before2() { int i = 0; int Limit = 100; int &ii = i; while (i < Limit) { // Not an error, since ii is alias of i. ii++; } do { ii++; } while (i < Limit); for (i = 0; i < Limit; ++ii) { } } void escape_inside1() { int i = 0; int Limit = 100; int *p = &i; while (i < Limit) { // Not an error, since *p is alias of i. int *p = &i; (*p)++; } do { int *p = &i; (*p)++; } while (i < Limit); } void escape_inside2() { int i = 0; int Limit = 100; while (i < Limit) { // Not an error, since ii is alias of i. int &ii = i; ii++; } do { int &ii = i; ii++; } while (i < Limit); } void escape_after1() { int i = 0; int j = 0; int Limit = 10; while (i < Limit) { // False negative, but difficult to detect without CFG-based analysis } int *p = &i; } void escape_after2() { int i = 0; int j = 0; int Limit = 10; while (i < Limit) { // False negative, but difficult to detect without CFG-based analysis } int &ii = i; } int glob; void global1(int &x) { int i = 0, Limit = 100; while (x < Limit) { // Not an error since 'x' can be an alias of 'glob'. glob++; } } void global2() { int i = 0, Limit = 100; while (glob < Limit) { // Since 'glob' is declared out of the function we do not warn. i++; } } struct X { int m; void change_m(); void member_expr1(int i) { while (i < m) { // False negative: No warning, since skipping the case where a struct or // class can be found in its condition. ; } } void member_expr2(int i) { while (i < m) { --m; } } void member_expr3(int i) { while (i < m) { change_m(); } } }; void array_index() { int i = 0; int v[10]; while (i < 10) { v[i++] = 0; } i = 0; do { v[i++] = 0; } while (i < 9); for (i = 0; i < 10;) { v[i++] = 0; } for (i = 0; i < 10; v[i++] = 0) { } } void no_loop_variable() { while (0) ; } void volatile_in_condition() { volatile int cond = 0; while (!cond) { } } namespace std { template class atomic { T val; public: atomic(T v): val(v) {}; operator T() { return val; }; }; } void atomic_in_condition() { std::atomic cond = 0; while (!cond) { } } void loop_exit1() { int i = 0; while (i) { if (unknown_function()) break; } } void loop_exit2() { int i = 0; while (i) { if (unknown_function()) return; } } void loop_exit3() { int i = 0; while (i) { if (unknown_function()) goto end; } end: ; } void loop_exit4() { int i = 0; while (i) { if (unknown_function()) throw 1; } } [[noreturn]] void exit(int); void loop_exit5() { int i = 0; while (i) { if (unknown_function()) exit(1); } } void loop_exit_in_lambda() { int i = 0; while (i) { // 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] auto l = []() { return 0; }; } } void lambda_capture() { int i = 0; int Limit = 100; int *p = &i; while (i < Limit) { // Not an error, since i is captured by reference in a lambda. auto l = [&i]() { ++i; }; } do { int *p = &i; (*p)++; } while (i < Limit); } template void accept_callback(T t) { // Potentially call the callback. // Possibly on a background thread or something. } void accept_block(void (^)(void)) { // Potentially call the callback. // Possibly on a background thread or something. } void wait(void) { // Wait for the previously passed callback to be called. } void lambda_capture_from_outside() { bool finished = false; accept_callback([&]() { finished = true; }); while (!finished) { wait(); } } void lambda_capture_from_outside_by_value() { bool finished = false; accept_callback([finished]() { if (finished) {} }); while (!finished) { // 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] wait(); } } void lambda_capture_from_outside_but_unchanged() { bool finished = false; accept_callback([&finished]() { if (finished) {} }); while (!finished) { // FIXME: Should warn. wait(); } } void block_capture_from_outside() { __block bool finished = false; accept_block(^{ finished = true; }); while (!finished) { wait(); } } void block_capture_from_outside_by_value() { bool finished = false; accept_block(^{ if (finished) {} }); while (!finished) { // 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] wait(); } } void block_capture_from_outside_but_unchanged() { __block bool finished = false; accept_block(^{ if (finished) {} }); while (!finished) { // FIXME: Should warn. wait(); } } void finish_at_any_time(bool *finished); void lambda_capture_with_loop_inside_lambda_bad() { bool finished = false; auto lambda = [=]() { while (!finished) { // 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] wait(); } }; finish_at_any_time(&finished); lambda(); } void lambda_capture_with_loop_inside_lambda_bad_init_capture() { bool finished = false; auto lambda = [captured_finished=finished]() { while (!captured_finished) { // 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] wait(); } }; finish_at_any_time(&finished); lambda(); } void lambda_capture_with_loop_inside_lambda_good() { bool finished = false; auto lambda = [&]() { while (!finished) { wait(); // No warning: the variable may be updated // from outside the lambda. } }; finish_at_any_time(&finished); lambda(); } void lambda_capture_with_loop_inside_lambda_good_init_capture() { bool finished = false; auto lambda = [&captured_finished=finished]() { while (!captured_finished) { wait(); // No warning: the variable may be updated // from outside the lambda. } }; finish_at_any_time(&finished); lambda(); } void block_capture_with_loop_inside_block_bad() { bool finished = false; auto block = ^() { while (!finished) { // 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] wait(); } }; finish_at_any_time(&finished); block(); } void block_capture_with_loop_inside_block_bad_simpler() { bool finished = false; auto block = ^() { while (!finished) { // 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] wait(); } }; block(); } void block_capture_with_loop_inside_block_good() { __block bool finished = false; auto block = ^() { while (!finished) { wait(); // No warning: the variable may be updated // from outside the block. } }; finish_at_any_time(&finished); block(); } void evaluatable(bool CondVar) { for (; false && CondVar;) { } while (false && CondVar) { } do { } while (false && CondVar); } struct logger { void (*debug)(struct logger *, const char *, ...); }; int foo(void) { struct logger *pl = 0; int iterator = 0; while (iterator < 10) { char *l_tmp_msg = 0; pl->debug(pl, "%d: %s\n", iterator, l_tmp_msg); iterator++; } return 0; } struct AggregateWithReference { int &y; }; void test_structured_bindings_good() { int x = 0; AggregateWithReference ref { x }; auto &[y] = ref; for (; x < 10; ++y) { // No warning. The loop is finite because 'y' is a reference to 'x'. } } struct AggregateWithValue { int y; }; void test_structured_bindings_bad() { int x = 0; AggregateWithValue val { x }; auto &[y] = val; for (; x < 10; ++y) { // 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] } } void test_volatile_cast() { // This is a no-op cast. Clang ignores the qualifier, we should too. for (int i = 0; (volatile int)i < 10;) { // 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] } } void test_volatile_concrete_address(int i, int size) { // No warning. The value behind the volatile concrete address // is beyond our control. It may change at any time. for (; *((volatile int *)0x1234) < size;) { } for (; *((volatile int *)(0x1234 + i)) < size;) { } for (; **((volatile int **)0x1234) < size;) { } volatile int *x = (volatile int *)0x1234; for (; *x < 10;) { } // FIXME: This one should probably also be suppressed. // Whatever the developer is doing here, they can do that again anywhere else // which basically makes it a global. for (; *(int *)0x1234 < size;) { // 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] } } template int some_template_fn() { return 1; } template void test_dependent_condition() { const int error = some_template_fn(); do { } while (false && error == 0); const int val = some_template_fn(); for (; !(val == 0 || true);) { } const int val2 = some_template_fn(); for (; !(val2 == 0 || false);) { // 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] } const int val3 = some_template_fn(); do { // 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] } while (1, (true) && val3 == 1); const int val4 = some_template_fn(); do { } while (1, (false) && val4 == 1); } void test_typeof() { __typeof__({ for (int i = 0; i < 10; ++i) { } 0; }) x; } void test_typeof_infinite() { __typeof__({ for (int i = 0; i < 10;) { } 0; }) x; } void test_typeof_while_infinite() { __typeof__({ int i = 0; while (i < 10) { } 0; }) x; } void test_typeof_dowhile_infinite() { __typeof__({ int i = 0; do { } while (i < 10); 0; }) x; } void test_local_static_recursion() { static int i = 10; int j = 0; i--; while (i >= 0) test_local_static_recursion(); // no warning, recursively decrement i for (; i >= 0;) test_local_static_recursion(); // no warning, recursively decrement i for (; i + j >= 0;) test_local_static_recursion(); // no warning, recursively decrement i for (; i >= 0; i--) ; // no warning, i decrements while (j >= 0) // 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] test_local_static_recursion(); int (*p)(int) = 0; while (i >= 0) // 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] p = 0; while (i >= 0) p(0); // we don't know what p points to so no warning }