Szelethus updated this revision to Diff 143875. Szelethus added a comment. In this diff I
- added a `Pedantic` flag that is set to false by default to filter out results from objects that don't have a single field initialized, - made it so that fields that are declared in system headers are now ignored, - refactored `isFullyInitialized` to `hasUnintializedFields` (it returned true when there were in fact uninit fields), - fixed everything mentioned in inline comments aside from the naming and the category, - added TODOs for `FieldChainInfo::toString`, I decided to fix those in a later patch to keep the diff just a little bit smaller, - added many more test cases, including tests for the `Pedantic` flag - added support for arrays. Granted, they worked wonderfully with the checker before, but there was nothing mentioned about them in the code. If you like how I implemented the `Pedantic` flag, then I think only the naming and choosing the correct category is left. I also rechecked the entire LLVM/Clang project before the system header fix and after the fix with `Pedantic` set to true and set to false. Here are my findings: How many reports did the checker emit? - Without fields in system headers being ignored: 208 (only functional had some fields uninitialized) - With fields in system headers being ignored and `Pedantic` set to true: 181 - With fields in system headers being ignored and `Pedantic` set to false: 150 Most of these are intentional, as a very large portion of the project is performance critical. I did however find some constructors with the checker that would benefit from having the rest of their fields initialized. I also found some constructors that didn't use `= default` for no good reason. https://reviews.llvm.org/D45532 Files: include/clang/StaticAnalyzer/Checkers/Checkers.td lib/StaticAnalyzer/Checkers/CMakeLists.txt lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp test/Analysis/Inputs/system-header-simulator-ctor-uninitialized-member.h test/Analysis/ctor-uninitialized-member-inheritance.cpp test/Analysis/ctor-uninitialized-member.cpp
Index: test/Analysis/ctor-uninitialized-member.cpp =================================================================== --- /dev/null +++ test/Analysis/ctor-uninitialized-member.cpp @@ -0,0 +1,1404 @@ +//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -analyzer-config alpha.cplusplus.CtorUninitializedMember:Pedantic=true -std=c++11 -DPEDANTIC -verify %s + +//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Default constructor test. +//===----------------------------------------------------------------------===// + +class CompilerGeneratedConstructorTest { + int a, b, c, d, e, f, g, h, i, j; + +public: + CompilerGeneratedConstructorTest() = default; +}; + +void f000() { + CompilerGeneratedConstructorTest(); +} + +#ifdef PEDANTIC +class DefaultConstructorTest { + int a; // expected-note{{uninitialized field 'this->a'}} + +public: + DefaultConstructorTest(); +}; + +DefaultConstructorTest::DefaultConstructorTest() = default; + +void f00() { + DefaultConstructorTest(); // expected-warning{{1 uninitialized field}} +} +#else +class DefaultConstructorTest { + int a; + +public: + DefaultConstructorTest(); +}; + +DefaultConstructorTest::DefaultConstructorTest() = default; + +void f00() { + DefaultConstructorTest(); +} +#endif // PEDANTIC + +//===----------------------------------------------------------------------===// +// Initializer list test. +//===----------------------------------------------------------------------===// + +class InitListTest1 { + int a; + int b; + +public: + InitListTest1() + : a(1), + b(2) { + // All good! + } +}; + +void f01() { + InitListTest1(); +} + +class InitListTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + InitListTest2() + : a(3) {} // expected-warning{{1 uninitialized field}} +}; + +void f02() { + InitListTest2(); +} + +class InitListTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + InitListTest3() + : b(4) {} // expected-warning{{1 uninitialized field}} +}; + +void f03() { + InitListTest3(); +} + +//===----------------------------------------------------------------------===// +// Constructor body test. +//===----------------------------------------------------------------------===// + +class CtorBodyTest1 { + int a, b; + +public: + CtorBodyTest1() { + a = 5; + b = 6; + // All good! + } +}; + +void f04() { + CtorBodyTest1(); +} + +class CtorBodyTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest2() { + a = 7; // expected-warning{{1 uninitialized field}} + } +}; + +void f05() { + CtorBodyTest2(); +} + +class CtorBodyTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + CtorBodyTest3() { + b = 8; // expected-warning{{1 uninitialized field}} + } +}; + +void f06() { + CtorBodyTest3(); +} + +#ifdef PEDANTIC +class CtorBodyTest4 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest4() {} +}; + +void f07() { + CtorBodyTest4(); // expected-warning{{2 uninitialized fields}} +} +#else +class CtorBodyTest4 { + int a; + int b; + +public: + CtorBodyTest4() {} +}; + +void f07() { + CtorBodyTest4(); +} +#endif + +//===----------------------------------------------------------------------===// +// Constructor delegation test. +//===----------------------------------------------------------------------===// + +class CtorDelegationTest1 { + int a; + int b; + +public: + CtorDelegationTest1(int) + : a(9) { + // leaves 'b' unintialized, but we'll never check this function + } + + CtorDelegationTest1() + : CtorDelegationTest1(int{}) { // Initializing 'a' + b = 10; + // All good! + } +}; + +void f08() { + CtorDelegationTest1(); +} + +class CtorDelegationTest2 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + CtorDelegationTest2(int) + : b(11) { + // leaves 'a' unintialized, but we'll never check this function + } + + CtorDelegationTest2() + : CtorDelegationTest2(int{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f09() { + CtorDelegationTest2(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing records. +//===----------------------------------------------------------------------===// + +class ContainsRecordTest1 { + struct RecordType { + int x; + int y; + } rec; + int c, d; + +public: + ContainsRecordTest1() + : rec({12, 13}), + c(14), + d(15) { + // All good! + } +}; + +void f10() { + ContainsRecordTest1(); +} + +class ContainsRecordTest2 { + struct RecordType { + int x; + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; + +public: + ContainsRecordTest2() + : c(16), + d(17) { + rec.x = 18; // expected-warning{{1 uninitialized field}} + } +}; + +void f11() { + ContainsRecordTest2(); +} + +class ContainsRecordTest3 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->rec.x'}} + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; + +public: + ContainsRecordTest3() + : c(19), + d(20) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f12() { + ContainsRecordTest3(); +} + +class ContainsRecordTest4 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->rec.x'}} + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; // expected-note{{uninitialized field 'this->d'}} + +public: + ContainsRecordTest4() + : c(19) { // expected-warning{{3 uninitialized fields}} + } +}; + +void f12p2() { + ContainsRecordTest4(); +} + +//===----------------------------------------------------------------------===// +// Tests for template classes. +//===----------------------------------------------------------------------===// + +template <class T> +class IntTemplateClassTest1 { + T t; + int b; + +public: + IntTemplateClassTest1(T i) { + b = 21; + t = i; + // All good! + } +}; + +void f13() { + IntTemplateClassTest1<int>(22); +} + +template <class T> +class IntTemplateClassTest2 { + T t; // expected-note{{uninitialized field 'this->t'}} + int b; + +public: + IntTemplateClassTest2() { + b = 23; // expected-warning{{1 uninitialized field}} + } +}; + +void f14() { + IntTemplateClassTest2<int>(); +} + +struct Record { + int x; // expected-note{{uninitialized field 'this->t.x'}} + int y; // expected-note{{uninitialized field 'this->t.y'}} +}; + +template <class T> +class RecordTemplateClassTest { + T t; + int b; + +public: + RecordTemplateClassTest() { + b = 24; // expected-warning{{2 uninitialized fields}} + } +}; + +void f15() { + RecordTemplateClassTest<Record>(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing pointers. +//===----------------------------------------------------------------------===// + +class NullPtrTest { + struct RecordType { + int x; + int y; + }; + + float *fptr = nullptr; + int *ptr; + RecordType *recPtr; + +public: + NullPtrTest() : ptr(nullptr), recPtr(nullptr) { + // All good! + } +}; + +void f16() { + NullPtrTest(); +} + +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 f17() { + 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 f18() { + HeapPointerTest2(); +} + +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 f19() { + 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 f20() { + 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 f20() { + 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 f21() { + UninitPointerTest(); +} + +// Void pointer tests are mainly no-crash tests. + +class VoidPointerTest1 { + void *vptr; + +public: + VoidPointerTest1(void *vptr, char) : vptr(vptr) { + // All good! + } +}; + +void *malloc(int size); + +void f23() { + void *vptr = malloc(sizeof(int)); + VoidPointerTest1(vptr, char()); +} + +class VoidPointerTest2 { + void **vpptr; + +public: + VoidPointerTest2(void **vpptr, char) : vpptr(vpptr) { + // All good! + } +}; + +void f23p5() { + void *vptr = malloc(sizeof(int)); + VoidPointerTest2(&vptr, char()); +} + +class VoidPointerRRefTest1 { + void *&&vptrrref; + +public: + VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) { + // All good! + } +}; + +void f23p10() { + void *vptr = malloc(sizeof(int)); + VoidPointerRRefTest1(vptr, char()); +} + +class VoidPointerRRefTest2 { + void **&&vpptrrref; + +public: + VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) { + // All good! + } +}; + +void f23p15() { + void *vptr = malloc(sizeof(int)); + VoidPointerRRefTest2(&vptr, char()); +} + +class VoidPointerLRefTest { + void *&vptrrref; + +public: + VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) { + // All good! + } +}; + +void f23p20() { + void *vptr = malloc(sizeof(int)); + VoidPointerLRefTest(vptr, char()); +} + +#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 f25() { + 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 f25() { + 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 f26() { + 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 f26() { + 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 f27() { + MultiPointerTest3::RecordType i{31, 32}; + MultiPointerTest3::RecordType *p1 = &i; + MultiPointerTest3::RecordType **mptr = &p1; + MultiPointerTest3(mptr, int()); // '**mptr' uninitialized +} + +class ListTest1 { +public: + struct Node { + Node *next = nullptr; // no crash + int i; + }; + +private: + Node *head = nullptr; + +public: + ListTest1() { + // All good! + } +}; + +void f28() { + 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 f29() { + ListTest2::Node n; + ListTest2(&n, int()); +} + +class CyclicPointer { +public: + struct Node { + Node *next = nullptr; + int i; // expected-note{{uninitialized field 'this->head->i'}} + }; + +private: + Node *head = nullptr; + +public: + CyclicPointer(Node *node, int) : head(node) { // expected-warning{{1 uninitialized field}} + } +}; + +void f29p5() { + /* + n3 + / \ + this -- n1 -- n2 + */ + + CyclicPointer::Node n1; + CyclicPointer::Node n2; + n2.next = &n1; + n2.i = 50; + CyclicPointer::Node n3; + n3.next = &n2; + n3.i = 50; + n1.next = &n3; + // note that n1.i is uninitialized + CyclicPointer(&n1, int()); +} + +//===----------------------------------------------------------------------===// +// 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 f30() { + 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 f31() { + 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 f31() { + 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 f32() { + 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 f33() { + ReferenceTest4::RecordType c, d{37, 38}; + ReferenceTest4(d, c); +} + +//===----------------------------------------------------------------------===// +// Tests involving functions with unknown implementations. +//===----------------------------------------------------------------------===// + +template <class T> +void mayInitialize(T &); + +template <class T> +void wontInitialize(const T &); + +class PassingToUnknownFunctionTest1 { + int a, b; + +public: + PassingToUnknownFunctionTest1() { + mayInitialize(a); + mayInitialize(b); + // All good! + } + + PassingToUnknownFunctionTest1(int) { + mayInitialize(a); + // All good! + } + + PassingToUnknownFunctionTest1(int, int) { + mayInitialize(*this); + // All good! + } +}; + +void f34() { + PassingToUnknownFunctionTest1(); + PassingToUnknownFunctionTest1(int()); + PassingToUnknownFunctionTest1(int(), int()); +} + +class PassingToUnknownFunctionTest2 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + PassingToUnknownFunctionTest2() { + wontInitialize(a); + b = 4; // expected-warning{{1 uninitialized field}} + } +}; + +void f35() { + PassingToUnknownFunctionTest2(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing unions. +//===----------------------------------------------------------------------===// + +// FIXME: As of writing this checker, there is no good support for union types +// in the CSA. Here is non-exhaustive list of cases. +// Note that the rules for unions are different in C++ and C. +// http://lists.llvm.org/pipermail/cfe-dev/2017-March/052910.html + +class ContainsSimpleUnionTest1 { + union SimpleUnion { + float uf; + int ui; + char uc; + } u; + +public: + ContainsSimpleUnionTest1() { + u.uf = 3.14; + // All good! + } +}; + +void f36() { + ContainsSimpleUnionTest1(); +} + +class ContainsSimpleUnionTest2 { + union SimpleUnion { + float uf; + int ui; + char uc; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsSimpleUnionTest2() {} +}; + +void f37() { + // TODO: we'd expect the warning: {{1 uninitialized field}} + ContainsSimpleUnionTest2(); // no-warning +} + +class UnionPointerTest1 { +public: + union SimpleUnion { + float uf; + int ui; + char uc; + }; + +private: + SimpleUnion *uptr; + +public: + UnionPointerTest1(SimpleUnion *uptr, int) : uptr(uptr) { + // All good! + } +}; + +void f38() { + UnionPointerTest1::SimpleUnion u; + u.uf = 41; + UnionPointerTest1(&u, int()); +} + +class UnionPointerTest2 { +public: + union SimpleUnion { + float uf; + int ui; + char uc; + }; + +private: + // TODO: we'd expect the note: {{uninitialized field 'this->uptr'}} + SimpleUnion *uptr; // no-note + +public: + UnionPointerTest2(SimpleUnion *uptr, char) : uptr(uptr) {} +}; + +void f39() { + UnionPointerTest2::SimpleUnion u; + // TODO: we'd expect the warning: {{1 uninitialized field}} + UnionPointerTest2(&u, int()); // no-warning +} + +class ContainsUnionWithRecordTest1 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + } u; + +public: + ContainsUnionWithRecordTest1() { + u.ud = 3.14; + // All good! + } +}; + +void f40() { + ContainsUnionWithRecordTest1(); +} + +class ContainsUnionWithRecordTest2 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + } u; + +public: + ContainsUnionWithRecordTest2() { + u.us = UnionWithRecord::RecordType{42, 43}; + // All good! + } +}; + +void f41() { + ContainsUnionWithRecordTest1(); +} + +class ContainsUnionWithRecordTest3 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsUnionWithRecordTest3() { + UnionWithRecord::RecordType rec; + rec.x = 44; + // TODO: we'd expect the warning: {{1 uninitialized field}} + u.us = rec; // no-warning + } +}; + +void f42() { + ContainsUnionWithRecordTest3(); +} + +class ContainsUnionWithSimpleUnionTest1 { + union UnionWithSimpleUnion { + union SimpleUnion { + float uf; + int ui; + char uc; + } usu; + long ul; + unsigned uu; + } u; + +public: + ContainsUnionWithSimpleUnionTest1() { + u.usu.ui = 5; + // All good! + } +}; + +void f43() { + ContainsUnionWithSimpleUnionTest1(); +} + +class ContainsUnionWithSimpleUnionTest2 { + union UnionWithSimpleUnion { + union SimpleUnion { + float uf; + int ui; + char uc; + } usu; + long ul; + unsigned uu; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsUnionWithSimpleUnionTest2() {} +}; + +void f44() { + // TODO: we'd expect the warning: {{1 uninitialized field}} + ContainsUnionWithSimpleUnionTest2(); // no-warning +} + +//===----------------------------------------------------------------------===// +// Zero initialization tests. +//===----------------------------------------------------------------------===// + +struct GlobalVariableTest { + int i; + + GlobalVariableTest() {} +}; + +GlobalVariableTest gvt; // no-warning + +//===----------------------------------------------------------------------===// +// Copy and move constructor tests. +//===----------------------------------------------------------------------===// + +template <class T> +void funcToSquelchCompilerWarnings(const T &t); + +#ifdef PEDANTIC +struct CopyConstructorTest { + int i; // expected-note{{uninitialized field 'this->i'}} + + CopyConstructorTest() : i(1337) {} + CopyConstructorTest(const CopyConstructorTest &other) {} +}; + +void f45() { + CopyConstructorTest cct; + CopyConstructorTest copy = cct; // expected-warning{{1 uninitialized field}} + funcToSquelchCompilerWarnings(copy); +} +#else +struct CopyConstructorTest { + int i; + + CopyConstructorTest() : i(1337) {} + CopyConstructorTest(const CopyConstructorTest &other) {} +}; + +void f45() { + CopyConstructorTest cct; + CopyConstructorTest copy = cct; + funcToSquelchCompilerWarnings(copy); +} +#endif // PEDANTIC + +struct MoveConstructorTest { + // TODO: we'd expect the note: {{uninitialized field 'this->i'}} + int i; // no-note + + MoveConstructorTest() : i(1337) {} + MoveConstructorTest(const CopyConstructorTest &other) = delete; + MoveConstructorTest(const CopyConstructorTest &&other) {} +}; + +void f46() { + MoveConstructorTest cct; + // TODO: we'd expect the warning: {{1 uninitialized field}} + MoveConstructorTest copy(static_cast<MoveConstructorTest &&>(cct)); // no-warning + funcToSquelchCompilerWarnings(copy); +} + +//===----------------------------------------------------------------------===// +// Array tests. +//===----------------------------------------------------------------------===// + +struct IntArrayTest { + int arr[256]; + + IntArrayTest() { + // All good! + } +}; + +void f47() { + IntArrayTest(); +} +struct RecordTypeArrayTest { + struct RecordType { + int x, y; + } arr[256]; + + RecordTypeArrayTest() { + // All good! + } +}; + +void f48() { + RecordTypeArrayTest(); +} + +template <class T> +class CharArrayPointerTest { + T *t; // no-crash + +public: + CharArrayPointerTest(T *t, int) : t(t) {} +}; + +void f49() { + char str[16] = "012345678912345"; + CharArrayPointerTest<char[16]>(&str, int()); +} + +//===----------------------------------------------------------------------===// +// Memset tests. +//===----------------------------------------------------------------------===// + +struct MemsetTest1 { + int a, b, c; + + MemsetTest1() { + __builtin_memset(this, 0, sizeof(decltype(*this))); + } +}; + +void f50() { + MemsetTest1(); +} + +struct MemsetTest2 { + int a; + + MemsetTest2() { + __builtin_memset(&a, 0, sizeof(int)); + } +}; + +void f51() { + MemsetTest2(); +} + +//===----------------------------------------------------------------------===// +// Lambda tests. +//===----------------------------------------------------------------------===// + +template <class Callable> +struct LambdaTest1 { + Callable functor; + + LambdaTest1(const Callable &functor, int) : functor(functor) { + // All good! + } +}; + +void f52() { + auto isEven = [](int a) { return a % 2 == 0; }; + LambdaTest1<decltype(isEven)>(isEven, int()); +} + +#ifdef PEDANTIC +template <class Callable> +struct LambdaTest2 { + Callable functor; + + LambdaTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}} +}; + +void f53() { + int b; + auto equals = [&b](int a) { return a == b; }; // expected-note{{uninitialized field 'this->functor.'}} + LambdaTest2<decltype(equals)>(equals, int()); +} +#else +template <class Callable> +struct LambdaTest2 { + Callable functor; + + LambdaTest2(const Callable &functor, int) : functor(functor) {} +}; + +void f53() { + int b; + auto equals = [&b](int a) { return a == b; }; + LambdaTest2<decltype(equals)>(equals, int()); +} +#endif //PEDANTIC + +#ifdef PEDANTIC +namespace LT3Detail { + +struct RecordType { + int x; // expected-note{{uninitialized field 'this->functor..x'}} + int y; // expected-note{{uninitialized field 'this->functor..y'}} +}; + +} // namespace LT3Detail +template <class Callable> +struct LambdaTest3 { + Callable functor; + + LambdaTest3(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized fields}} +}; + +void f54() { + LT3Detail::RecordType rec1; + auto equals = [&rec1](LT3Detail::RecordType rec2) { + return rec1.x == rec2.x; + }; + LambdaTest3<decltype(equals)>(equals, int()); +} +#else +namespace LT3Detail { + +struct RecordType { + int x; + int y; +}; + +} // namespace LT3Detail +template <class Callable> +struct LambdaTest3 { + Callable functor; + + LambdaTest3(const Callable &functor, int) : functor(functor) {} +}; + +void f54() { + LT3Detail::RecordType rec1; + auto equals = [&rec1](LT3Detail::RecordType rec2) { + return rec1.x == rec2.x; + }; + LambdaTest3<decltype(equals)>(equals, int()); +} +#endif //PEDANTIC +//===----------------------------------------------------------------------===// +// System header tests. +//===----------------------------------------------------------------------===// + +#include "Inputs/system-header-simulator-ctor-uninitialized-member.h" + +struct SystemHeaderTest1 { + RecordInSystemHeader rec; // defined in the system header simulator + + SystemHeaderTest1() { + // All good! + } +}; + +void f55() { + SystemHeaderTest1(); +} + +#ifdef PEDANTIC +struct SystemHeaderTest2 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->container.t.x}} + int y; // expected-note{{uninitialized field 'this->container.t.y}} + }; + ContainerInSystemHeader<RecordType> container; + + SystemHeaderTest2(RecordType &rec, int) : container(rec) {} // expected-warning{{2 uninitialized fields}} +}; + +void f56() { + SystemHeaderTest2::RecordType rec; + SystemHeaderTest2(rec, int()); +} +#else +struct SystemHeaderTest2 { + struct RecordType { + int x; + int y; + }; + ContainerInSystemHeader<RecordType> container; + + SystemHeaderTest2(RecordType &rec, int) : container(rec) {} +}; + +void f56() { + SystemHeaderTest2::RecordType rec; + SystemHeaderTest2(rec, int()); +} +#endif //PEDANTIC Index: test/Analysis/ctor-uninitialized-member-inheritance.cpp =================================================================== --- /dev/null +++ test/Analysis/ctor-uninitialized-member-inheritance.cpp @@ -0,0 +1,775 @@ +//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -analyzer-config alpha.cplusplus.CtorUninitializedMember:Pedantic=true -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Non-polymorphic inheritance tests +//===----------------------------------------------------------------------===// + +class NonPolymorphicLeft1 { + int x; + +protected: + int y; + +public: + NonPolymorphicLeft1() = default; + NonPolymorphicLeft1(int) : x(1) {} +}; + +class NonPolymorphicInheritanceTest1 : public NonPolymorphicLeft1 { + int z; + +public: + NonPolymorphicInheritanceTest1() + : NonPolymorphicLeft1(int{}) { + y = 2; + z = 3; + // All good! + } +}; + +void f00() { + NonPolymorphicInheritanceTest1(); +} + +class NonPolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + NonPolymorphicRight1() = default; + NonPolymorphicRight1(int) : x(4) {} +}; + +class NonPolymorphicInheritanceTest2 : public NonPolymorphicRight1 { + int z; + +public: + NonPolymorphicInheritanceTest2() { + y = 5; + z = 6; // expected-warning{{1 uninitialized field}} + } +}; + +void f01() { + NonPolymorphicInheritanceTest2(); +} + +class NonPolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + NonPolymorphicBaseClass3() = default; + NonPolymorphicBaseClass3(int) : x(7) {} +}; + +class NonPolymorphicInheritanceTest3 : public NonPolymorphicBaseClass3 { + int z; + +public: + NonPolymorphicInheritanceTest3() + : NonPolymorphicBaseClass3(int{}) { + z = 8; // expected-warning{{1 uninitialized field}} + } +}; + +void f02() { + NonPolymorphicInheritanceTest3(); +} + +class NonPolymorphicBaseClass4 { + int x; + +protected: + int y; + +public: + NonPolymorphicBaseClass4() = default; + NonPolymorphicBaseClass4(int) : x(9) {} +}; + +class NonPolymorphicInheritanceTest4 : public NonPolymorphicBaseClass4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonPolymorphicInheritanceTest4() + : NonPolymorphicBaseClass4(int{}) { + y = 10; // expected-warning{{1 uninitialized field}} + } +}; + +void f03() { + NonPolymorphicInheritanceTest4(); +} + +//===----------------------------------------------------------------------===// +// Polymorphic inheritance tests +//===----------------------------------------------------------------------===// + +class PolymorphicLeft1 { + int x; + +protected: + int y; + +public: + virtual ~PolymorphicLeft1() = default; + PolymorphicLeft1() = default; + PolymorphicLeft1(int) : x(11) {} +}; + +class PolymorphicInheritanceTest1 : public PolymorphicLeft1 { + int z; + +public: + PolymorphicInheritanceTest1() + : PolymorphicLeft1(int{}) { + y = 12; + z = 13; + // All good! + } +}; + +void f04() { + PolymorphicInheritanceTest1(); +} + +class PolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + virtual ~PolymorphicRight1() = default; + PolymorphicRight1() = default; + PolymorphicRight1(int) : x(14) {} +}; + +class PolymorphicInheritanceTest2 : public PolymorphicRight1 { + int z; + +public: + PolymorphicInheritanceTest2() { + y = 15; + z = 16; // expected-warning{{1 uninitialized field}} + } +}; + +void f05() { + PolymorphicInheritanceTest2(); +} + +class PolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + virtual ~PolymorphicBaseClass3() = default; + PolymorphicBaseClass3() = default; + PolymorphicBaseClass3(int) : x(17) {} +}; + +class PolymorphicInheritanceTest3 : public PolymorphicBaseClass3 { + int z; + +public: + PolymorphicInheritanceTest3() + : PolymorphicBaseClass3(int{}) { + z = 18; // expected-warning{{1 uninitialized field}} + } +}; + +void f06() { + PolymorphicInheritanceTest3(); +} + +class PolymorphicBaseClass4 { + int x; + +protected: + int y; + +public: + virtual ~PolymorphicBaseClass4() = default; + PolymorphicBaseClass4() = default; + PolymorphicBaseClass4(int) : x(19) {} +}; + +class PolymorphicInheritanceTest4 : public PolymorphicBaseClass4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + PolymorphicInheritanceTest4() + : PolymorphicBaseClass4(int{}) { + y = 20; // expected-warning{{1 uninitialized field}} + } +}; + +void f07() { + PolymorphicInheritanceTest4(); +} + +//===----------------------------------------------------------------------===// +// Virtual inheritance tests +//===----------------------------------------------------------------------===// + +class VirtualPolymorphicLeft1 { + int x; + +protected: + int y; + +public: + virtual ~VirtualPolymorphicLeft1() = default; + VirtualPolymorphicLeft1() = default; + VirtualPolymorphicLeft1(int) : x(21) {} +}; + +class VirtualInheritanceTest1 : virtual public VirtualPolymorphicLeft1 { + int z; + +public: + VirtualInheritanceTest1() + : VirtualPolymorphicLeft1(int()) { + y = 22; + z = 23; + // All good! + } +}; + +void f08() { + VirtualInheritanceTest1(); +} + +class VirtualPolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + virtual ~VirtualPolymorphicRight1() = default; + VirtualPolymorphicRight1() = default; + VirtualPolymorphicRight1(int) : x(24) {} +}; + +class VirtualInheritanceTest2 : virtual public VirtualPolymorphicRight1 { + int z; + +public: + VirtualInheritanceTest2() { + y = 25; + z = 26; // expected-warning{{1 uninitialized field}} + } +}; + +void f09() { + VirtualInheritanceTest2(); +} + +class VirtualPolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + virtual ~VirtualPolymorphicBaseClass3() = default; + VirtualPolymorphicBaseClass3() = default; + VirtualPolymorphicBaseClass3(int) : x(27) {} +}; + +class VirtualInheritanceTest3 : virtual public VirtualPolymorphicBaseClass3 { + int z; + +public: + VirtualInheritanceTest3() + : VirtualPolymorphicBaseClass3(int{}) { + z = 28; // expected-warning{{1 uninitialized field}} + } +}; + +void f10() { + VirtualInheritanceTest3(); +} + +//===----------------------------------------------------------------------===// +// Multiple inheritance tests +//===----------------------------------------------------------------------===// + +/* + Left Right + \ / + \ / + \ / + MultipleInheritanceTest +*/ + +struct Left1 { + int x; + Left1() = default; + Left1(int) : x(29) {} +}; +struct Right1 { + int y; + Right1() = default; + Right1(int) : y(30) {} +}; + +class MultipleInheritanceTest1 : public Left1, public Right1 { + int z; + +public: + MultipleInheritanceTest1() + : Left1(int{}), + Right1(char{}) { + z = 31; + // All good! + } + + MultipleInheritanceTest1(int) + : Left1(int{}) { + y = 32; + z = 33; + // All good! + } + + MultipleInheritanceTest1(int, int) + : Right1(char{}) { + x = 34; + z = 35; + // All good! + } +}; + +void f11() { + MultipleInheritanceTest1(); + MultipleInheritanceTest1(int()); + MultipleInheritanceTest1(int(), int()); +} + +struct Left2 { + int x; + Left2() = default; + Left2(int) : x(36) {} +}; +struct Right2 { + int y; // expected-note{{uninitialized field 'this->y'}} + Right2() = default; + Right2(int) : y(37) {} +}; + +class MultipleInheritanceTest2 : public Left2, public Right2 { + int z; + +public: + MultipleInheritanceTest2() + : Left2(int{}) { + z = 38; // expected-warning{{1 uninitialized field}} + } +}; + +void f12() { + MultipleInheritanceTest2(); +} + +struct Left3 { + int x; // expected-note{{uninitialized field 'this->x'}} + Left3() = default; + Left3(int) : x(39) {} +}; +struct Right3 { + int y; + Right3() = default; + Right3(int) : y(40) {} +}; + +class MultipleInheritanceTest3 : public Left3, public Right3 { + int z; + +public: + MultipleInheritanceTest3() + : Right3(char{}) { + z = 41; // expected-warning{{1 uninitialized field}} + } +}; + +void f13() { + MultipleInheritanceTest3(); +} + +struct Left4 { + int x; + Left4() = default; + Left4(int) : x(42) {} +}; +struct Right4 { + int y; + Right4() = default; + Right4(int) : y(43) {} +}; + +class MultipleInheritanceTest4 : public Left4, public Right4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + MultipleInheritanceTest4() + : Left4(int{}), + Right4(char{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f14() { + MultipleInheritanceTest4(); +} + +struct Left5 { + int x; + Left5() = default; + Left5(int) : x(44) {} +}; +struct Right5 { + int y; // expected-note{{uninitialized field 'this->y'}} + Right5() = default; + Right5(int) : y(45) {} +}; + +class MultipleInheritanceTest5 : public Left5, public Right5 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + MultipleInheritanceTest5() // expected-warning{{2 uninitialized fields}} + : Left5(int{}) { + } +}; + +void f15() { + MultipleInheritanceTest5(); +} + +//===----------------------------------------------------------------------===// +// Non-virtual diamond inheritance tests +//===----------------------------------------------------------------------===// + +/* + NonVirtualBase NonVirtualBase + | | + | | + | | + First Second + \ / + \ / + \ / + NonVirtualDiamondInheritanceTest +*/ + +struct NonVirtualBase1 { + int x; + NonVirtualBase1() = default; + NonVirtualBase1(int) : x(46) {} +}; +struct First1 : public NonVirtualBase1 { + First1() = default; + First1(int) : NonVirtualBase1(int{}) {} +}; +struct Second1 : public NonVirtualBase1 { + Second1() = default; + Second1(int) : NonVirtualBase1(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest1 : public First1, public Second1 { + int z; + +public: + NonVirtualDiamondInheritanceTest1() + : First1(int{}), + Second1(int{}) { + z = 47; + // All good! + } + + NonVirtualDiamondInheritanceTest1(int) + : First1(int{}) { + Second1::x = 48; + z = 49; + // All good! + } + + NonVirtualDiamondInheritanceTest1(int, int) + : Second1(int{}) { + First1::x = 50; + z = 51; + // All good! + } +}; + +void f16() { + NonVirtualDiamondInheritanceTest1(); + NonVirtualDiamondInheritanceTest1(int()); + NonVirtualDiamondInheritanceTest1(int(), int()); +} + +struct NonVirtualBase2 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase2() = default; + NonVirtualBase2(int) : x(52) {} +}; +struct First2 : public NonVirtualBase2 { + First2() = default; + First2(int) : NonVirtualBase2(int{}) {} +}; +struct Second2 : public NonVirtualBase2 { + Second2() = default; + Second2(int) : NonVirtualBase2(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest2 : public First2, public Second2 { + int z; + +public: + NonVirtualDiamondInheritanceTest2() + : First2(int{}) { + z = 53; // expected-warning{{1 uninitialized field}} + } +}; + +void f17() { + NonVirtualDiamondInheritanceTest2(); +} + +struct NonVirtualBase3 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase3() = default; + NonVirtualBase3(int) : x(54) {} +}; +struct First3 : public NonVirtualBase3 { + First3() = default; + First3(int) : NonVirtualBase3(int{}) {} +}; +struct Second3 : public NonVirtualBase3 { + Second3() = default; + Second3(int) : NonVirtualBase3(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest3 : public First3, public Second3 { + int z; + +public: + NonVirtualDiamondInheritanceTest3() + : Second3(int{}) { + z = 55; // expected-warning{{1 uninitialized field}} + } +}; + +void f18() { + NonVirtualDiamondInheritanceTest3(); +} + +struct NonVirtualBase4 { + int x; // expected-note{{uninitialized field 'this->x'}} + // expected-note@-1{{uninitialized field 'this->x'}} + NonVirtualBase4() = default; + NonVirtualBase4(int) : x(56) {} +}; +struct First4 : public NonVirtualBase4 { + First4() = default; + First4(int) : NonVirtualBase4(int{}) {} +}; +struct Second4 : public NonVirtualBase4 { + Second4() = default; + Second4(int) : NonVirtualBase4(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest4 : public First4, public Second4 { + int z; + +public: + NonVirtualDiamondInheritanceTest4() { + z = 57; // expected-warning{{2 uninitialized fields}} + } +}; + +void f19() { + NonVirtualDiamondInheritanceTest4(); +} + +struct NonVirtualBase5 { + int x; + NonVirtualBase5() = default; + NonVirtualBase5(int) : x(58) {} +}; +struct First5 : public NonVirtualBase5 { + First5() = default; + First5(int) : NonVirtualBase5(int{}) {} +}; +struct Second5 : public NonVirtualBase5 { + Second5() = default; + Second5(int) : NonVirtualBase5(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest5 : public First5, public Second5 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonVirtualDiamondInheritanceTest5() + : First5(int{}), + Second5(int{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f20() { + NonVirtualDiamondInheritanceTest5(); +} + +struct NonVirtualBase6 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase6() = default; + NonVirtualBase6(int) : x(59) {} +}; +struct First6 : public NonVirtualBase6 { + First6() = default; + First6(int) : NonVirtualBase6(int{}) {} +}; +struct Second6 : public NonVirtualBase6 { + Second6() = default; + Second6(int) : NonVirtualBase6(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest6 : public First6, public Second6 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonVirtualDiamondInheritanceTest6() // expected-warning{{2 uninitialized fields}} + : First6(int{}) { + // 'z' and 'Second::x' unintialized + } +}; + +void f21() { + NonVirtualDiamondInheritanceTest6(); +} + +//===----------------------------------------------------------------------===// +// Virtual diamond inheritance tests +//===----------------------------------------------------------------------===// + +/* + VirtualBase + / \ + / \ + / \ + VirtualFirst VirtualSecond + \ / + \ / + \ / + NonVirtualDiamondInheritanceTest +*/ + +struct VirtualBase1 { + int x; + VirtualBase1() = default; + VirtualBase1(int) : x(60) {} +}; +struct VirtualFirst1 : virtual public VirtualBase1 { + VirtualFirst1() = default; + VirtualFirst1(int) : VirtualBase1(int{}) {} + VirtualFirst1(int, int) { x = 61; } +}; +struct VirtualSecond1 : virtual public VirtualBase1 { + VirtualSecond1() = default; + VirtualSecond1(int) : VirtualBase1(int{}) {} + VirtualSecond1(int, int) { x = 62; } +}; + +class VirtualDiamondInheritanceTest1 : public VirtualFirst1, public VirtualSecond1 { + +public: + VirtualDiamondInheritanceTest1() { + x = 0; + // All good! + } + + VirtualDiamondInheritanceTest1(int) + : VirtualFirst1(int{}, int{}), + VirtualSecond1(int{}, int{}) { + // All good! + } + + VirtualDiamondInheritanceTest1(int, int) + : VirtualFirst1(int{}, int{}) { + // All good! + } +}; + +void f22() { + VirtualDiamondInheritanceTest1(); + VirtualDiamondInheritanceTest1(int()); + VirtualDiamondInheritanceTest1(int(), int()); +} + +struct VirtualBase2 { + int x; // expected-note{{uninitialized field 'this->x'}} + VirtualBase2() = default; + VirtualBase2(int) : x(63) {} +}; +struct VirtualFirst2 : virtual public VirtualBase2 { + VirtualFirst2() = default; + VirtualFirst2(int) : VirtualBase2(int{}) {} + VirtualFirst2(int, int) { x = 64; } +}; +struct VirtualSecond2 : virtual public VirtualBase2 { + VirtualSecond2() = default; + VirtualSecond2(int) : VirtualBase2(int{}) {} + VirtualSecond2(int, int) { x = 65; } +}; + +class VirtualDiamondInheritanceTest2 : public VirtualFirst2, public VirtualSecond2 { + +public: + VirtualDiamondInheritanceTest2() // expected-warning{{1 uninitialized field}} + : VirtualFirst2(int{}) { + // From the N4659 C++ Standard Working Draft: + // + // (15.6.2.7) + // [...] A 'mem-initializer' where the 'mem-initializer-id' denotes a + // virtual base class is ignored during execution of a constructor of any + // class that is not the most derived class. + // + // This means that Left1::x will not be initialized, because in both + // VirtualFirst::VirtualFirst(int) and VirtualSecond::VirtualSecond(int) + // the constructor delegation to Left1::Left1(int) will be + // ignored. + } +}; + +void f23() { + VirtualDiamondInheritanceTest2(); +} + +struct VirtualBase3 { + int x; // expected-note{{uninitialized field 'this->x'}} + VirtualBase3() = default; + VirtualBase3(int) : x(66) {} +}; +struct VirtualFirst3 : virtual public VirtualBase3 { + VirtualFirst3() = default; + VirtualFirst3(int) : VirtualBase3(int{}) {} + VirtualFirst3(int, int) { x = 67; } +}; +struct VirtualSecond3 : virtual public VirtualBase3 { + VirtualSecond3() = default; + VirtualSecond3(int) : VirtualBase3(int{}) {} + VirtualSecond3(int, int) { x = 68; } +}; + +class VirtualDiamondInheritanceTest3 : public VirtualFirst3, public VirtualSecond3 { + +public: + VirtualDiamondInheritanceTest3() // expected-warning{{1 uninitialized field}} + : VirtualFirst3(int{}) {} +}; + +void f24() { + VirtualDiamondInheritanceTest3(); +} Index: test/Analysis/Inputs/system-header-simulator-ctor-uninitialized-member.h =================================================================== --- /dev/null +++ test/Analysis/Inputs/system-header-simulator-ctor-uninitialized-member.h @@ -0,0 +1,18 @@ +// Like the compiler, the static analyzer treats some functions differently if +// they come from a system header -- for example, it is assumed that system +// functions do not arbitrarily free() their parameters, and that some bugs +// found in system headers cannot be fixed by the user and should be +// suppressed. + +#pragma clang system_header + +struct RecordInSystemHeader { + int a; + int b; +}; + +template <class T> +struct ContainerInSystemHeader { + T &t; + ContainerInSystemHeader(T& t) : t(t) {} +}; Index: lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp @@ -0,0 +1,574 @@ +//=======- CtorUninitializedMemberChecker.cpp --------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a checker that reports uninitialized fields in objects +// created after a constructor call. +// +// This checker has an option "Pedantic" (boolean). If its not set or is set to +// false, the checker won't emit warnings for objects that don't have at least +// one initialized field. This may be set with +// `-analyzer-config alpha.cplusplus.CtorUninitializedMember:Pedantic=true`. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <algorithm> + +using namespace clang; +using namespace clang::ento; + +namespace { + +class CtorUninitializedMemberChecker : public Checker<check::EndFunction> { + std::unique_ptr<BuiltinBug> BT_uninitField; + +public: + bool IsPedantic; // Will be initialized when registering the checker. + + CtorUninitializedMemberChecker() + : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} + void checkEndFunction(CheckerContext &C) const; +}; + +/// Represents a field chain. A field chain is a vector of fields where the +/// first element of the chain is the object under checking (not stored), and +/// every other element is a field, and the element that precedes it is the +/// object that contains it. +class FieldChainInfo { + using FieldChain = llvm::SmallVector<const FieldRegion *, 10>; + + FieldChain Chain; + + /// If this is a fieldchain whose last element is an uninitialized region of a + /// pointer type, this variable will store whether the pointer itself or the + /// pointee is uninitialized. + bool IsDereferenced = false; + +public: + FieldChainInfo() = default; + /// Delegates to the copy ctor and adds FR to Chain. Note that Chain cannot be + /// modified in a FieldChainInfo object after its construction. + FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR); + + bool contains(const FieldRegion *FR) { + return std::find(Chain.begin(), Chain.end(), FR) != Chain.end(); + } + + bool isPointer() const { + return Chain.back()->getDecl()->getType()->isPointerType(); + } + bool isDereferenced() const; + void dereference() { IsDereferenced = true; } + const FieldDecl *getEndOfChain() const { return Chain.back()->getDecl(); } + template <unsigned Size> void toString(SmallString<Size> &Buf) const; + friend struct FieldChainInfoComparator; +}; + +struct FieldChainInfoComparator { + bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) { + return lhs.Chain.back() < rhs.Chain.back(); + } +}; + +using UninitFieldSet = std::set<FieldChainInfo, FieldChainInfoComparator>; + +/// Searches for and stores uninitialized fields in a non-union object. +class FindUninitializedFields { + StoreManager &StoreMgr; + MemRegionManager &MrMgr; + SourceManager &SrcMgr; + Store S; + const TypedValueRegion *const ObjectR; + + const bool IsPedantic; + bool IsChecked = false; + bool IsAnyFieldInitialized = false; + + UninitFieldSet UninitFields; + +public: + FindUninitializedFields(StoreManager &StoreMgr, MemRegionManager &MrMgr, + SourceManager &SrcMgr, Store S, + const TypedValueRegion *const R, bool IsPedantic); + bool hasUnintializedFields(); + const UninitFieldSet &getUninitFields(); + +private: + /// Adds a FieldChainInfo object to UninitFields. Return true if an insertion + /// took place. + bool addFieldToUninits(FieldChainInfo LocalChain); + + // For the purposes of this checker, we'll regard the object under checking as + // a directed tree, where + // * the root is the object under checking + // * every node is an object that is + // - a union + // - a non-union record + // - a pointer/reference + // - an array + // - of a fundamental type (int, double, etc.) + // * the parent of each node is the object that contains it + // * every leaf is an array, a fundamental object, a nullptr or an undefined + // pointer. + // + // Example: + // + // struct A { + // struct B { + // int x, y = 0; + // }; + // B b; + // int *iptr = new int; + // B* bptr; + // + // A() {} + // }; + // + // The directed tree: + // + // ->x + // / + // ->b--->y + // / + // A-->iptr->(int value) + // \ + // ->bptr + // + // From this we'll construct a vector of fieldchains, where each fieldchain + // represents an uninitialized field. If the field is a pointer type, we'll + // also store whether the pointer itself or value it points to is uninit. + // In the above example, for the default constructor call we'll end up with + // these fieldchains: + // + // this->b.x + // this->iptr (pointee uninit) + // this->bptr (pointer uninit) + // + // We'll traverse each node of the above graph with the appropiate one of + // these methods: + bool isUnionUninit(const TypedValueRegion *R); + bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); + bool isPointerOrReferenceUninit(const FieldRegion *FR, + FieldChainInfo LocalChain); + bool isFundamentalUninit(const SVal &V); + // Note that we don't have a method for arrays -- the elements of an array are + // often left uninitialized intentionally even when it is of a C++ record + // type, so we'll assume that an array is always initialized. +}; + +// Utility function declarations. + +/// Returns the with the object that was constructed by CtorDecl, or None if +/// that isn't possible. +Optional<nonloc::LazyCompoundVal> +getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context); + +/// Checks whether the constructor under checking is called by another +/// constructor. This avoids essentially the same error being reported multiple +/// times. +bool isCalledByConstructor(const CheckerContext &Context); + +/// Returns whether FD can be (transitively) dereferenced to a void pointer type +/// (void*, void**, ...). The type of the region behind a void pointer isn't +/// known, and thus FD can not be analyzed. +bool isVoidPointer(const FieldDecl *FD); + +} // namespace + +//===----------------------------------------------------------------------===// +// Methods for CtorUninitializedMemberChecker. +//===----------------------------------------------------------------------===// + +void CtorUninitializedMemberChecker::checkEndFunction( + CheckerContext &Context) const { + + const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( + Context.getLocationContext()->getDecl()); + if (!CtorDecl) + return; + + if (!CtorDecl->isUserProvided()) + return; + + if (CtorDecl->getParent()->isUnion()) + return; + + if (isCalledByConstructor(Context)) + return; + + Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context); + if (!Object) + return; + + FindUninitializedFields F(Context.getStoreManager(), + Context.getSValBuilder().getRegionManager(), + Context.getSourceManager(), Object->getStore(), + Object->getRegion(), IsPedantic); + + if (!F.hasUnintializedFields()) + return; + + // There are uninitialized fields in the record. + const UninitFieldSet &UninitFields = F.getUninitFields(); + + ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState()); + if (!Node) + return; + + PathDiagnosticLocation LocUsedForUniqueing; + const Stmt *CallSite = Context.getStackFrame()->getCallSite(); + if (CallSite) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + CallSite, Context.getSourceManager(), Node->getLocationContext()); + + llvm::Twine WarningMsg = llvm::Twine(UninitFields.size()) + + llvm::Twine(" uninitialized field") + + llvm::Twine(UninitFields.size() == 1 ? "" : "s") + + llvm::Twine(" at the end of the constructor call"); + + auto Report = llvm::make_unique<BugReport>( + *BT_uninitField, WarningMsg.str(), Node, LocUsedForUniqueing, + Node->getLocationContext()->getDecl()); + + for (const auto &FieldChain : UninitFields) { + SmallString<200> FieldChainBuf; + FieldChain.toString(FieldChainBuf); + SmallString<200> NoteBuf; + llvm::raw_svector_ostream NoteOS(NoteBuf); + + if (FieldChain.isPointer()) { + if (FieldChain.isDereferenced()) + NoteOS << "uninitialized pointee 'this->"; + else + NoteOS << "uninitialized pointer 'this->"; + } else + NoteOS << "uninitialized field 'this->"; + NoteOS << FieldChainBuf; + NoteOS << "'"; + + Report->addNote(NoteBuf, + PathDiagnosticLocation::create(FieldChain.getEndOfChain(), + Context.getSourceManager())); + } + + Context.emitReport(std::move(Report)); +} + +//===----------------------------------------------------------------------===// +// Methods for FindUninitializedFields. +//===----------------------------------------------------------------------===// + +FindUninitializedFields::FindUninitializedFields( + StoreManager &StoreMgr, MemRegionManager &MrMgr, SourceManager &SrcMgr, + Store S, const TypedValueRegion *const R, bool IsPedantic) + : StoreMgr(StoreMgr), MrMgr(MrMgr), SrcMgr(SrcMgr), S(S), ObjectR(R), + IsPedantic(IsPedantic) {} + +bool FindUninitializedFields::hasUnintializedFields() { + if (!IsChecked) { + IsChecked = true; + if (!IsPedantic) + return isNonUnionUninit(ObjectR, FieldChainInfo()) && + IsAnyFieldInitialized; + return isNonUnionUninit(ObjectR, FieldChainInfo()); + } + + if (!IsPedantic) + return UninitFields.empty() && IsAnyFieldInitialized; + return UninitFields.empty(); +} + +const UninitFieldSet &FindUninitializedFields::getUninitFields() { + if (!IsChecked) + hasUnintializedFields(); + return UninitFields; +} + +bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) { + if (SrcMgr.isInSystemHeader(Chain.getEndOfChain()->getLocation())) + return false; + + return UninitFields.insert(std::move(Chain)).second; +} + +bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, + FieldChainInfo LocalChain) { + + const RecordDecl *RD = + R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition(); + assert(RD && "Referred record has no definition"); + + bool ContainsUninitField = false; + + // Are all of this non-union's fields initialized? + for (const FieldDecl *I : RD->fields()) { + const FieldRegion *FR = MrMgr.getFieldRegion(I, R); + QualType T = I->getType(); + + // If LocalChain already contains FR, then we encountered a cyclic + // reference. In this case, region FR is already under checking at an + // earlier node in the directed tree. + if (LocalChain.contains(FR)) + return false; + + if (T->isStructureOrClassType()) { + if (isNonUnionUninit(FR, {LocalChain, FR})) + ContainsUninitField = true; + continue; + } + + if (T->isUnionType()) { + if (isUnionUninit(FR)) { + if (addFieldToUninits({LocalChain, FR})) + ContainsUninitField = true; + } else + IsAnyFieldInitialized = true; + continue; + } + + if (T->isArrayType()) { + IsAnyFieldInitialized = true; + continue; + } + + SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR)); + + // If this is a pointer or reference type. + if (V.getAs<Loc>()) { + if (isPointerOrReferenceUninit(FR, {LocalChain, FR})) + ContainsUninitField = true; + continue; + } + + // At this point the field is a fundamental type. + if (isFundamentalUninit(V)) { + if (addFieldToUninits({LocalChain, FR})) + ContainsUninitField = true; + } + } + + // Checking bases. + const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); + if (!CXXRD) + return ContainsUninitField; + + for (const CXXBaseSpecifier BaseSpec : CXXRD->bases()) { + const auto *Base = BaseSpec.getType()->getAsCXXRecordDecl(); + const auto *BaseRegion = + MrMgr.getCXXBaseObjectRegion(Base, R, BaseSpec.isVirtual()); + + if (isNonUnionUninit(BaseRegion, LocalChain)) + ContainsUninitField = true; + } + + return ContainsUninitField; +} + +bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { + // TODO: Implement support for union fields. + return false; +} + +// Note that pointers/references don't contain fields themselves, so in this +// function we won't add anything to LocalChain. +bool FindUninitializedFields::isPointerOrReferenceUninit( + const FieldRegion *FR, FieldChainInfo LocalChain) { + + SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR)); + + if (V.isUnknown() || V.isZeroConstant()) { + IsAnyFieldInitialized = true; + return false; + } + + if (V.isUndef()) { + return addFieldToUninits(std::move(LocalChain)); + } + + const FieldDecl *FD = FR->getDecl(); + + if (isVoidPointer(FD)) { + IsAnyFieldInitialized = true; + return false; + } + + SVal DerefdV = StoreMgr.getBinding(S, V.castAs<Loc>()); + while (Optional<Loc> L = DerefdV.getAs<Loc>()) + DerefdV = StoreMgr.getBinding(S, *L); + + // If V is a pointer pointing to a record type. + if (Optional<nonloc::LazyCompoundVal> RecordV = + DerefdV.getAs<nonloc::LazyCompoundVal>()) { + + const TypedValueRegion *R = RecordV->getRegion(); + + // We can't reason about symbolic regions, assume its initialized. + // Note that this also avoids a potential infinite recursion, because + // constructors for list-like classes are checked without being called, and + // the CSA will conjure a symbolic region for Node *next; or similar code + // snippets. + if (R->getSymbolicBase()) { + IsAnyFieldInitialized = true; + return false; + } + + const QualType T = R->getValueType(); + + if (T->isStructureOrClassType()) + return isNonUnionUninit(R, std::move(LocalChain)); + + if (T->isUnionType()) { + // TODO: does control ever reach here? + if (isUnionUninit(R)) { + LocalChain.dereference(); + return addFieldToUninits(std::move(LocalChain)); + } else { + IsAnyFieldInitialized = true; + return false; + } + } + + if (T->isArrayType()) { + IsAnyFieldInitialized = true; + return false; + } + } + + // At this point V is a pointer pointing to a fundamental type. + if (isFundamentalUninit(DerefdV)) { + LocalChain.dereference(); + return addFieldToUninits(std::move(LocalChain)); + } + IsAnyFieldInitialized = true; + return false; +} + +bool FindUninitializedFields::isFundamentalUninit(const SVal &V) { + if (V.isUndef()) + return true; + + IsAnyFieldInitialized = true; + return false; +} + +//===----------------------------------------------------------------------===// +// Methods for FieldChainInfo. +//===----------------------------------------------------------------------===// + +FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other, + const FieldRegion *FR) + : FieldChainInfo(Other) { + assert(!contains(FR) && "Can't add a field that is already a part of the " + "fieldchain! Is this a cyclic reference?"); + Chain.push_back(FR); +} + +bool FieldChainInfo::isDereferenced() const { + assert(isPointer() && "Only pointers may or may not be dereferenced!"); + return IsDereferenced; +} + +// TODO: This function constructs an incorrect fieldchain string in the +// following case: +// +// struct Base { int x; }; +// struct D1 : Base {}; struct D2 : Base {}; +// +// struct MostDerived : D1, D2 { +// MostDerived() {} +// } +// +// A call to MostDerived::MostDerived() will cause two notes that say +// "uninitialized field 'this->x'", but we can't refer to 'x' directly, +// we need an explicit namespace resolution whether the uninit field was +// 'D1::x' or 'D2::x'. +// +// TODO: If a field in the fieldchain is a captured lambda parameter, this +// function constructs an empty string for it: +// +// template <class Callable> struct A { +// Callable c; +// A(const Callable &c, int) : c(c) {} +// }; +// +// int b; // say that this isn't zero initialized +// auto alwaysTrue = [&b](int a) { return true; }; +// +// A call with these parameters: A<decltype(alwaysTrue)>::A(alwaysTrue, int()) +// will emit a note with the message "uninitialized field: 'this->c.'". If +// possible, the lambda parameter name should be retrieved or be replaced with a +// "<lambda parameter>" or something similar. +template <unsigned Size> +void FieldChainInfo::toString(SmallString<Size> &Buf) const { + if (Chain.empty()) + return; + + llvm::raw_svector_ostream OS(Buf); + const FieldRegion *Last = Chain.back(); + for (auto It = Chain.begin(); *It != Last; ++It) { + const FieldDecl *Field = (*It)->getDecl(); + OS << Field->getNameAsString(); + OS << (Field->getType()->isPointerType() ? "->" : "."); + } + OS << Last->getDecl()->getNameAsString(); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +namespace { + +bool isVoidPointer(const FieldDecl *FD) { + QualType T = FD->getType(); + + while (T.getTypePtrOrNull()) { + if (T->isVoidPointerType()) + return true; + T = T->getPointeeType(); + } + return false; +} + +Optional<nonloc::LazyCompoundVal> +getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { + + Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(), + Context.getStackFrame()); + // Getting the value for 'this'. + SVal This = Context.getState()->getSVal(ThisLoc); + + // Getting the value for '*this'. + SVal Object = Context.getState()->getSVal(This.castAs<Loc>()); + + return Object.getAs<nonloc::LazyCompoundVal>(); +} + +bool isCalledByConstructor(const CheckerContext &Context) { + const LocationContext *LC = Context.getLocationContext()->getParent(); + + while (LC) { + if (isa<CXXConstructorDecl>(LC->getDecl())) + return true; + + LC = LC->getParent(); + } + return false; +} + +} // namespace + +void ento::registerCtorUninitializedMemberChecker(CheckerManager &Mgr) { + auto Chk = Mgr.registerChecker<CtorUninitializedMemberChecker>(); + Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption( + "Pedantic", /*DefaultVal*/ false, Chk); +} Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -26,6 +26,7 @@ ClangCheckers.cpp CloneChecker.cpp ConversionChecker.cpp + CtorUninitializedMemberChecker.cpp CXXSelfAssignmentChecker.cpp DeadStoresChecker.cpp DebugCheckers.cpp Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -300,6 +300,10 @@ let ParentPackage = CplusplusAlpha in { +def CtorUninitializedMemberChecker: Checker<"CtorUninitializedMember">, + HelpText<"Reports uninitialized members in constructors">, + DescFile<"CtorUninitializedMemberChecker.cpp">; + def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">, HelpText<"Reports destructions of polymorphic objects with a non-virtual " "destructor in their base class">,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits