Szelethus created this revision.
Szelethus added reviewers: NoQ, xazax.hun, dkrupp, whisperity.
Herald added subscribers: cfe-commits, a.sidorin, rnkovacs, szepet, mgorny.
Herald added a reviewer: george.karpenkov.

This checker checks at the end of a constructor call whether all fields of the 
object were initialized.

Note that a significant portion of the code is for handling unions, for which 
there is very little support in the CSA. In most cases, all fields of a union 
is regarded as unknown. I checked these cases by regarding unknown fields as 
uninitialized.

The test files also contain checks for heap allocated objects, and heap regions 
are mostly conjured in the CSA, so the tests for them and unions should work 
theoretically, but its not 100%.


Repository:
  rC Clang

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,870 @@
+//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -std=c++11 -verify %s
+
+//===----------------------------------------------------------------------===//
+// Default constructor test.
+//===----------------------------------------------------------------------===//
+
+class DefaultConstructorTest {
+  int a, b, c, d, e, f, g, h, i, j;
+
+public:
+  DefaultConstructorTest() = default;
+};
+
+void f00() {
+  DefaultConstructorTest();
+}
+
+//===----------------------------------------------------------------------===//
+// Initializer list test.
+//===----------------------------------------------------------------------===//
+
+class InitListTest1 {
+  int a;
+  int b;
+
+public:
+  InitListTest1()
+      : a(420),
+        b(420) {
+    // All good!
+  }
+};
+
+void f01() {
+  InitListTest1();
+}
+
+class InitListTest2 {
+  int a;
+  int b; // expected-note{{uninitialized field 'this->b'}}
+
+public:
+  InitListTest2()
+      : a(420) {} // expected-warning{{1 uninitialized field}}
+};
+
+void f02() {
+  InitListTest2();
+}
+
+class InitListTest3 {
+  int a; // expected-note{{uninitialized field 'this->a'}}
+  int b;
+
+public:
+  InitListTest3()
+      : b(420) {} // expected-warning{{1 uninitialized field}}
+};
+
+void f03() {
+  InitListTest3();
+}
+
+//===----------------------------------------------------------------------===//
+// Constructor body test.
+//===----------------------------------------------------------------------===//
+
+class CtorBodyTest1 {
+  int a, b;
+
+public:
+  CtorBodyTest1() {
+    a = 420;
+    b = 420;
+    // All good!
+  }
+};
+
+void f04() {
+  CtorBodyTest1();
+}
+
+class CtorBodyTest2 {
+  int a;
+  int b; // expected-note{{uninitialized field 'this->b'}}
+
+public:
+  CtorBodyTest2() {
+    a = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f05() {
+  CtorBodyTest2();
+}
+
+class CtorBodyTest3 {
+  int a; // expected-note{{uninitialized field 'this->a'}}
+  int b;
+
+public:
+  CtorBodyTest3() {
+    b = 420; // 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(420) {
+    // leaves 'b' unintialized, but we'll never check this function
+  }
+
+  CtorDelegationTest1()
+      : CtorDelegationTest1(int{}) { // Initializing 'a'
+    b = 420;
+    // All good!
+  }
+};
+
+void f08() {
+  CtorDelegationTest1();
+}
+
+class CtorDelegationTest2 {
+  int a; // expected-note{{uninitialized field 'this->a'}}
+  int b;
+
+public:
+  CtorDelegationTest2(int)
+      : b(420) {
+    // 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({420, 420}),
+        c(420),
+        d(420) {
+    // 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(420),
+        d(420) {
+    rec.x = 420; // 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(420),
+        d(420) { // expected-warning{{2 uninitialized fields}}
+  }
+};
+
+void f12() {
+  ContainsRecordTest3();
+}
+
+//===----------------------------------------------------------------------===//
+// Tests for template classes.
+//===----------------------------------------------------------------------===//
+
+template <class T>
+class IntTemplateClassTest1 {
+  T t;
+  int b;
+
+public:
+  IntTemplateClassTest1(T i) {
+    b = 420;
+    t = i;
+    // All good!
+  }
+};
+
+void f13() {
+  IntTemplateClassTest1<int>(420);
+}
+
+template <class T>
+class IntTemplateClassTest2 {
+  T t; // expected-note{{uninitialized field 'this->t'}}
+  int b;
+
+public:
+  IntTemplateClassTest2() {
+    b = 420; // 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 = 420; // 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 {
+    int x; //xpected-note{{uninitialized field 'this->recPtr->x'}}
+    int y; //xpected-note{{uninitialized field 'this->recPtr->y'}}
+  };
+
+  float *fptr = new float; //xpected-note{{uninitialized pointee 'this->fptr'}}
+  int *ptr;                //xpected-note{{uninitialized pointee 'this->ptr'}}
+  RecordType *recPtr;
+
+public:
+  HeapPointerTest1() : ptr(new int), recPtr(new RecordType) { //xpected-warning{{3 uninitialized fields}}
+  }
+};
+
+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{420}), recPtr(new RecordType{420, 420}) {
+    // 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 = 420;
+  StackPointerTest1::RecordType ok_rec{420, 420};
+  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();
+}
+
+class VoidPointerTest {
+  void *vptr;
+
+public:
+  VoidPointerTest(void *vptr, char) : vptr(vptr) {
+    // All good!
+  }
+};
+
+void *malloc(int size);
+
+void f23() {
+  void *vptr = malloc(sizeof(int));
+  VoidPointerTest(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{420, 420};
+  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());
+}
+
+//===----------------------------------------------------------------------===//
+// 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{420, 420};
+  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{420, 420};
+  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{420, 420};
+  ReferenceTest4(d, c);
+}
+
+//===----------------------------------------------------------------------===//
+// Tests for classes containing unions.
+//===----------------------------------------------------------------------===//
+
+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/242017-March/42052912.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;
+  } u; // xpected-note{{uninitialized field 'this->u'}}
+
+public:
+  ContainsSimpleUnionTest2() {}
+};
+
+void f37() {
+  ContainsSimpleUnionTest2(); // xpected-warning{{1 uninitialized field}}
+}
+
+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 = 420;
+  UnionPointerTest1(&u, int());
+}
+
+class UnionPointerTest2 {
+public:
+  union SimpleUnion {
+    float uf;
+    int ui;
+    char uc;
+  };
+
+private:
+  SimpleUnion *uptr; // xpected-note{{uninitialized pointee 'this->uptr'}}
+
+public:
+  UnionPointerTest2(SimpleUnion *uptr, char) : uptr(uptr) {}
+};
+
+void f39() {
+  UnionPointerTest2::SimpleUnion u;
+  UnionPointerTest2(&u, int()); // xpected-warning{{1 uninitialized field}}
+}
+
+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{420, 420};
+    // All good!
+  }
+};
+
+void f41() {
+  ContainsUnionWithRecordTest1();
+}
+
+class ContainsUnionWithRecordTest3 {
+  union UnionWithRecord {
+    struct RecordType {
+      int x;
+      int y;
+    } us;
+    double ud;
+    long ul;
+
+    UnionWithRecord(){};
+  } u; // xpected-note{{uninitialized field 'this->u'}}
+
+public:
+  ContainsUnionWithRecordTest3() {
+    UnionWithRecord::RecordType rec;
+    rec.x = 420;
+    u.us = rec; // xpected-warning{{1 uninitialized field}}
+  }
+};
+
+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;
+  } u; // xpected-note{{uninitialized field 'this->u'}}
+
+public:
+  ContainsUnionWithSimpleUnionTest2() {}
+};
+
+void f44() {
+  ContainsUnionWithSimpleUnionTest2(); // xpected-warning{{1 uninitialized field}}
+}
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(420) {}
+};
+
+class NonPolymorphicInheritanceTest1 : public NonPolymorphicLeft1 {
+  int z;
+
+public:
+  NonPolymorphicInheritanceTest1()
+      : NonPolymorphicLeft1(int{}) {
+    y = 420;
+    z = 420;
+    // All good!
+  }
+};
+
+void f00() {
+  NonPolymorphicInheritanceTest1();
+}
+
+class NonPolymorphicRight1 {
+  int x; // expected-note{{uninitialized field 'this->x'}}
+protected:
+  int y;
+
+public:
+  NonPolymorphicRight1() = default;
+  NonPolymorphicRight1(int) : x(420) {}
+};
+
+class NonPolymorphicInheritanceTest2 : public NonPolymorphicRight1 {
+  int z;
+
+public:
+  NonPolymorphicInheritanceTest2() {
+    y = 420;
+    z = 420; // 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(420) {}
+};
+
+class NonPolymorphicInheritanceTest3 : public NonPolymorphicBaseClass3 {
+  int z;
+
+public:
+  NonPolymorphicInheritanceTest3()
+      : NonPolymorphicBaseClass3(int{}) {
+    z = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f02() {
+  NonPolymorphicInheritanceTest3();
+}
+
+class NonPolymorphicBaseClass4 {
+  int x;
+
+protected:
+  int y;
+
+public:
+  NonPolymorphicBaseClass4() = default;
+  NonPolymorphicBaseClass4(int) : x(420) {}
+};
+
+class NonPolymorphicInheritanceTest4 : public NonPolymorphicBaseClass4 {
+  int z; // expected-note{{uninitialized field 'this->z'}}
+
+public:
+  NonPolymorphicInheritanceTest4()
+      : NonPolymorphicBaseClass4(int{}) {
+    y = 420; // 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(420) {}
+};
+
+class PolymorphicInheritanceTest1 : public PolymorphicLeft1 {
+  int z;
+
+public:
+  PolymorphicInheritanceTest1()
+      : PolymorphicLeft1(int{}) {
+    y = 420;
+    z = 420;
+    // 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(420) {}
+};
+
+class PolymorphicInheritanceTest2 : public PolymorphicRight1 {
+  int z;
+
+public:
+  PolymorphicInheritanceTest2() {
+    y = 420;
+    z = 420; // 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(420) {}
+};
+
+class PolymorphicInheritanceTest3 : public PolymorphicBaseClass3 {
+  int z;
+
+public:
+  PolymorphicInheritanceTest3()
+      : PolymorphicBaseClass3(int{}) {
+    z = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f06() {
+  PolymorphicInheritanceTest3();
+}
+
+class PolymorphicBaseClass4 {
+  int x;
+
+protected:
+  int y;
+
+public:
+  virtual ~PolymorphicBaseClass4() = default;
+  PolymorphicBaseClass4() = default;
+  PolymorphicBaseClass4(int) : x(420) {}
+};
+
+class PolymorphicInheritanceTest4 : public PolymorphicBaseClass4 {
+  int z; // expected-note{{uninitialized field 'this->z'}}
+
+public:
+  PolymorphicInheritanceTest4()
+      : PolymorphicBaseClass4(int{}) {
+    y = 420; // 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(420) {}
+};
+
+class VirtualInheritanceTest1 : virtual public VirtualPolymorphicLeft1 {
+  int z;
+
+public:
+  VirtualInheritanceTest1()
+      : VirtualPolymorphicLeft1(int()) {
+    y = 420;
+    z = 420;
+    // 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(420) {}
+};
+
+class VirtualInheritanceTest2 : virtual public VirtualPolymorphicRight1 {
+  int z;
+
+public:
+  VirtualInheritanceTest2() {
+    y = 420;
+    z = 420; // 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(420) {}
+};
+
+class VirtualInheritanceTest3 : virtual public VirtualPolymorphicBaseClass3 {
+  int z;
+
+public:
+  VirtualInheritanceTest3()
+      : VirtualPolymorphicBaseClass3(int{}) {
+    z = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f10() {
+  VirtualInheritanceTest3();
+}
+
+//===----------------------------------------------------------------------===//
+// Multiple inheritance tests
+//===----------------------------------------------------------------------===//
+
+/*
+        Left        Right
+          \           /
+           \         /
+            \       /
+     MultipleInheritanceTest
+*/
+
+struct Left1 {
+  int x;
+  Left1() = default;
+  Left1(int) : x(420) {}
+};
+struct Right1 {
+  int y;
+  Right1() = default;
+  Right1(int) : y(420) {}
+};
+
+class MultipleInheritanceTest1 : public Left1, public Right1 {
+  int z;
+
+public:
+  MultipleInheritanceTest1()
+      : Left1(int{}),
+        Right1(char{}) {
+    z = 420;
+    // All good!
+  }
+
+  MultipleInheritanceTest1(int)
+      : Left1(int{}) {
+    y = 420;
+    z = 420;
+    // All good!
+  }
+
+  MultipleInheritanceTest1(int, int)
+      : Right1(char{}) {
+    x = 420;
+    z = 420;
+    // All good!
+  }
+};
+
+void f11() {
+  MultipleInheritanceTest1();
+  MultipleInheritanceTest1(int());
+  MultipleInheritanceTest1(int(), int());
+}
+
+struct Left2 {
+  int x;
+  Left2() = default;
+  Left2(int) : x(420) {}
+};
+struct Right2 {
+  int y; // expected-note{{uninitialized field 'this->y'}}
+  Right2() = default;
+  Right2(int) : y(420) {}
+};
+
+class MultipleInheritanceTest2 : public Left2, public Right2 {
+  int z;
+
+public:
+  MultipleInheritanceTest2()
+      : Left2(int{}) {
+    z = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f12() {
+  MultipleInheritanceTest2();
+}
+
+struct Left3 {
+  int x; // expected-note{{uninitialized field 'this->x'}}
+  Left3() = default;
+  Left3(int) : x(420) {}
+};
+struct Right3 {
+  int y;
+  Right3() = default;
+  Right3(int) : y(420) {}
+};
+
+class MultipleInheritanceTest3 : public Left3, public Right3 {
+  int z;
+
+public:
+  MultipleInheritanceTest3()
+      : Right3(char{}) {
+    z = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f13() {
+  MultipleInheritanceTest3();
+}
+
+struct Left4 {
+  int x;
+  Left4() = default;
+  Left4(int) : x(420) {}
+};
+struct Right4 {
+  int y;
+  Right4() = default;
+  Right4(int) : y(420) {}
+};
+
+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(420) {}
+};
+struct Right5 {
+  int y; // expected-note{{uninitialized field 'this->y'}}
+  Right5() = default;
+  Right5(int) : y(420) {}
+};
+
+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(420) {}
+};
+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 = 420;
+    // All good!
+  }
+
+  NonVirtualDiamondInheritanceTest1(int)
+      : First1(int{}) {
+    Second1::x = 420;
+    z = 420;
+    // All good!
+  }
+
+  NonVirtualDiamondInheritanceTest1(int, int)
+      : Second1(int{}) {
+    First1::x = 420;
+    z = 420;
+    // 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(420) {}
+};
+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 = 420; // expected-warning{{1 uninitialized field}}
+  }
+};
+
+void f17() {
+  NonVirtualDiamondInheritanceTest2();
+}
+
+struct NonVirtualBase3 {
+  int x; // expected-note{{uninitialized field 'this->x'}}
+  NonVirtualBase3() = default;
+  NonVirtualBase3(int) : x(420) {}
+};
+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 = 420; // 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(420) {}
+};
+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 = 420; // expected-warning{{2 uninitialized fields}}
+  }
+};
+
+void f19() {
+  NonVirtualDiamondInheritanceTest4();
+}
+
+struct NonVirtualBase5 {
+  int x;
+  NonVirtualBase5() = default;
+  NonVirtualBase5(int) : x(420) {}
+};
+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(420) {}
+};
+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(420) {}
+};
+struct VirtualFirst1 : virtual public VirtualBase1 {
+  VirtualFirst1() = default;
+  VirtualFirst1(int) : VirtualBase1(int{}) {}
+  VirtualFirst1(int, int) { x = 420; }
+};
+struct VirtualSecond1 : virtual public VirtualBase1 {
+  VirtualSecond1() = default;
+  VirtualSecond1(int) : VirtualBase1(int{}) {}
+  VirtualSecond1(int, int) { x = 420; }
+};
+
+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(420) {}
+};
+struct VirtualFirst2 : virtual public VirtualBase2 {
+  VirtualFirst2() = default;
+  VirtualFirst2(int) : VirtualBase2(int{}) {}
+  VirtualFirst2(int, int) { x = 420; }
+};
+struct VirtualSecond2 : virtual public VirtualBase2 {
+  VirtualSecond2() = default;
+  VirtualSecond2(int) : VirtualBase2(int{}) {}
+  VirtualSecond2(int, int) { x = 420; }
+};
+
+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(420) {}
+};
+struct VirtualFirst3 : virtual public VirtualBase3 {
+  VirtualFirst3() = default;
+  VirtualFirst3(int) : VirtualBase3(int{}) {}
+  VirtualFirst3(int, int) { x = 420; }
+};
+struct VirtualSecond3 : virtual public VirtualBase3 {
+  VirtualSecond3() = default;
+  VirtualSecond3(int) : VirtualBase3(int{}) {}
+  VirtualSecond3(int, int) { x = 420; }
+};
+
+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,523 @@
+//=======- 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 checks cunstructors for possibly
+//  uninitialized fields.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <algorithm>
+#include <vector>
+
+using namespace clang;
+using namespace clang::ento;
+
+namespace {
+
+/// 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:
+  void push_back(const FieldRegion *FR) { Chain.push_back(FR); }
+  llvm::iterator_range<FieldChain::const_iterator> fieldRegions() const {
+    return llvm::iterator_range<FieldChain::const_iterator>(Chain);
+  }
+
+  bool isPointer() const {
+    return Chain.back()->getDecl()->getType()->isPointerType();
+  }
+  bool isDereferenced() const;
+  void dereference() { IsDereferenced = true; }
+  const FieldDecl *getEndOfChain() const { return Chain.back()->getDecl(); }
+  StringRef getAsString(SmallString<200> &Buf) const;
+  friend class FieldChainInfoComparator;
+};
+
+struct FieldChainInfoComparator {
+  bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) {
+    return lhs.Chain.back() < rhs.Chain.back();
+  }
+};
+
+using UninitFieldSet = std::set<FieldChainInfo, FieldChainInfoComparator>;
+
+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;
+  void reportUninitFields(CheckerContext &Context,
+                          const UninitFieldSet &UninitFields) const;
+};
+
+/// 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(Optional<FieldChainInfo> Chain, const FieldDecl *Field,
+                         const FieldRegion *Region);
+
+  // 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
+  //     - non-union record
+  //     - pointer/reference
+  //     - fundamental object (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 (pointed value 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,
+                        const Optional<FieldChainInfo> &Chain = None);
+  bool isPointerOrReferenceUninit(const SVal &V, const FieldRegion *FR,
+                                  const FieldDecl *I,
+                                  const Optional<FieldChainInfo> &Chain = None);
+  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.
+  reportUninitFields(Context, F.getUninitFields());
+}
+
+void CtorUninitializedMemberChecker::reportUninitFields(
+    CheckerContext &Context, const UninitFieldSet &UninitFields) const {
+
+  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;
+    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 << FieldChain.getAsString(FieldChainBuf);
+    NoteOS << "'";
+
+    Report->addNote(NoteOS.str(),
+                    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) {}
+
+bool FindUninitializedFields::isFullyInitialized() {
+  bool ret = isNonUnionUninit(ObjectR, FieldChainInfo());
+  IsChecked = true;
+  return ret;
+}
+
+const UninitFieldSet &FindUninitializedFields::getUninitFields() {
+  if (!IsChecked)
+    isFullyInitialized();
+  return UninitFields;
+}
+
+// If Chain's value is None, that means that this function was called to
+// determine whether a union has any initialized fields. In this case we're not
+// collecting fields, we'd only like to know  whether the value contained in
+// region R is initialized.
+bool FindUninitializedFields::isNonUnionUninit(
+    const TypedValueRegion *R, const Optional<FieldChainInfo> &Chain) {
+
+  const RecordDecl *RD =
+      R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition();
+  assert(RD && "Referred record has no definition");
+
+  bool ContainsUninitField = false;
+  Optional<FieldChainInfo> LocalChain = Chain;
+
+  // 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 (T->isStructureOrClassType()) {
+      if (LocalChain)
+        LocalChain->push_back(FR);
+      if (isNonUnionUninit(FR, LocalChain))
+        ContainsUninitField = true;
+      continue;
+    }
+
+    if (T->isUnionType()) {
+      if (isUnionUninit(FR)) {
+        addFieldToUninits(LocalChain, I, FR);
+        ContainsUninitField = true;
+      }
+      continue;
+    }
+
+    // At this point the field is a fundamental object, pointer or reference.
+    SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR));
+
+    // If this is a pointer or reference type.
+    if (auto LocType = V.getAs<Loc>()) {
+      if (isPointerOrReferenceUninit(V, FR, I, LocalChain))
+        ContainsUninitField = true;
+      continue;
+    }
+
+    // At this point the field is a fundamental type.
+    if (isFundamentalUninit(V)) {
+      addFieldToUninits(LocalChain, I, 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;
+}
+
+// TODO As of writing this checker, there is very little support for unions in
+// the CSA. This function relies on a nonexisting implementation by assuming as
+// little about it as possible.
+bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
+  const RecordDecl *RD =
+      R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition();
+  assert(RD && "Referred record has no definition");
+
+  bool ContainsInitField = false;
+  auto It = RD->field_begin(), End = RD->field_end();
+
+  // Does this union contain at least one initalized field?
+  while (It != End && !ContainsInitField) {
+
+    const FieldRegion *FR = MrMgr.getFieldRegion(*It, R);
+    QualType T = (*It)->getType();
+
+    if (T->isStructureOrClassType())
+      ContainsInitField = !isNonUnionUninit(FR);
+
+    else if (T->isUnionType())
+      ContainsInitField = !isUnionUninit(FR);
+
+    else {
+      SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR));
+
+      if (auto LocType = V.getAs<Loc>())
+        ContainsInitField = !isPointerOrReferenceUninit(V, FR, *It);
+      else
+        ContainsInitField = !isFundamentalUninit(V);
+    }
+
+    ++It;
+  }
+  return !ContainsInitField;
+}
+
+bool FindUninitializedFields::isPointerOrReferenceUninit(
+    const SVal &V, const FieldRegion *FR, const FieldDecl *FD,
+    const Optional<FieldChainInfo> &Chain) {
+
+  if (V.isUnknown() || V.isZeroConstant())
+    return false;
+
+  if (V.isUndef()) {
+    addFieldToUninits(Chain, FD, FR);
+    return true;
+  }
+
+  if (isVoidPointer(FD))
+    return false;
+
+  Optional<FieldChainInfo> LocalChain = Chain;
+
+  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 *RT = 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 (RT->getSymbolicBase())
+      return false;
+
+    const auto *T = RT->getValueType()->getAs<RecordType>();
+
+    if (T->isStructureOrClassType()) {
+      if (LocalChain)
+        LocalChain->push_back(FR);
+      return isNonUnionUninit(RT, LocalChain);
+    }
+
+    if (T->isUnionType()) {
+      // TODO does control ever reach here?
+      if (isUnionUninit(RT)) {
+        if (LocalChain)
+          LocalChain->dereference();
+        addFieldToUninits(LocalChain, FD, FR);
+        return true;
+      }
+    }
+  }
+
+  // At this point V is a pointer pointing to a fundamental type.
+  if (isFundamentalUninit(DerefdV)) {
+    if (LocalChain)
+      LocalChain->dereference();
+    addFieldToUninits(LocalChain, FD, FR);
+    return true;
+  }
+
+  return false;
+}
+
+bool FindUninitializedFields::isFundamentalUninit(const SVal &V) {
+  return V.isUndef();
+}
+
+void FindUninitializedFields::addFieldToUninits(Optional<FieldChainInfo> Chain,
+                                                const FieldDecl *Field,
+                                                const FieldRegion *Region) {
+  if (!Chain)
+    return;
+
+  Chain->push_back(Region);
+  UninitFields.insert(std::move(*Chain));
+}
+
+//===----------------------------------------------------------------------===//
+//                       Methods for FieldChainInfo.
+//===----------------------------------------------------------------------===//
+
+bool FieldChainInfo::isDereferenced() const {
+  assert(isPointer() && "Only pointers may or may not be dereferenced!");
+  return IsDereferenced;
+}
+
+StringRef FieldChainInfo::getAsString(SmallString<200> &Buf) const {
+  llvm::raw_svector_ostream OS(Buf);
+  for (const FieldRegion *FR : Chain) {
+    const FieldDecl *Field = FR->getDecl();
+    OS << Field->getNameAsString();
+    OS << (Field->getType()->isPointerType() ? "->" : ".");
+  }
+
+  // Remove last "->" or "." from the end of the string.
+  if (Chain.back()->getDecl()->getType()->isPointerType())
+    return OS.str().drop_back(2);
+  return OS.str().drop_back(1);
+}
+
+//===----------------------------------------------------------------------===//
+//                           Utility functions.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+bool isVoidPointer(const FieldDecl *FD) {
+  QualType T = FD->getType();
+  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 'this'
+  SVal This = Context.getState()->getSVal(ThisLoc);
+
+  // getting '*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
@@ -314,6 +314,10 @@
               "object will be reported">,
      DescFile<"MisusedMovedObjectChecker.cpp">;
 
+def CtorUninitializedMemberChecker: Checker<"CtorUninitializedMember">,
+     HelpText<"Reports uninitialized members in constructors">,
+     DescFile<"CtorUninitializedMemberChecker.cpp">;
+
 } // end: "alpha.cplusplus"
 
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to