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

Repository:
  rC Clang

https://reviews.llvm.org/D49228

Files:
  lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
  test/Analysis/cxx-uninitialized-object-ptr-ref.cpp

Index: test/Analysis/cxx-uninitialized-object-ptr-ref.cpp
===================================================================
--- test/Analysis/cxx-uninitialized-object-ptr-ref.cpp
+++ test/Analysis/cxx-uninitialized-object-ptr-ref.cpp
@@ -287,7 +287,7 @@
 }
 
 struct IntDynTypedVoidPointerTest1 {
-  void *vptr; // expected-note{{uninitialized pointee 'this->vptr'}}
+  void *vptr; // expected-note{{uninitialized pointee 'this->static_cast<int *>(vptr)'}}
   int dontGetFilteredByNonPedanticMode = 0;
 
   IntDynTypedVoidPointerTest1(void *vptr) : vptr(vptr) {} // expected-warning{{1 uninitialized field}}
@@ -300,8 +300,8 @@
 
 struct RecordDynTypedVoidPointerTest {
   struct RecordType {
-    int x; // expected-note{{uninitialized field 'this->vptr->x'}}
-    int y; // expected-note{{uninitialized field 'this->vptr->y'}}
+    int x; // expected-note{{uninitialized field 'this->static_cast<struct RecordDynTypedVoidPointerTest::RecordType *>(vptr)->x'}}
+    int y; // expected-note{{uninitialized field 'this->static_cast<struct RecordDynTypedVoidPointerTest::RecordType *>(vptr)->y'}}
   };
 
   void *vptr;
@@ -317,9 +317,9 @@
 
 struct NestedNonVoidDynTypedVoidPointerTest {
   struct RecordType {
-    int x;      // expected-note{{uninitialized field 'this->vptr->x'}}
-    int y;      // expected-note{{uninitialized field 'this->vptr->y'}}
-    void *vptr; // expected-note{{uninitialized pointee 'this->vptr->vptr'}}
+    int x;      // expected-note{{uninitialized field 'this->static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(vptr)->x'}}
+    int y;      // expected-note{{uninitialized field 'this->static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(vptr)->y'}}
+    void *vptr; // expected-note{{uninitialized pointee 'this->static_cast<struct NestedNonVoidDynTypedVoidPointerTest::RecordType *>(vptr)->static_cast<char *>(vptr)'}}
   };
 
   void *vptr;
Index: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
@@ -50,31 +50,60 @@
   void checkEndFunction(CheckerContext &C) const;
 };
 
-llvm::ImmutableListFactory<const FieldRegion *> Factory;
-
 /// 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.
 ///
 /// Note that this class is immutable, and new fields may only be added through
 /// constructor calls.
 class FieldChainInfo {
+  using FieldChainImpl = llvm::ImmutableListImpl<const FieldRegion *>;
+
+  // If the fieldchain has void pointers or nonloc::LocAsPointer objects,
+  // they have to be casted back in order to dereference them.
+  //
+  //   struct B {
+  //     void *ptr;
+  //     B(void *ptr) : ptr(ptr) {}
+  //   };
+  //   struct A { int x; } a; // say this isn't zero initialized
+  //
+  //   B b(&a); // warning
+  //
+  // For cases like this, we can't report that 'this->ptr->a' is uninitialized,
+  // since void pointers can't be dereferenced, but rather the note message
+  // should say 'static_cast<A *>(this->ptr)->a' is uninitialized.
+  //
+  // We'll store which elements of the fieldchain needs to be casted back,
+  // and the type it needs to be casted to.
+  using CastBack = const std::pair<QualType, const FieldChainImpl *>;
+  using CastBacksListImpl = llvm::ImmutableListImpl<CastBack>;
+
+public:
+  // We'll expose these types for a nicer looking factory instantionation.
   using FieldChain = llvm::ImmutableList<const FieldRegion *>;
+  using CastBackList = llvm::ImmutableList<CastBack>;
 
+private:
   FieldChain Chain;
+  CastBackList CastBacks;
 
   const bool IsDereferenced = false;
 
 public:
   FieldChainInfo() = default;
 
   FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
-      : Chain(Other.Chain), IsDereferenced(IsDereferenced) {}
+      : Chain(Other.Chain), CastBacks(Other.CastBacks),
+        IsDereferenced(IsDereferenced) {}
 
   FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR,
                  const bool IsDereferenced = false);
 
+  FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR,
+                 const bool IsDereferenced, QualType CastBackType);
+
   bool contains(const FieldRegion *FR) const { return Chain.contains(FR); }
   bool isPointer() const;
 
@@ -90,8 +119,8 @@
   /// elements in reverse order, and have no reverse iterators, we use a
   /// recursive function to print the fieldchain correctly. The last element in
   /// the chain is to be printed by `print`.
-  static void printTail(llvm::raw_ostream &Out,
-                        const llvm::ImmutableListImpl<const FieldRegion *> *L);
+  static void printTail(llvm::raw_ostream &Out, const FieldChainImpl *L,
+                        const CastBacksListImpl *C);
   friend struct FieldChainInfoComparator;
 };
 
@@ -103,6 +132,9 @@
   }
 };
 
+FieldChainInfo::FieldChain::Factory ChainFactory;
+FieldChainInfo::CastBackList::Factory CastBackFactory;
+
 using UninitFieldSet = std::set<FieldChainInfo, FieldChainInfoComparator>;
 
 /// Searches for and stores uninitialized fields in a non-union object.
