llvm/clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp

// 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<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]
  }
}

template <typename T>
int some_template_fn() { return 1; }

template <typename T>
void test_dependent_condition() {
  const int error = some_template_fn<T>();
  do {
  } while (false && error == 0);

  const int val = some_template_fn<T>();
  for (; !(val == 0 || true);) {
  }

  const int val2 = some_template_fn<T>();
  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<T>();
  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<T>();
  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
}