forked from OSchip/llvm-project
625 lines
13 KiB
C++
625 lines
13 KiB
C++
// RUN: %check_clang_tidy %s bugprone-infinite-loop %t \
|
|
// RUN: -- -- -fexceptions -fblocks
|
|
|
|
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<typename T> class atomic {
|
|
T val;
|
|
public:
|
|
atomic(T v): val(v) {};
|
|
operator T() { return val; };
|
|
};
|
|
}
|
|
|
|
void atomic_in_condition() {
|
|
std::atomic<int> 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 <typename T> 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]
|
|
}
|
|
}
|