@@ -482,6 +514,9 @@
   }
 
   QualType DynT = DynTInfo.getType();
+  // If the static type of the field is a void pointer, we need to cast it back
+  // to the dynamic type before dereferencing.
+  bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType());
 
   if (isVoidPointer(DynT)) {
     IsAnyFieldInitialized = true;
@@ -521,11 +556,18 @@
 
     const TypedValueRegion *R = RecordV->getRegion();
 
-    if (DynT->getPointeeType()->isStructureOrClassType())
+    if (DynT->getPointeeType()->isStructureOrClassType()) {
+      if (NeedsCastBack)
+        return isNonUnionUninit(
+            R, {LocalChain, FR, /*IsDereferenced */ false, DynT});
       return isNonUnionUninit(R, {LocalChain, FR});
+    }
 
     if (DynT->getPointeeType()->isUnionType()) {
       if (isUnionUninit(R)) {
+        if (NeedsCastBack)
+          return addFieldToUninits(
+              {LocalChain, FR, /*IsDereferenced*/ true, DynT});
         return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
       } else {
         IsAnyFieldInitialized = true;
@@ -546,8 +588,11 @@
          "At this point FR must either have a primitive dynamic type, or it "
          "must be a null, undefined, unknown or concrete pointer!");
 
-  if (isPrimitiveUninit(DerefdV))
+  if (isPrimitiveUninit(DerefdV)) {
+    if (NeedsCastBack)
+      return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true, DynT});
     return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
+  }
 
   IsAnyFieldInitialized = true;
   return false;
@@ -570,7 +615,16 @@
     : FieldChainInfo(Other, IsDereferenced) {
   assert(!contains(FR) && "Can't add a field that is already a part of the "
                           "fieldchain! Is this a cyclic reference?");
-  Chain = Factory.add(FR, Other.Chain);
+  Chain = ChainFactory.add(FR, Other.Chain);
+}
+
+FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other,
+                               const FieldRegion *FR, const bool IsDereferenced,
+                               QualType CastBackType)
+    : FieldChainInfo(Other, FR, IsDereferenced) {
+  CastBacks = CastBackFactory.add(
+      std::make_pair(CastBackType, Chain.getInternalPointer()),
+      Other.CastBacks);
 }
 
 bool FieldChainInfo::isPointer() const {
@@ -588,25 +642,6 @@
   return (*Chain.begin())->getDecl();
 }
 
-// TODO: This function constructs an incorrect string if a void pointer is a
-// part of the chain:
-//
-//   struct B { int x; }
-//
-//   struct A {
-//     void *vptr;
-//     A(void* vptr) : vptr(vptr) {}
-//   };
-//
-//   void f() {
-//     B b;
-//     A a(&b);
-//   }
-//
-// The note message will be "uninitialized field 'this->vptr->x'", even though
-// void pointers can't be dereferenced. This should be changed to "uninitialized
-// field 'static_cast<B*>(this->vptr)->x'".
-//
 // TODO: This function constructs an incorrect fieldchain string in the
 // following case:
 //
@@ -641,21 +676,45 @@
   if (Chain.isEmpty())
     return;
 
-  const llvm::ImmutableListImpl<const FieldRegion *> *L =
-      Chain.getInternalPointer();
-  printTail(Out, L->getTail());
-  Out << L->getHead()->getDecl()->getNameAsString();
+  const FieldChainImpl *L = Chain.getInternalPointer();
+  const CastBacksListImpl *C = CastBacks.getInternalPointer();
+
+  // During the construction of a FieldChainInfo object, the CastBacks field
+  // will be filled in order with pointers to certain elements of the FieldChain
+  // that needs to be casted to another type.
+  // If L is the first element stored in CastBacks, we'll pass the rest of the
+  // CastBacks list to printTail. If not, we know that the first element of it
+  // will refer to another field in the fieldchain, unless no field needs to be
+  // casted back.
+  if (C && L == C->getHead().second) {
+    printTail(Out, L->getTail(), C->getTail());
+    Out << "static_cast<" << C->getHead().first.getAsString() << ">("
+        << L->getHead()->getDecl()->getNameAsString() << ')';
+  } else {
+    printTail(Out, L->getTail(), C);
+    Out << L->getHead()->getDecl()->getNameAsString();
+  }
 }
 
-void FieldChainInfo::printTail(
-    llvm::raw_ostream &Out,
-    const llvm::ImmutableListImpl<const FieldRegion *> *L) {
-  if (!L)
+void FieldChainInfo::printTail(llvm::raw_ostream &Out, const FieldChainImpl *L,
+                               const CastBacksListImpl *C) {
+  if (!L) {
+    assert(!C && "Entire fieldchain printed but some elements of CastBacks is "
+                 "unused!");
     return;
+  }
+
+  if (C && L == C->getHead().second)
+    printTail(Out, L->getTail(), C->getTail());
+  else
+    printTail(Out, L->getTail(), C);
 
-  printTail(Out, L->getTail());
   const FieldDecl *Field = L->getHead()->getDecl();
-  Out << Field->getNameAsString();
+  if (C && L == C->getHead().second)
+    Out << "static_cast<" << C->getHead().first.getAsString() << ">("
+        << L->getHead()->getDecl()->getNameAsString() << ')';
+  else
+    Out << Field->getNameAsString();
   Out << (Field->getType()->isPointerType() ? "->" : ".");
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to