// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config unroll-loops=true,cfg-loopexit=true -verify -std=c++11 %s

void clang_analyzer_numTimesReached();
void clang_analyzer_warnIfReached();

int getNum();
void foo(int &);

int simple_unroll1() {
  int a[9];
  int k = 42;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{9}}
    a[i] = 42;
  }
  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int simple_unroll2() {
  int a[9];
  int k = 42;
  int i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{9}}
    a[i] = 42;
  }

  for (int j = 0; j <= 9; ++j) {
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
    a[j] = 42;
  }

  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int simple_no_unroll1() {
  int a[9];
  int k = 42;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
    foo(i);
  }
  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int simple_no_unroll2() {
  int a[9];
  int k = 42;
  int i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
    i += getNum();
  }
  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int simple_no_unroll3() {
  int a[9];
  int k = 42;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
    (void)&i;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int simple_no_unroll4() {
  int a[9];
  int k = 42;
  int i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
    int &j = i;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int simple_no_unroll5() {
  int a[9];
  int k = 42;
  int i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
    int &j{i};
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int make_new_branches_loop_cached() {
  for (int i = 0; i < 8; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    if(getNum()){
        (void) i; // Since this Stmt does not change the State the analyzer
                  // won't make a new execution path but reuse the earlier nodes.
      }
  }
  clang_analyzer_warnIfReached(); // no-warning
  return 0;
}

int make_new_branches_loop_uncached() {
  int l = 2;
  for (int i = 0; i < 8; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
    if(getNum()){
      ++l;
    }
  }
  clang_analyzer_warnIfReached(); // no-warning
  return 0;
}

int make_new_branches_loop_uncached2() {
  int l = 2;
  for (int i = 0; i < 8; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
    if(getNum()){
      ++l;
    }
    (void)&i; // This ensures that the loop won't be unrolled.
  }
  clang_analyzer_warnIfReached(); // no-warning
  return 0;
}


int escape_before_loop_no_unroll1() {
  int a[9];
  int k = 42;
  int i;
  int &j = i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int escape_before_loop_no_unroll2() {
  int a[9];
  int k = 42;
  int i;
  int *p = &i;
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int escape_before_loop_no_unroll3() {
  int a[9];
  int k = 42;
  int i;
  foo(i);
  for (i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    a[i] = 42;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int nested_outer_unrolled() {
  int a[9];
  int k = 42;
  int j = 0;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{1}}
    for (j = 0; j < 9; ++j) {
      clang_analyzer_numTimesReached(); // expected-warning {{4}}
      a[j] = 22;
      (void) &j; // ensures that the inner loop won't be unrolled
    }
    a[i] = 42;
  }
  int b = 22 / (k - 42); // no-warning
  return 0;
}

int nested_inner_unrolled() {
  int a[9];
  int k = 42;
  int j = 0;
  for (int i = 0; i < getNum(); i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    for (j = 0; j < 8; ++j) {
      clang_analyzer_numTimesReached(); // expected-warning {{32}}
      a[j] = 22;
    }
    a[i] = 42;
  }
  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int nested_both_unrolled() {
  int a[9];
  int k = 42;
  int j = 0;
  for (int i = 0; i < 7; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{7}}
    for (j = 0; j < 6; ++j) {
      clang_analyzer_numTimesReached(); // expected-warning {{42}}
      a[j] = 22;
    }
    a[i] = 42;
  }
  int b = 22 / (k - 42); // expected-warning {{Division by zero}}
  return 0;
}

int simple_known_bound_loop() {
  for (int i = 2; i < 12; i++) {
    // This function is inlined in nested_inlined_unroll1()
    clang_analyzer_numTimesReached(); // expected-warning {{90}}
  }
  return 0;
}

int simple_unknown_bound_loop() {
  for (int i = 2; i < getNum(); i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
  }
  return 0;
}

int nested_inlined_unroll1() {
  int k;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{9}}
    k = simple_known_bound_loop();    // no reevaluation without inlining
  }
  int a = 22 / k; // expected-warning {{Division by zero}}
  return 0;
}

int nested_inlined_no_unroll1() {
  int k;
  for (int i = 0; i < 9; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{15}}
    k = simple_unknown_bound_loop();  // reevaluation without inlining, splits the state as well
  }
  int a = 22 / k; // no-warning
  return 0;
}

int recursion_unroll1(bool b) {
  int k = 2;
  for (int i = 0; i < 5; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{13}}
    if(i == 0 && b) // Splits the state in the first iteration but the recursion
                    // call will be unrolled anyway since the condition is known there.
      recursion_unroll1(false);
    clang_analyzer_numTimesReached(); // expected-warning {{14}}
  }
  int a = 22 / k; // no-warning
  return 0;
}

int recursion_unroll2(bool b) {
  int k = 0;
  for (int i = 0; i < 5; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{9}}
    if(i == 0 && b)
      recursion_unroll2(false);
    clang_analyzer_numTimesReached(); // expected-warning {{9}}
  }
  int a = 22 / k; // expected-warning {{Division by zero}}
  return 0;
}

int recursion_unroll3(bool b) {
  int k = 2;
  for (int i = 0; i < 5; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
    if (i == 4 && b) {
      recursion_unroll3(false);
      break;
    }
    clang_analyzer_numTimesReached(); // expected-warning {{10}}
  }
  int a = 22 / k;
  return 0;
}

int recursion_unroll4(bool b) {
  int k = 2;
  for (int i = 0; i < 5; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{13}}
    if(i == 0 && b) {
      recursion_unroll4(false);
      continue;
    }
    clang_analyzer_numTimesReached(); // expected-warning {{13}}
  }
  int a = 22 / k;
  return 0;
}

int loop_exit_while_empty_loop_stack() {
  if (getNum())
    for (int i = 1; i < 8; i++)
      ;
  return 0;
}

int num_steps_on_limit() {
  for (int i = 0; i < 128; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{128}}
  }
  clang_analyzer_numTimesReached(); // expected-warning {{1}}
  return 0;
}

int num_steps_over_limit1() {
  for (int i = 0; i < 129; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
  }
  return 0;
}

int num_steps_on_limit2() {
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 64; j++) {
      clang_analyzer_numTimesReached(); // expected-warning {{128}}
    }
  }
  return 0;
}

int num_steps_over_limit2() {
  for (int i = 0; i < 2; i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{1}}
    for (int j = 0; j <= 64; j++) {
      clang_analyzer_numTimesReached(); // expected-warning {{4}}
    }
  }
  return 0;
}

int num_steps_on_limit3() {
  for (int i = 0; i < getNum(); i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{4}}
    for (int j = 0; j < 32; j++) {
      clang_analyzer_numTimesReached(); // expected-warning {{128}}
    }
  }
  return 0;
}

int num_steps_over_limit3() {
  for (int i = 0; i < getNum(); i++) {
    clang_analyzer_numTimesReached(); // expected-warning {{1}}
    for (int j = 0; j < 33; j++) {
      clang_analyzer_numTimesReached(); // expected-warning {{4}}
    }
  }
  return 0;
}

