Not only global variables can hold references to dead stack variables.
Consider this example:
void write_stack_address_to(char **q) {
char local;
*q = &local;
}
void test_stack() {
char *p;
write_stack_address_to(&p);
}
The address of 'local' is assigned to 'p', which becomes a dangling
pointer after 'write_stack_address_to()' returns.
The StackAddrEscapeChecker was looking for bindings in the store which
referred to variables of the popped stack frame, but it only considered
global variables in this regard. This patch relaxes this, catching
stack variable bindings as well.
---
This patch also works for temporary objects like:
struct Bar {
const int &ref;
explicit Bar(int y) : ref(y) {
// Okay.
} // End of the constructor call, `ref` is dangling now. Warning!
};
void test() {
Bar{33}; // Temporary object, so the corresponding memregion is
// *not* a VarRegion.
}
---
The return value optimization aka. copy-elision might kick in but that
is modeled by passing an imaginary CXXThisRegion which refers to the
parent stack frame which is supposed to be the 'return slot'.
Objects residing in the 'return slot' outlive the scope of the inner
call, thus we should expect no warning about them - except if we
explicitly disable copy-elision.
Reviewed By: NoQ, martong
Differential Revision: https://reviews.llvm.org/D107078
950 lines
24 KiB
C++
950 lines
24 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.UninitializedObject \
|
|
// RUN: -analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true -DPEDANTIC \
|
|
// RUN: -analyzer-config optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true \
|
|
// RUN: -std=c++11 -verify %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.UninitializedObject \
|
|
// RUN: -analyzer-config optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true \
|
|
// RUN: -std=c++11 -verify %s
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Concrete location tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct ConcreteIntLocTest {
|
|
int *ptr;
|
|
|
|
ConcreteIntLocTest() : ptr(reinterpret_cast<int *>(0xDEADBEEF)) {}
|
|
};
|
|
|
|
void fConcreteIntLocTest() {
|
|
ConcreteIntLocTest();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// nonloc::LocAsInteger tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
using intptr_t = unsigned long long;
|
|
|
|
struct LocAsIntegerTest {
|
|
intptr_t ptr; // expected-note{{uninitialized pointee 'reinterpret_cast<char *>(this->ptr)'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
LocAsIntegerTest(void *ptr) : ptr(reinterpret_cast<intptr_t>(ptr)) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fLocAsIntegerTest() {
|
|
char c;
|
|
LocAsIntegerTest t(&c);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Null pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class NullPtrTest {
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
float *fptr = nullptr;
|
|
int *ptr;
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
NullPtrTest() : ptr(nullptr), recPtr(nullptr) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fNullPtrTest() {
|
|
NullPtrTest();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Alloca tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct UntypedAllocaTest {
|
|
void *allocaPtr;
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
// expected-warning-re@+3 {{Address of stack memory allocated by call to \
|
|
alloca() on line {{[0-9]+}} is still referred to by a temporary object on the \
|
|
stack upon returning to the caller. This will be a dangling reference}}
|
|
UntypedAllocaTest() : allocaPtr(__builtin_alloca(sizeof(int))) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fUntypedAllocaTest() {
|
|
UntypedAllocaTest();
|
|
}
|
|
|
|
struct TypedAllocaTest1 {
|
|
int *allocaPtr; // expected-note{{uninitialized pointee 'this->allocaPtr'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
TypedAllocaTest1() // expected-warning{{1 uninitialized field}}
|
|
: allocaPtr(static_cast<int *>(__builtin_alloca(sizeof(int)))) {}
|
|
// expected-warning-re@-2 {{Address of stack memory allocated by call to \
|
|
alloca() on line {{[0-9]+}} is still referred to by a temporary object on the \
|
|
stack upon returning to the caller. This will be a dangling reference}}
|
|
};
|
|
|
|
void fTypedAllocaTest1() {
|
|
TypedAllocaTest1();
|
|
}
|
|
|
|
struct TypedAllocaTest2 {
|
|
int *allocaPtr;
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
// expected-warning-re@+5 {{Address of stack memory allocated by call to \
|
|
alloca() on line {{[0-9]+}} is still referred to by a temporary object on the \
|
|
stack upon returning to the caller. This will be a dangling reference}}
|
|
TypedAllocaTest2()
|
|
: allocaPtr(static_cast<int *>(__builtin_alloca(sizeof(int)))) {
|
|
*allocaPtr = 55555;
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fTypedAllocaTest2() {
|
|
TypedAllocaTest2();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Heap pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class HeapPointerTest1 {
|
|
struct RecordType {
|
|
// TODO: we'd expect the note: {{uninitialized field 'this->recPtr->y'}}
|
|
int x; // no-note
|
|
// TODO: we'd expect the note: {{uninitialized field 'this->recPtr->y'}}
|
|
int y; // no-note
|
|
};
|
|
// TODO: we'd expect the note: {{uninitialized pointee 'this->fptr'}}
|
|
float *fptr = new float; // no-note
|
|
// TODO: we'd expect the note: {{uninitialized pointee 'this->ptr'}}
|
|
int *ptr; // no-note
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
// TODO: we'd expect the warning: {{4 uninitialized fields}}
|
|
HeapPointerTest1() : ptr(new int), recPtr(new RecordType) { // no-note
|
|
}
|
|
};
|
|
|
|
void fHeapPointerTest1() {
|
|
HeapPointerTest1();
|
|
}
|
|
|
|
class HeapPointerTest2 {
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
float *fptr = new float(); // initializes to 0
|
|
int *ptr;
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
HeapPointerTest2() : ptr(new int{25}), recPtr(new RecordType{26, 27}) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fHeapPointerTest2() {
|
|
HeapPointerTest2();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Stack pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class StackPointerTest1 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
int *ptr;
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
StackPointerTest1(int *_ptr, StackPointerTest1::RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fStackPointerTest1() {
|
|
int ok_a = 28;
|
|
StackPointerTest1::RecordType ok_rec{29, 30};
|
|
StackPointerTest1(&ok_a, &ok_rec); // 'a', 'rec.x', 'rec.y' uninitialized
|
|
}
|
|
|
|
#ifdef PEDANTIC
|
|
class StackPointerTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'this->recPtr->x'}}
|
|
int y; // expected-note{{uninitialized field 'this->recPtr->y'}}
|
|
};
|
|
|
|
private:
|
|
int *ptr; // expected-note{{uninitialized pointee 'this->ptr'}}
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
StackPointerTest2(int *_ptr, RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) { // expected-warning{{3 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fStackPointerTest2() {
|
|
int a;
|
|
StackPointerTest2::RecordType rec;
|
|
StackPointerTest2(&a, &rec); // 'a', 'rec.x', 'rec.y' uninitialized
|
|
}
|
|
#else
|
|
class StackPointerTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
int *ptr;
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
StackPointerTest2(int *_ptr, RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) {
|
|
}
|
|
};
|
|
|
|
void fStackPointerTest2() {
|
|
int a;
|
|
StackPointerTest2::RecordType rec;
|
|
StackPointerTest2(&a, &rec); // 'a', 'rec.x', 'rec.y' uninitialized
|
|
}
|
|
#endif // PEDANTIC
|
|
|
|
class UninitPointerTest {
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
int *ptr; // expected-note{{uninitialized pointer 'this->ptr'}}
|
|
RecordType *recPtr;
|
|
|
|
public:
|
|
UninitPointerTest() : recPtr(new RecordType{13, 13}) { // expected-warning{{1 uninitialized field}}
|
|
}
|
|
};
|
|
|
|
void fUninitPointerTest() {
|
|
UninitPointerTest();
|
|
}
|
|
|
|
struct CharPointerTest {
|
|
const char *str;
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
CharPointerTest() : str("") {}
|
|
};
|
|
|
|
void fCharPointerTest() {
|
|
CharPointerTest();
|
|
}
|
|
|
|
struct VectorSizePointer {
|
|
VectorSizePointer() {} // expected-warning{{1 uninitialized field}}
|
|
__attribute__((__vector_size__(8))) int *x; // expected-note{{uninitialized pointer 'this->x'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
};
|
|
|
|
void __vector_size__PointerTest() {
|
|
VectorSizePointer v;
|
|
}
|
|
|
|
struct VectorSizePointee {
|
|
using MyVectorType = __attribute__((__vector_size__(8))) int;
|
|
MyVectorType *x;
|
|
|
|
VectorSizePointee(decltype(x) x) : x(x) {}
|
|
};
|
|
|
|
void __vector_size__PointeeTest() {
|
|
VectorSizePointee::MyVectorType i;
|
|
// TODO: Report v.x's pointee.
|
|
VectorSizePointee v(&i);
|
|
}
|
|
|
|
struct CyclicPointerTest1 {
|
|
int *ptr; // expected-note{{object references itself 'this->ptr'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
CyclicPointerTest1() : ptr(reinterpret_cast<int *>(&ptr)) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fCyclicPointerTest1() {
|
|
CyclicPointerTest1();
|
|
}
|
|
|
|
struct CyclicPointerTest2 {
|
|
int **pptr; // expected-note{{object references itself 'this->pptr'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
CyclicPointerTest2() : pptr(reinterpret_cast<int **>(&pptr)) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fCyclicPointerTest2() {
|
|
CyclicPointerTest2();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Void pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Void pointer tests are mainly no-crash tests.
|
|
|
|
void *malloc(int size);
|
|
|
|
class VoidPointerTest1 {
|
|
void *vptr;
|
|
|
|
public:
|
|
VoidPointerTest1(void *vptr, char) : vptr(vptr) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fVoidPointerTest1() {
|
|
void *vptr = malloc(sizeof(int));
|
|
VoidPointerTest1(vptr, char());
|
|
}
|
|
|
|
class VoidPointerTest2 {
|
|
void **vpptr;
|
|
|
|
public:
|
|
VoidPointerTest2(void **vpptr, char) : vpptr(vpptr) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fVoidPointerTest2() {
|
|
void *vptr = malloc(sizeof(int));
|
|
VoidPointerTest2(&vptr, char());
|
|
}
|
|
|
|
class VoidPointerRRefTest1 {
|
|
void *&&vptrrref; // expected-note {{here}}
|
|
|
|
public:
|
|
// expected-warning@+3 {{Address of stack memory associated with local \
|
|
variable 'vptr' is still referred to by a temporary object on the stack \
|
|
upon returning to the caller. This will be a dangling reference}}
|
|
VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fVoidPointerRRefTest1() {
|
|
void *vptr = malloc(sizeof(int));
|
|
VoidPointerRRefTest1(vptr, char());
|
|
}
|
|
|
|
class VoidPointerRRefTest2 {
|
|
void **&&vpptrrref; // expected-note {{here}}
|
|
|
|
public:
|
|
// expected-warning@+3 {{Address of stack memory associated with local \
|
|
variable 'vptr' is still referred to by a temporary object on the stack \
|
|
upon returning to the caller. This will be a dangling reference}}
|
|
VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) { // expected-warning {{binding reference member 'vpptrrref' to stack allocated parameter 'vptr'}}
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fVoidPointerRRefTest2() {
|
|
void *vptr = malloc(sizeof(int));
|
|
VoidPointerRRefTest2(&vptr, char());
|
|
}
|
|
|
|
class VoidPointerLRefTest {
|
|
void *&vptrrref; // expected-note {{here}}
|
|
|
|
public:
|
|
// expected-warning@+3 {{Address of stack memory associated with local \
|
|
variable 'vptr' is still referred to by a temporary object on the stack \
|
|
upon returning to the caller. This will be a dangling reference}}
|
|
VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fVoidPointerLRefTest() {
|
|
void *vptr = malloc(sizeof(int));
|
|
VoidPointerLRefTest(vptr, char());
|
|
}
|
|
|
|
struct CyclicVoidPointerTest {
|
|
void *vptr; // expected-note{{object references itself 'this->vptr'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
CyclicVoidPointerTest() : vptr(&vptr) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fCyclicVoidPointerTest() {
|
|
CyclicVoidPointerTest();
|
|
}
|
|
|
|
struct IntDynTypedVoidPointerTest1 {
|
|
void *vptr; // expected-note{{uninitialized pointee 'static_cast<int *>(this->vptr)'}}
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
IntDynTypedVoidPointerTest1(void *vptr) : vptr(vptr) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fIntDynTypedVoidPointerTest1() {
|
|
int a;
|
|
IntDynTypedVoidPointerTest1 tmp(&a);
|
|
}
|
|
|
|
struct RecordDynTypedVoidPointerTest {
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'static_cast<struct RecordDynTypedVoidPointerTest::RecordType *>(this->vptr)->x'}}
|
|
int y; // expected-note{{uninitialized field 'static_cast<struct RecordDynTypedVoidPointerTest::RecordType *>(this->vptr)->y'}}
|
|
};
|
|
|
|
void *vptr;
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
RecordDynTypedVoidPointerTest(void *vptr) : vptr(vptr) {} // expected-warning{{2 uninitialized fields}}
|
|
};
|
|
|
|
void fRecordDynTypedVoidPointerTest() {
|
|
RecordDynTypedVoidPointerTest::RecordType a;
|
|
RecordDynTypedVoidPointerTest tmp(&a);
|
|
}
|
|
|
|
struct NestedNonVoidDynTypedVoidPointerTest {
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(this->vptr)->x'}}
|
|
int y; // expected-note{{uninitialized field 'static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(this->vptr)->y'}}
|
|
void *vptr; // expected-note{{uninitialized pointee 'static_cast<char *>(static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(this->vptr)->vptr)'}}
|
|
};
|
|
|
|
void *vptr;
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
NestedNonVoidDynTypedVoidPointerTest(void *vptr, void *c) : vptr(vptr) {
|
|
static_cast<RecordType *>(vptr)->vptr = c; // expected-warning{{3 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fNestedNonVoidDynTypedVoidPointerTest() {
|
|
NestedNonVoidDynTypedVoidPointerTest::RecordType a;
|
|
char c;
|
|
NestedNonVoidDynTypedVoidPointerTest tmp(&a, &c);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Multipointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifdef PEDANTIC
|
|
class MultiPointerTest1 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType **mptr; // expected-note{{uninitialized pointee 'this->mptr'}}
|
|
|
|
public:
|
|
MultiPointerTest1(RecordType **p, int) : mptr(p) { // expected-warning{{1 uninitialized field}}
|
|
}
|
|
};
|
|
|
|
void fMultiPointerTest1() {
|
|
MultiPointerTest1::RecordType *p1;
|
|
MultiPointerTest1::RecordType **mptr = &p1;
|
|
MultiPointerTest1(mptr, int()); // '*mptr' uninitialized
|
|
}
|
|
#else
|
|
class MultiPointerTest1 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType **mptr;
|
|
|
|
public:
|
|
MultiPointerTest1(RecordType **p, int) : mptr(p) {}
|
|
};
|
|
|
|
void fMultiPointerTest1() {
|
|
MultiPointerTest1::RecordType *p1;
|
|
MultiPointerTest1::RecordType **mptr = &p1;
|
|
MultiPointerTest1(mptr, int()); // '*mptr' uninitialized
|
|
}
|
|
#endif // PEDANTIC
|
|
|
|
#ifdef PEDANTIC
|
|
class MultiPointerTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'this->mptr->x'}}
|
|
int y; // expected-note{{uninitialized field 'this->mptr->y'}}
|
|
};
|
|
|
|
private:
|
|
RecordType **mptr;
|
|
|
|
public:
|
|
MultiPointerTest2(RecordType **p, int) : mptr(p) { // expected-warning{{2 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fMultiPointerTest2() {
|
|
MultiPointerTest2::RecordType i;
|
|
MultiPointerTest2::RecordType *p1 = &i;
|
|
MultiPointerTest2::RecordType **mptr = &p1;
|
|
MultiPointerTest2(mptr, int()); // '**mptr' uninitialized
|
|
}
|
|
#else
|
|
class MultiPointerTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType **mptr;
|
|
|
|
public:
|
|
MultiPointerTest2(RecordType **p, int) : mptr(p) {
|
|
}
|
|
};
|
|
|
|
void fMultiPointerTest2() {
|
|
MultiPointerTest2::RecordType i;
|
|
MultiPointerTest2::RecordType *p1 = &i;
|
|
MultiPointerTest2::RecordType **mptr = &p1;
|
|
MultiPointerTest2(mptr, int()); // '**mptr' uninitialized
|
|
}
|
|
#endif // PEDANTIC
|
|
|
|
class MultiPointerTest3 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType **mptr;
|
|
|
|
public:
|
|
MultiPointerTest3(RecordType **p, int) : mptr(p) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fMultiPointerTest3() {
|
|
MultiPointerTest3::RecordType i{31, 32};
|
|
MultiPointerTest3::RecordType *p1 = &i;
|
|
MultiPointerTest3::RecordType **mptr = &p1;
|
|
MultiPointerTest3(mptr, int()); // '**mptr' uninitialized
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Incomplete pointee tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class IncompleteType;
|
|
|
|
struct IncompletePointeeTypeTest {
|
|
IncompleteType *pImpl; //no-crash
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
IncompletePointeeTypeTest(IncompleteType *A) : pImpl(A) {}
|
|
};
|
|
|
|
void fIncompletePointeeTypeTest(void *ptr) {
|
|
IncompletePointeeTypeTest(reinterpret_cast<IncompleteType *>(ptr));
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Function pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct FunctionPointerWithDifferentDynTypeTest {
|
|
using Func1 = void *(*)();
|
|
using Func2 = int *(*)();
|
|
|
|
Func1 f; // no-crash
|
|
FunctionPointerWithDifferentDynTypeTest(Func2 f) : f((Func1)f) {}
|
|
};
|
|
|
|
// Note that there isn't a function calling the constructor of
|
|
// FunctionPointerWithDifferentDynTypeTest, because a crash could only be
|
|
// reproduced without it.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Member pointer tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct UsefulFunctions {
|
|
int a, b;
|
|
|
|
void print() {}
|
|
void dump() {}
|
|
};
|
|
|
|
#ifdef PEDANTIC
|
|
struct PointerToMemberFunctionTest1 {
|
|
void (UsefulFunctions::*f)(void); // expected-note{{uninitialized field 'this->f'}}
|
|
PointerToMemberFunctionTest1() {}
|
|
};
|
|
|
|
void fPointerToMemberFunctionTest1() {
|
|
PointerToMemberFunctionTest1(); // expected-warning{{1 uninitialized field}}
|
|
}
|
|
|
|
struct PointerToMemberFunctionTest2 {
|
|
void (UsefulFunctions::*f)(void);
|
|
PointerToMemberFunctionTest2(void (UsefulFunctions::*f)(void)) : f(f) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fPointerToMemberFunctionTest2() {
|
|
void (UsefulFunctions::*f)(void) = &UsefulFunctions::print;
|
|
PointerToMemberFunctionTest2 a(f);
|
|
}
|
|
|
|
struct MultiPointerToMemberFunctionTest1 {
|
|
void (UsefulFunctions::**f)(void); // expected-note{{uninitialized pointer 'this->f'}}
|
|
MultiPointerToMemberFunctionTest1() {}
|
|
};
|
|
|
|
void fMultiPointerToMemberFunctionTest1() {
|
|
MultiPointerToMemberFunctionTest1(); // expected-warning{{1 uninitialized field}}
|
|
}
|
|
|
|
struct MultiPointerToMemberFunctionTest2 {
|
|
void (UsefulFunctions::**f)(void);
|
|
MultiPointerToMemberFunctionTest2(void (UsefulFunctions::**f)(void)) : f(f) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fMultiPointerToMemberFunctionTest2() {
|
|
void (UsefulFunctions::*f)(void) = &UsefulFunctions::print;
|
|
MultiPointerToMemberFunctionTest2 a(&f);
|
|
}
|
|
|
|
struct PointerToMemberDataTest1 {
|
|
int UsefulFunctions::*d; // expected-note{{uninitialized field 'this->d'}}
|
|
PointerToMemberDataTest1() {}
|
|
};
|
|
|
|
void fPointerToMemberDataTest1() {
|
|
PointerToMemberDataTest1(); // expected-warning{{1 uninitialized field}}
|
|
}
|
|
|
|
struct PointerToMemberDataTest2 {
|
|
int UsefulFunctions::*d;
|
|
PointerToMemberDataTest2(int UsefulFunctions::*d) : d(d) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fPointerToMemberDataTest2() {
|
|
int UsefulFunctions::*d = &UsefulFunctions::a;
|
|
PointerToMemberDataTest2 a(d);
|
|
}
|
|
|
|
struct MultiPointerToMemberDataTest1 {
|
|
int UsefulFunctions::**d; // expected-note{{uninitialized pointer 'this->d'}}
|
|
MultiPointerToMemberDataTest1() {}
|
|
};
|
|
|
|
void fMultiPointerToMemberDataTest1() {
|
|
MultiPointerToMemberDataTest1(); // expected-warning{{1 uninitialized field}}
|
|
}
|
|
|
|
struct MultiPointerToMemberDataTest2 {
|
|
int UsefulFunctions::**d;
|
|
MultiPointerToMemberDataTest2(int UsefulFunctions::**d) : d(d) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fMultiPointerToMemberDataTest2() {
|
|
int UsefulFunctions::*d = &UsefulFunctions::a;
|
|
MultiPointerToMemberDataTest2 a(&d);
|
|
}
|
|
#endif // PEDANTIC
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Tests for list-like records.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class ListTest1 {
|
|
public:
|
|
struct Node {
|
|
Node *next = nullptr; // no crash
|
|
int i;
|
|
};
|
|
|
|
private:
|
|
Node *head = nullptr;
|
|
|
|
public:
|
|
ListTest1() {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fListTest1() {
|
|
ListTest1();
|
|
}
|
|
|
|
class ListTest2 {
|
|
public:
|
|
struct Node {
|
|
Node *next = nullptr;
|
|
int i; // expected-note{{uninitialized field 'this->head->i'}}
|
|
};
|
|
|
|
private:
|
|
Node *head = nullptr;
|
|
|
|
public:
|
|
ListTest2(Node *node, int) : head(node) { // expected-warning{{1 uninitialized field}}
|
|
}
|
|
};
|
|
|
|
void fListTest2() {
|
|
ListTest2::Node n;
|
|
ListTest2(&n, int());
|
|
}
|
|
|
|
class CyclicList {
|
|
public:
|
|
struct Node {
|
|
Node *next = nullptr;
|
|
int i; // expected-note{{uninitialized field 'this->head->i'}}
|
|
};
|
|
|
|
private:
|
|
Node *head = nullptr;
|
|
|
|
public:
|
|
CyclicList(Node *node, int) : head(node) { // expected-warning{{1 uninitialized field}}
|
|
}
|
|
};
|
|
|
|
void fCyclicList() {
|
|
/*
|
|
n3
|
|
/ \
|
|
this -- n1 -- n2
|
|
*/
|
|
|
|
CyclicList::Node n1;
|
|
CyclicList::Node n2;
|
|
n2.next = &n1;
|
|
n2.i = 50;
|
|
CyclicList::Node n3;
|
|
n3.next = &n2;
|
|
n3.i = 50;
|
|
n1.next = &n3;
|
|
// note that n1.i is uninitialized
|
|
CyclicList(&n1, int());
|
|
}
|
|
|
|
struct RingListTest {
|
|
RingListTest *next; // no-crash
|
|
RingListTest() : next(this) {}
|
|
};
|
|
|
|
void fRingListTest() {
|
|
RingListTest();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Tests for classes containing references.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class ReferenceTest1 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType &lref;
|
|
RecordType &&rref;
|
|
|
|
public:
|
|
ReferenceTest1(RecordType &lref, RecordType &rref) : lref(lref), rref(static_cast<RecordType &&>(rref)) {
|
|
// All good!
|
|
}
|
|
};
|
|
|
|
void fReferenceTest1() {
|
|
ReferenceTest1::RecordType d{33, 34};
|
|
ReferenceTest1(d, d);
|
|
}
|
|
|
|
#ifdef PEDANTIC
|
|
class ReferenceTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'this->lref.x'}}
|
|
int y; // expected-note{{uninitialized field 'this->lref.y'}}
|
|
};
|
|
|
|
private:
|
|
RecordType &lref;
|
|
RecordType &&rref;
|
|
|
|
public:
|
|
ReferenceTest2(RecordType &lref, RecordType &rref)
|
|
: lref(lref), rref(static_cast<RecordType &&>(rref)) { // expected-warning{{2 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fReferenceTest2() {
|
|
ReferenceTest2::RecordType c;
|
|
ReferenceTest2(c, c);
|
|
}
|
|
#else
|
|
class ReferenceTest2 {
|
|
public:
|
|
struct RecordType {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
private:
|
|
RecordType &lref;
|
|
RecordType &&rref;
|
|
|
|
public:
|
|
ReferenceTest2(RecordType &lref, RecordType &rref)
|
|
: lref(lref), rref(static_cast<RecordType &&>(rref)) {
|
|
}
|
|
};
|
|
|
|
void fReferenceTest2() {
|
|
ReferenceTest2::RecordType c;
|
|
ReferenceTest2(c, c);
|
|
}
|
|
#endif // PEDANTIC
|
|
|
|
class ReferenceTest3 {
|
|
public:
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'this->lref.x'}}
|
|
int y; // expected-note{{uninitialized field 'this->lref.y'}}
|
|
};
|
|
|
|
private:
|
|
RecordType &lref;
|
|
RecordType &&rref;
|
|
|
|
public:
|
|
ReferenceTest3(RecordType &lref, RecordType &rref)
|
|
: lref(lref), rref(static_cast<RecordType &&>(rref)) { // expected-warning{{2 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fReferenceTest3() {
|
|
ReferenceTest3::RecordType c, d{35, 36};
|
|
ReferenceTest3(c, d);
|
|
}
|
|
|
|
class ReferenceTest4 {
|
|
public:
|
|
struct RecordType {
|
|
int x; // expected-note{{uninitialized field 'this->rref.x'}}
|
|
int y; // expected-note{{uninitialized field 'this->rref.y'}}
|
|
};
|
|
|
|
private:
|
|
RecordType &lref;
|
|
RecordType &&rref;
|
|
|
|
public:
|
|
ReferenceTest4(RecordType &lref, RecordType &rref)
|
|
: lref(lref), rref(static_cast<RecordType &&>(rref)) { // expected-warning{{2 uninitialized fields}}
|
|
}
|
|
};
|
|
|
|
void fReferenceTest5() {
|
|
ReferenceTest4::RecordType c, d{37, 38};
|
|
ReferenceTest4(d, c);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Tests for objects containing multiple references to the same object.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct IntMultipleReferenceToSameObjectTest {
|
|
int *iptr; // expected-note{{uninitialized pointee 'this->iptr'}}
|
|
int &iref; // no-note, pointee of this->iref was already reported
|
|
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
IntMultipleReferenceToSameObjectTest(int *i) : iptr(i), iref(*i) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
void fIntMultipleReferenceToSameObjectTest() {
|
|
int a;
|
|
IntMultipleReferenceToSameObjectTest Test(&a);
|
|
}
|
|
|
|
struct IntReferenceWrapper1 {
|
|
int &a; // expected-note{{uninitialized pointee 'this->a'}}
|
|
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
IntReferenceWrapper1(int &a) : a(a) {} // expected-warning{{1 uninitialized field}}
|
|
};
|
|
|
|
struct IntReferenceWrapper2 {
|
|
int &a; // no-note, pointee of this->a was already reported
|
|
|
|
int dontGetFilteredByNonPedanticMode = 0;
|
|
|
|
IntReferenceWrapper2(int &a) : a(a) {} // no-warning
|
|
};
|
|
|
|
void fMultipleObjectsReferencingTheSameObjectTest() {
|
|
int a;
|
|
|
|
IntReferenceWrapper1 T1(a);
|
|
IntReferenceWrapper2 T2(a);
|
|
}
|