250 lines
6.3 KiB
C++
250 lines
6.3 KiB
C++
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s
|
||
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s
|
||
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -analyzer-config cfg-temporary-dtors=true %s -DTEMPORARY_DTORS
|
||
|
|
||
|
extern bool clang_analyzer_eval(bool);
|
||
|
|
||
|
struct Trivial {
|
||
|
Trivial(int x) : value(x) {}
|
||
|
int value;
|
||
|
};
|
||
|
|
||
|
struct NonTrivial : public Trivial {
|
||
|
NonTrivial(int x) : Trivial(x) {}
|
||
|
~NonTrivial();
|
||
|
};
|
||
|
|
||
|
|
||
|
Trivial getTrivial() {
|
||
|
return Trivial(42); // no-warning
|
||
|
}
|
||
|
|
||
|
const Trivial &getTrivialRef() {
|
||
|
return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}}
|
||
|
}
|
||
|
|
||
|
|
||
|
NonTrivial getNonTrivial() {
|
||
|
return NonTrivial(42); // no-warning
|
||
|
}
|
||
|
|
||
|
const NonTrivial &getNonTrivialRef() {
|
||
|
return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}}
|
||
|
}
|
||
|
|
||
|
namespace rdar13265460 {
|
||
|
struct TrivialSubclass : public Trivial {
|
||
|
TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {}
|
||
|
int anotherValue;
|
||
|
};
|
||
|
|
||
|
TrivialSubclass getTrivialSub() {
|
||
|
TrivialSubclass obj(1);
|
||
|
obj.value = 42;
|
||
|
obj.anotherValue = -42;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
void testImmediate() {
|
||
|
TrivialSubclass obj = getTrivialSub();
|
||
|
|
||
|
clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}}
|
||
|
clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}}
|
||
|
|
||
|
clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}}
|
||
|
clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}}
|
||
|
}
|
||
|
|
||
|
void testMaterializeTemporaryExpr() {
|
||
|
const TrivialSubclass &ref = getTrivialSub();
|
||
|
clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
const Trivial &baseRef = getTrivialSub();
|
||
|
clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace rdar13281951 {
|
||
|
struct Derived : public Trivial {
|
||
|
Derived(int value) : Trivial(value), value2(-value) {}
|
||
|
int value2;
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
Derived obj(1);
|
||
|
obj.value = 42;
|
||
|
const Trivial * const &pointerRef = &obj;
|
||
|
clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace compound_literals {
|
||
|
struct POD {
|
||
|
int x, y;
|
||
|
};
|
||
|
struct HasCtor {
|
||
|
HasCtor(int x, int y) : x(x), y(y) {}
|
||
|
int x, y;
|
||
|
};
|
||
|
struct HasDtor {
|
||
|
int x, y;
|
||
|
~HasDtor();
|
||
|
};
|
||
|
struct HasCtorDtor {
|
||
|
HasCtorDtor(int x, int y) : x(x), y(y) {}
|
||
|
~HasCtorDtor();
|
||
|
int x, y;
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}}
|
||
|
clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
#if __cplusplus >= 201103L
|
||
|
clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
// FIXME: should be TRUE, but we don't inline the constructors of
|
||
|
// temporaries because we can't model their destructors yet.
|
||
|
clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace destructors {
|
||
|
void testPR16664Crash() {
|
||
|
struct Dtor {
|
||
|
~Dtor();
|
||
|
};
|
||
|
extern bool coin();
|
||
|
extern bool check(const Dtor &);
|
||
|
|
||
|
// Don't crash here.
|
||
|
if (coin() && (coin() || coin() || check(Dtor()))) {
|
||
|
Dtor();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef TEMPORARY_DTORS
|
||
|
struct NoReturnDtor {
|
||
|
~NoReturnDtor() __attribute__((noreturn));
|
||
|
};
|
||
|
|
||
|
void noReturnTemp(int *x) {
|
||
|
if (! x) NoReturnDtor();
|
||
|
*x = 47; // no warning
|
||
|
}
|
||
|
|
||
|
void noReturnInline(int **x) {
|
||
|
NoReturnDtor();
|
||
|
}
|
||
|
|
||
|
void callNoReturn() {
|
||
|
int *x;
|
||
|
noReturnInline(&x);
|
||
|
*x = 47; // no warning
|
||
|
}
|
||
|
|
||
|
extern bool check(const NoReturnDtor &);
|
||
|
|
||
|
void testConsistencyIf(int i) {
|
||
|
if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
|
||
|
clang_analyzer_eval(true); // expected-warning{{TRUE}}
|
||
|
|
||
|
if (i != 5)
|
||
|
return;
|
||
|
if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) {
|
||
|
clang_analyzer_eval(true); // no warning, unreachable code
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testConsistencyTernary(int i) {
|
||
|
(i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;
|
||
|
|
||
|
clang_analyzer_eval(true); // expected-warning{{TRUE}}
|
||
|
|
||
|
if (i != 5)
|
||
|
return;
|
||
|
|
||
|
(i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;
|
||
|
|
||
|
clang_analyzer_eval(true); // no warning, unreachable code
|
||
|
}
|
||
|
|
||
|
void testConsistencyNested(int i) {
|
||
|
extern bool compute(bool);
|
||
|
|
||
|
if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
|
||
|
clang_analyzer_eval(true); // expected-warning{{TRUE}}
|
||
|
|
||
|
if (i != 5)
|
||
|
return;
|
||
|
|
||
|
if (compute(i == 5 &&
|
||
|
(i == 4 || compute(true) ||
|
||
|
compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
|
||
|
i != 4) {
|
||
|
clang_analyzer_eval(true); // expected-warning{{TRUE}}
|
||
|
}
|
||
|
|
||
|
if (compute(i == 5 &&
|
||
|
(i == 4 || i == 4 ||
|
||
|
compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
|
||
|
i != 4) {
|
||
|
clang_analyzer_eval(true); // no warning, unreachable code
|
||
|
}
|
||
|
}
|
||
|
#endif // TEMPORARY_DTORS
|
||
|
}
|
||
|
|
||
|
void testStaticMaterializeTemporaryExpr() {
|
||
|
static const Trivial &ref = getTrivial();
|
||
|
clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
static const Trivial &directRef = Trivial(42);
|
||
|
clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
#if __has_feature(cxx_thread_local)
|
||
|
thread_local static const Trivial &threadRef = getTrivial();
|
||
|
clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}}
|
||
|
|
||
|
thread_local static const Trivial &threadDirectRef = Trivial(42);
|
||
|
clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
namespace PR16629 {
|
||
|
struct A {
|
||
|
explicit A(int* p_) : p(p_) {}
|
||
|
int* p;
|
||
|
};
|
||
|
|
||
|
extern void escape(const A*[]);
|
||
|
extern void check(int);
|
||
|
|
||
|
void callEscape(const A& a) {
|
||
|
const A* args[] = { &a };
|
||
|
escape(args);
|
||
|
}
|
||
|
|
||
|
void testNoWarning() {
|
||
|
int x;
|
||
|
callEscape(A(&x));
|
||
|
check(x); // Analyzer used to give a "x is uninitialized warning" here
|
||
|
}
|
||
|
|
||
|
void set(const A*a[]) {
|
||
|
*a[0]->p = 47;
|
||
|
}
|
||
|
|
||
|
void callSet(const A& a) {
|
||
|
const A* args[] = { &a };
|
||
|
set(args);
|
||
|
}
|
||
|
|
||
|
void testConsistency() {
|
||
|
int x;
|
||
|
callSet(A(&x));
|
||
|
clang_analyzer_eval(x == 47); // expected-warning{{TRUE}}
|
||
|
}
|
||
|
}
|