227 lines
3.9 KiB
C
227 lines
3.9 KiB
C
|
// 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
|
||
|
__builtin_assume(0); // 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
|
||
|
}
|
||
|
|
||
|
// Avoid FP when macro argument is known
|
||
|
void writeSomething(int *x);
|
||
|
#define MACRO(C) \
|
||
|
if (!C) { \
|
||
|
static int x; \
|
||
|
writeSomething(&x); \
|
||
|
}
|
||
|
void macro2(void) {
|
||
|
MACRO(1);
|
||
|
}
|