forked from OSchip/llvm-project
410 lines
9.1 KiB
C++
410 lines
9.1 KiB
C++
// RUN: %clang_cc1 %s -fcxx-exceptions -fexceptions -fsyntax-only -verify -fblocks -std=c++11 -Wunreachable-code-aggressive -Wno-unused-value -Wno-tautological-compare
|
|
|
|
int &halt() __attribute__((noreturn));
|
|
int &live();
|
|
int dead();
|
|
int liveti() throw(int);
|
|
int (*livetip)() throw(int);
|
|
|
|
int test1() {
|
|
try {
|
|
live();
|
|
} catch (int i) {
|
|
live();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void test2() {
|
|
try {
|
|
live();
|
|
} catch (int i) {
|
|
live();
|
|
}
|
|
try {
|
|
liveti();
|
|
} catch (int i) {
|
|
live();
|
|
}
|
|
try {
|
|
livetip();
|
|
} catch (int i) {
|
|
live();
|
|
}
|
|
throw 1;
|
|
dead(); // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
|
|
void test3() {
|
|
halt()
|
|
--; // expected-warning {{will never be executed}}
|
|
// FIXME: The unreachable part is just the '?', but really all of this
|
|
// code is unreachable and shouldn't be separately reported.
|
|
halt() // expected-warning {{will never be executed}}
|
|
?
|
|
dead() : dead();
|
|
live(),
|
|
float
|
|
(halt()); // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
void test4() {
|
|
struct S {
|
|
int mem;
|
|
} s;
|
|
S &foor();
|
|
halt(), foor()// expected-warning {{will never be executed}}
|
|
.mem;
|
|
}
|
|
|
|
void test5() {
|
|
struct S {
|
|
int mem;
|
|
} s;
|
|
S &foonr() __attribute__((noreturn));
|
|
foonr()
|
|
.mem; // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
void test6() {
|
|
struct S {
|
|
~S() { }
|
|
S(int i) { }
|
|
};
|
|
live(),
|
|
S
|
|
(halt()); // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
// Don't warn about unreachable code in template instantiations, as
|
|
// they may only be unreachable in that specific instantiation.
|
|
void isUnreachable();
|
|
|
|
template <typename T> void test_unreachable_templates() {
|
|
T::foo();
|
|
isUnreachable(); // no-warning
|
|
}
|
|
|
|
struct TestUnreachableA {
|
|
static void foo() __attribute__((noreturn));
|
|
};
|
|
struct TestUnreachableB {
|
|
static void foo();
|
|
};
|
|
|
|
void test_unreachable_templates_harness() {
|
|
test_unreachable_templates<TestUnreachableA>();
|
|
test_unreachable_templates<TestUnreachableB>();
|
|
}
|
|
|
|
// Do warn about explict template specializations, as they represent
|
|
// actual concrete functions that somebody wrote.
|
|
|
|
template <typename T> void funcToSpecialize() {}
|
|
template <> void funcToSpecialize<int>() {
|
|
halt();
|
|
dead(); // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
// Handle 'try' code dominating a dead return.
|
|
enum PR19040_test_return_t
|
|
{ PR19040_TEST_FAILURE };
|
|
namespace PR19040_libtest
|
|
{
|
|
class A {
|
|
public:
|
|
~A ();
|
|
};
|
|
}
|
|
PR19040_test_return_t PR19040_fn1 ()
|
|
{
|
|
try
|
|
{
|
|
throw PR19040_libtest::A ();
|
|
} catch (...)
|
|
{
|
|
return PR19040_TEST_FAILURE;
|
|
}
|
|
return PR19040_TEST_FAILURE; // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
__attribute__((noreturn))
|
|
void raze();
|
|
|
|
namespace std {
|
|
template<typename T> struct basic_string {
|
|
basic_string(const T* x) {}
|
|
~basic_string() {};
|
|
};
|
|
typedef basic_string<char> string;
|
|
}
|
|
|
|
std::string testStr() {
|
|
raze();
|
|
return ""; // expected-warning {{'return' will never be executed}}
|
|
}
|
|
|
|
std::string testStrWarn(const char *s) {
|
|
raze();
|
|
return s; // expected-warning {{will never be executed}}
|
|
}
|
|
|
|
bool testBool() {
|
|
raze();
|
|
return true; // expected-warning {{'return' will never be executed}}
|
|
}
|
|
|
|
static const bool ConditionVar = 1;
|
|
int test_global_as_conditionVariable() {
|
|
if (ConditionVar)
|
|
return 1;
|
|
return 0; // no-warning
|
|
}
|
|
|
|
// Handle unreachable temporary destructors.
|
|
class A {
|
|
public:
|
|
A();
|
|
~A();
|
|
};
|
|
|
|
__attribute__((noreturn))
|
|
void raze(const A& x);
|
|
|
|
void test_with_unreachable_tmp_dtors(int x) {
|
|
raze(x ? A() : A()); // no-warning
|
|
}
|
|
|
|
// Test sizeof - sizeof in enum declaration.
|
|
enum { BrownCow = sizeof(long) - sizeof(char) };
|
|
enum { CowBrown = 8 - 1 };
|
|
|
|
|
|
int test_enum_sizeof_arithmetic() {
|
|
if (BrownCow)
|
|
return 1;
|
|
return 2;
|
|
}
|
|
|
|
int test_enum_arithmetic() {
|
|
if (CowBrown)
|
|
return 1;
|
|
return 2; // expected-warning {{never be executed}}
|
|
}
|
|
|
|
int test_arithmetic() {
|
|
if (8 -1)
|
|
return 1;
|
|
return 2; // expected-warning {{never be executed}}
|
|
}
|
|
|
|
int test_treat_const_bool_local_as_config_value() {
|
|
const bool controlValue = false;
|
|
if (!controlValue)
|
|
return 1;
|
|
test_treat_const_bool_local_as_config_value(); // no-warning
|
|
return 0;
|
|
}
|
|
|
|
int test_treat_non_const_bool_local_as_non_config_value() {
|
|
bool controlValue = false;
|
|
if (!controlValue)
|
|
return 1;
|
|
// There is no warning here because 'controlValue' isn't really
|
|
// a control value at all. The CFG will not treat this
|
|
// branch as unreachable.
|
|
test_treat_non_const_bool_local_as_non_config_value(); // no-warning
|
|
return 0;
|
|
}
|
|
|
|
void test_do_while(int x) {
|
|
// Handle trivial expressions with
|
|
// implicit casts to bool.
|
|
do {
|
|
break;
|
|
} while (0); // no-warning
|
|
}
|
|
|
|
class Frobozz {
|
|
public:
|
|
Frobozz(int x);
|
|
~Frobozz();
|
|
};
|
|
|
|
Frobozz test_return_object(int flag) {
|
|
return Frobozz(flag);
|
|
return Frobozz(42); // expected-warning {{'return' will never be executed}}
|
|
}
|
|
|
|
Frobozz test_return_object_control_flow(int flag) {
|
|
return Frobozz(flag);
|
|
return Frobozz(flag ? 42 : 24); // expected-warning {{code will never be executed}}
|
|
}
|
|
|
|
void somethingToCall();
|
|
|
|
static constexpr bool isConstExprConfigValue() { return true; }
|
|
|
|
int test_const_expr_config_value() {
|
|
if (isConstExprConfigValue()) {
|
|
somethingToCall();
|
|
return 0;
|
|
}
|
|
somethingToCall(); // no-warning
|
|
return 1;
|
|
}
|
|
int test_const_expr_config_value_2() {
|
|
if (!isConstExprConfigValue()) {
|
|
somethingToCall(); // no-warning
|
|
return 0;
|
|
}
|
|
somethingToCall();
|
|
return 1;
|
|
}
|
|
|
|
class Frodo {
|
|
public:
|
|
static const bool aHobbit = true;
|
|
};
|
|
|
|
void test_static_class_var() {
|
|
if (Frodo::aHobbit)
|
|
somethingToCall();
|
|
else
|
|
somethingToCall(); // no-warning
|
|
}
|
|
|
|
void test_static_class_var(Frodo &F) {
|
|
if (F.aHobbit)
|
|
somethingToCall();
|
|
else
|
|
somethingToCall(); // no-warning
|
|
}
|
|
|
|
void test_unreachable_for_null_increment() {
|
|
for (unsigned i = 0; i < 10 ; ) // no-warning
|
|
break;
|
|
}
|
|
|
|
void test_unreachable_forrange_increment() {
|
|
int x[10] = { 0 };
|
|
for (auto i : x) { // expected-warning {{loop will run at most once (loop increment never executed)}}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void calledFun() {}
|
|
|
|
// Test "silencing" with parentheses.
|
|
void test_with_paren_silencing(int x) {
|
|
if (false) calledFun(); // expected-warning {{will never be executed}} expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
if ((false)) calledFun(); // no-warning
|
|
|
|
if (true) // expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if ((true))
|
|
calledFun();
|
|
else
|
|
calledFun(); // no-warning
|
|
|
|
if (!true) // expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
calledFun(); // expected-warning {{code will never be executed}}
|
|
else
|
|
calledFun();
|
|
|
|
if ((!true))
|
|
calledFun(); // no-warning
|
|
else
|
|
calledFun();
|
|
|
|
if (!(true))
|
|
calledFun(); // no-warning
|
|
else
|
|
calledFun();
|
|
}
|
|
|
|
void test_with_paren_silencing_impcast(int x) {
|
|
if (0) calledFun(); // expected-warning {{will never be executed}} expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
if ((0)) calledFun(); // no-warning
|
|
|
|
if (1) // expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if ((1))
|
|
calledFun();
|
|
else
|
|
calledFun(); // no-warning
|
|
|
|
if (!1) // expected-note {{silence by adding parentheses to mark code as explicitly dead}}
|
|
calledFun(); // expected-warning {{code will never be executed}}
|
|
else
|
|
calledFun();
|
|
|
|
if ((!1))
|
|
calledFun(); // no-warning
|
|
else
|
|
calledFun();
|
|
|
|
if (!(1))
|
|
calledFun(); // no-warning
|
|
else
|
|
calledFun();
|
|
}
|
|
|
|
void tautological_compare(bool x, int y) {
|
|
if (x > 10) // expected-note {{silence}}
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
if (10 < x) // expected-note {{silence}}
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
if (x == 10) // expected-note {{silence}}
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if (x < 10) // expected-note {{silence}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
if (10 > x) // expected-note {{silence}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
if (x != 10) // expected-note {{silence}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if (y != 5 && y == 5) // expected-note {{silence}}
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if (y > 5 && y < 4) // expected-note {{silence}}
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
if (y < 10 || y > 5) // expected-note {{silence}}
|
|
calledFun();
|
|
else
|
|
calledFun(); // expected-warning {{will never be executed}}
|
|
|
|
// TODO: Extend warning to the following code:
|
|
if (x < -1)
|
|
calledFun();
|
|
if (x == -1)
|
|
calledFun();
|
|
|
|
if (x != -1)
|
|
calledFun();
|
|
else
|
|
calledFun();
|
|
if (-1 > x)
|
|
calledFun();
|
|
else
|
|
calledFun();
|
|
|
|
if (y == -1 && y != -1)
|
|
calledFun();
|
|
}
|