blob: 33d94820b0f699103a060f1a2b1d3b10f233cc4b [file] [log] [blame]
// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fexceptions
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++;
}
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++;
}
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--;
}
do {
Limit--;
} while (i < Limit);
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);
}