In C++, rvalues that need to have their address taken (for example, to be passed to a function by const reference) will be wrapped in a MaterializeTemporaryExpr, which lets CodeGen know to create a temporary region to store this value. However, MaterializeTemporaryExprs are /not/ created when a method is called on an rvalue struct, even though the 'this' pointer needs a valid value. CodeGen works around this by creating a temporary region anyway; now, so does the analyzer. The analyzer also does this when accessing a field of a struct rvalue. This is a little unfortunate, since the rest of the struct will soon be thrown away, but it does make things consistent with the rest of the analyzer. This allows us to bring back the assumption that all known 'this' values are Locs. This is a revised version of r164828-9, reverted in r164876-7. <rdar://problem/12137950> llvm-svn: 166120
156 lines
3.3 KiB
C++
156 lines
3.3 KiB
C++
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=range -verify -Wno-null-dereference %s
|
|
|
|
void clang_analyzer_eval(bool);
|
|
|
|
typedef typeof(sizeof(int)) size_t;
|
|
void malloc (size_t);
|
|
|
|
void f1() {
|
|
int const &i = 3;
|
|
int b = i;
|
|
|
|
int *p = 0;
|
|
|
|
if (b != 3)
|
|
*p = 1; // no-warning
|
|
}
|
|
|
|
char* ptr();
|
|
char& ref();
|
|
|
|
// These next two tests just shouldn't crash.
|
|
char t1 () {
|
|
ref() = 'c';
|
|
return '0';
|
|
}
|
|
|
|
// just a sanity test, the same behavior as t1()
|
|
char t2 () {
|
|
*ptr() = 'c';
|
|
return '0';
|
|
}
|
|
|
|
// Each of the tests below is repeated with pointers as well as references.
|
|
// This is mostly a sanity check, but then again, both should work!
|
|
char t3 () {
|
|
char& r = ref();
|
|
r = 'c'; // no-warning
|
|
if (r) return r;
|
|
return *(char*)0; // no-warning
|
|
}
|
|
|
|
char t4 () {
|
|
char* p = ptr();
|
|
*p = 'c'; // no-warning
|
|
if (*p) return *p;
|
|
return *(char*)0; // no-warning
|
|
}
|
|
|
|
char t5 (char& r) {
|
|
r = 'c'; // no-warning
|
|
if (r) return r;
|
|
return *(char*)0; // no-warning
|
|
}
|
|
|
|
char t6 (char* p) {
|
|
*p = 'c'; // no-warning
|
|
if (*p) return *p;
|
|
return *(char*)0; // no-warning
|
|
}
|
|
|
|
|
|
// PR13440 / <rdar://problem/11977113>
|
|
// Test that the array-to-pointer decay works for array references as well.
|
|
// More generally, when we want an lvalue for a reference field, we still need
|
|
// to do one level of load.
|
|
namespace PR13440 {
|
|
typedef int T[1];
|
|
struct S {
|
|
T &x;
|
|
|
|
int *m() { return x; }
|
|
};
|
|
|
|
struct S2 {
|
|
int (&x)[1];
|
|
|
|
int *m() { return x; }
|
|
};
|
|
|
|
void test() {
|
|
int a[1];
|
|
S s = { a };
|
|
S2 s2 = { a };
|
|
|
|
if (s.x != a) return;
|
|
if (s2.x != a) return;
|
|
|
|
a[0] = 42;
|
|
clang_analyzer_eval(s.x[0] == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(s2.x[0] == 42); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
void testNullReference() {
|
|
int *x = 0;
|
|
int &y = *x; // expected-warning{{Dereference of null pointer}}
|
|
y = 5;
|
|
}
|
|
|
|
void testRetroactiveNullReference(int *x) {
|
|
// According to the C++ standard, there is no such thing as a
|
|
// "null reference". So the 'if' statement ought to be dead code.
|
|
// However, Clang (and other compilers) don't actually check that a pointer
|
|
// value is non-null in the implementation of references, so it is possible
|
|
// to produce a supposed "null reference" at runtime. The analyzer shoeuld
|
|
// still warn when it can prove such errors.
|
|
int &y = *x;
|
|
if (x != 0)
|
|
return;
|
|
y = 5; // expected-warning{{Dereference of null pointer}}
|
|
}
|
|
|
|
void testReferenceAddress(int &x) {
|
|
clang_analyzer_eval(&x != 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(&ref() != 0); // expected-warning{{TRUE}}
|
|
|
|
struct S { int &x; };
|
|
|
|
extern S getS();
|
|
clang_analyzer_eval(&getS().x != 0); // expected-warning{{TRUE}}
|
|
|
|
extern S *getSP();
|
|
clang_analyzer_eval(&getSP()->x != 0); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
void testFunctionPointerReturn(void *opaque) {
|
|
typedef int &(*RefFn)();
|
|
|
|
RefFn getRef = (RefFn)opaque;
|
|
|
|
// Don't crash writing to or reading from this reference.
|
|
int &x = getRef();
|
|
x = 42;
|
|
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
// ------------------------------------
|
|
// False negatives
|
|
// ------------------------------------
|
|
|
|
namespace rdar11212286 {
|
|
class B{};
|
|
|
|
B test() {
|
|
B *x = 0;
|
|
return *x; // should warn here!
|
|
}
|
|
|
|
B &testRef() {
|
|
B *x = 0;
|
|
return *x; // should warn here!
|
|
}
|
|
}
|