Szelethus updated this revision to Diff 142760.
Szelethus added a comment.

In https://reviews.llvm.org/D45532#1068700, @Szelethus wrote:

> Also, I managed to cause a crash with the class `linked_ptr_internal` from 
> google's boringssl when I analyzed the grpc project. I'll look deeper into 
> this, but I have a strong suspicion that the error lies within the CSA core.


I was very much wrong on that, the checker didn't handle cyclic references. I 
fixed that and some other crashes I encountered when checking the LLVM project.

To summarize, in this diff I

- added some more test cases,
- fixed crashes,
- removed unnecessary headers,
- fixed some typos in the documentation.


https://reviews.llvm.org/D45532

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp
  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,1122 @@
+//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();
+}
+
+class DefaultConstructorTest {
+  int a; // expected-note{{uninitialized field 'this->a'}}
+
+public:
+  DefaultConstructorTest();
+};
+
+DefaultConstructorTest::DefaultConstructorTest() = default;
+
+void f00() {
+  DefaultConstructorTest(); // expected-warning{{1 uninitialized field}}
+}
+
+//===----------------------------------------------------------------------===//
+// 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();
+}
+
+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}}
+}
+
+//===----------------------------------------------------------------------===//
+// 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
+}
+
+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
+}
+
+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());
+}
+
+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
+}
+
+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
+}
+
+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);
+}
+
+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);
+}
+
+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);
+
+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);
+}
+
+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();
+}
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 -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: lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp
===================================================================
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp
@@ -0,0 +1,489 @@
+//=======- 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.
+//
+//===----------------------------------------------------------------------===//
+
+#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:
+  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;
+  Store S;
+  const TypedValueRegion *const ObjectR;
+  bool IsChecked = false;
+
+  UninitFieldSet UninitFields;
+
+public:
+  FindUninitializedFields(StoreManager &StoreMgr, MemRegionManager &MrMgr,
+                          Store S, const TypedValueRegion *const R);
+  bool isFullyInitialized();
+  const UninitFieldSet &getUninitFields();
+
+private:
+  void 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
+  //     - of a fundamental type (int, double, etc.)
+  //   * the parent of each node is the object that contains it
+  //   * every leaf is 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);
+};
+
+// 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 true if FD is a void pointer (void*, void**, etc.).
+/// We can't know the type of the region that a void pointer points to, so FD
+/// can't be analyzed if this function return true for it.
+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(),
+                            Object->getStore(), Object->getRegion());
+
+  if (!F.isFullyInitialized())
+    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());
+
+  std::string WarningMsg = std::to_string(UninitFields.size()) +
+                           " uninitialized field" +
+                           (UninitFields.size() == 1 ? "" : "s") +
+                           " at the end of the constructor call";
+
+  auto Report = llvm::make_unique<BugReport>(
+      *BT_uninitField, WarningMsg, 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, Store S,
+    const TypedValueRegion *const R)
+    : StoreMgr(StoreMgr), MrMgr(MrMgr), S(S), ObjectR(R) {}
+
+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 FindUninitializedFields::isFullyInitialized() {
+  if (!IsChecked) {
+    IsChecked = true;
+    return isNonUnionUninit(ObjectR, FieldChainInfo());
+  }
+  return UninitFields.empty();
+}
+
+const UninitFieldSet &FindUninitializedFields::getUninitFields() {
+  if (!IsChecked)
+    isFullyInitialized();
+  return UninitFields;
+}
+
+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)) {
+        addFieldToUninits({LocalChain, FR});
+        ContainsUninitField = 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)) {
+      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())
+    return false;
+
+  if (V.isUndef()) {
+    addFieldToUninits(std::move(LocalChain));
+    return true;
+  }
+  const FieldDecl *FD = FR->getDecl();
+
+  if (isVoidPointer(FD))
+    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())
+      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();
+        addFieldToUninits(std::move(LocalChain));
+        return true;
+      }
+    }
+  }
+
+  // At this point V is a pointer pointing to a fundamental type.
+  if (isFundamentalUninit(DerefdV)) {
+    LocalChain.dereference();
+    addFieldToUninits(std::move(LocalChain));
+    return true;
+  }
+
+  return false;
+}
+
+bool FindUninitializedFields::isFundamentalUninit(const SVal &V) {
+  return V.isUndef();
+}
+
+void FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) {
+  UninitFields.insert(std::move(Chain));
+}
+
+//===----------------------------------------------------------------------===//
+//                       Methods for FieldChainInfo.
+//===----------------------------------------------------------------------===//
+
+bool FieldChainInfo::isDereferenced() const {
+  assert(isPointer() && "Only pointers may or may not be dereferenced!");
+  return IsDereferenced;
+}
+
+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();
+
+  if (T->isReferenceType())
+    T = T->getPointeeType();
+
+  while (T->isPointerType() && !T->isVoidPointerType())
+    T = T->getPointeeType();
+
+  return T->isVoidPointerType();
+}
+
+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) {
+  Mgr.registerChecker<CtorUninitializedMemberChecker>();
+}
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

Reply via email to