// RUN: %clang_analyze_cc1 -analyzer-checker=core,deadcode.DeadStores,alpha.deadcode.UnreachableCode -verify -analyzer-opt-analyze-nested-blocks -Wno-unused-value %s

extern void foo(int a);

// The first few tests are non-path specific - we should be able to find them

void test(unsigned a) {
  switch (a) {
    a += 5; // expected-warning{{never executed}}
  case 2:
    a *= 10;
  case 3:
    a %= 2;
  }
  foo(a);
}

void test2(unsigned a) {
 help:
  if (a > 0)
    return;
  if (a == 0)
    return;
  foo(a); // expected-warning{{never executed}}
  goto help;
}

void test3(unsigned a) {
  while(1);
  if (a > 5) { // expected-warning{{never executed}}
    return;
  }
}

// These next tests are path-sensitive

void test4() {
  int a = 5;

  while (a > 1)
    a -= 2;

  if (a > 1) {
    a = a + 56; // expected-warning{{never executed}}
  }

  foo(a);
}

extern void bar(char c);

void test5(const char *c) {
  foo(c[0]);

  if (!c) {
    bar(1); // expected-warning{{never executed}}
  }
}

// These next tests are false positives and should not generate warnings

void test6(const char *c) {
  if (c) return;
  if (!c) return;
  __builtin_unreachable(); // no-warning
}

// Compile-time constant false positives
#define CONSTANT 0
enum test_enum { Off, On };
void test7() {
  if (CONSTANT)
    return; // no-warning

  if (sizeof(int))
    return; // no-warning

  if (Off)
    return; // no-warning
}

void test8() {
  static unsigned a = 0;

  if (a)
    a = 123; // no-warning

  a = 5;
}

// Check for bugs where multiple statements are reported
void test9(unsigned a) {
  switch (a) {
    if (a) // expected-warning{{never executed}}
      foo(a + 5); // no-warning
    else          // no-warning
      foo(a);     // no-warning
    case 1:
    case 2:
      break;
    default:
      break;
  }
}

// Tests from flow-sensitive version
void test10() {
  goto c;
  d:
  goto e; // expected-warning {{never executed}}
  c: ;
  int i;
  return;
  goto b; // expected-warning {{never executed}}
  goto a; // expected-warning {{never executed}}
  b:
  i = 1; // no-warning
  a:
  i = 2;  // no-warning
  goto f;
  e:
  goto d;
  f: ;
}

// test11: we can actually end up in the default case, even if it is not
// obvious: there might be something wrong with the given argument.
enum foobar { FOO, BAR };
extern void error();
void test11(enum foobar fb) {
  switch (fb) {
    case FOO:
      break;
    case BAR:
      break;
    default:
      error(); // no-warning
      return;
      error(); // expected-warning {{never executed}}
  }
}

void inlined(int condition) {
  if (condition) {
    foo(5); // no-warning
  } else {
    foo(6);
  }
}

void testInlined() {
  extern int coin();
  int cond = coin();
  if (!cond) {
    inlined(0);
    if (cond) {
      foo(5); // expected-warning {{never executed}}
    }
  }
}

// Don't warn about unreachable VarDecl.
void dostuff(int*A);
void varDecl1(int X) {
  switch (X) {
    int A; // No warning here.
  case 1:
    dostuff(&A);
    break;
  case 2:
    dostuff(&A);
    break;
  }
}
void varDecl2(int X) {
  switch (X) {
    int A=1; // expected-warning {{never executed}}
  case 1:
    dostuff(&A);
    break;
  case 2:
    dostuff(&A);
    break;
  }
}

// Ensure that ExplodedGraph and unoptimized CFG match.
void test12(int x) {
  switch (x) {
  case 1:
    break; // not unreachable
  case 2:
    do { } while (0);
    break;
  }
}

// Don't merge return nodes in ExplodedGraph unless they are same.
extern int table[];
static int inlineFunction(const int i) {
  if (table[i] != 0)
    return 1;
  return 0;
}
void test13(int i) {
  int x = inlineFunction(i);
  x && x < 10; // no-warning
}

// Don't warn in a macro
#define RETURN(X)  do { return; } while (0)
void macro(void) {
  RETURN(1); // no-warning
}

