]> granicus.if.org Git - clang/commitdiff
[analyzer] Handle zeroing CXXConstructExprs.
authorJordan Rose <jordan_rose@apple.com>
Tue, 25 Jun 2013 01:56:08 +0000 (01:56 +0000)
committerJordan Rose <jordan_rose@apple.com>
Tue, 25 Jun 2013 01:56:08 +0000 (01:56 +0000)
Re-apply r184511, reverted in r184561, with the trivial default constructor
fast path removed -- it turned out not to be necessary here.

Certain expressions can cause a constructor invocation to zero-initialize
its object even if the constructor itself does no initialization. The
analyzer now handles that before evaluating the call to the constructor,
using the same "default binding" mechanism that calloc() uses, rather
than simply ignoring the zero-initialization flag.

<rdar://problem/14212563>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@184815 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
test/Analysis/ctor.mm [moved from test/Analysis/ctor-inlining.mm with 83% similarity]

index 84f96349f74dae07ff700de3f427359278533c21..3f16c62492263d33f0c24f52c8c239bbf1df0762 100644 (file)
@@ -176,6 +176,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
       }
 
       // FIXME: This will eventually need to handle new-expressions as well.
+      // Don't forget to update the pre-constructor initialization code below.
     }
 
     // If we couldn't find an existing region to construct into, assume we're
@@ -233,8 +234,38 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
 
   ExplodedNodeSet DstPreVisit;
   getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+
+  ExplodedNodeSet PreInitialized;
+  {
+    StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
+    if (CE->requiresZeroInitialization()) {
+      // Type of the zero doesn't matter.
+      SVal ZeroVal = svalBuilder.makeZeroVal(getContext().CharTy);
+
+      for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
+                                     E = DstPreVisit.end();
+           I != E; ++I) {
+        ProgramStateRef State = (*I)->getState();
+        // FIXME: Once we properly handle constructors in new-expressions, we'll
+        // need to invalidate the region before setting a default value, to make
+        // sure there aren't any lingering bindings around. This probably needs
+        // to happen regardless of whether or not the object is zero-initialized
+        // to handle random fields of a placement-initialized object picking up
+        // old bindings. We might only want to do it when we need to, though.
+        // FIXME: This isn't actually correct for arrays -- we need to zero-
+        // initialize the entire array, not just the first element -- but our
+        // handling of arrays everywhere else is weak as well, so this shouldn't
+        // actually make things worse. Placement new makes this tricky as well,
+        // since it's then possible to be initializing one part of a multi-
+        // dimensional array.
+        State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal);
+        Bldr.generateNode(CE, *I, State, /*tag=*/0, ProgramPoint::PreStmtKind);
+      }
+    }
+  }
+
   ExplodedNodeSet DstPreCall;
-  getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit,
+  getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized,
                                             *Call, *this);
 
   ExplodedNodeSet DstEvaluated;
similarity index 83%
rename from test/Analysis/ctor-inlining.mm
rename to test/Analysis/ctor.mm
index 9eb9888e935fb3bbd3cf83b59214c7ec50a82588..16ad9d10030727694fe41cfe207b994a464e585f 100644 (file)
@@ -534,3 +534,94 @@ namespace VirtualInheritance {
     clang_analyzer_eval(counter == 1); // expected-warning{{TRUE}}
   }
 }
+
+namespace ZeroInitialization {
+  struct raw_pair {
+    int p1;
+    int p2;
+  };
+
+  void testVarDecl() {
+    raw_pair p{};
+    clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
+  }
+
+  void testTemporary() {
+    clang_analyzer_eval(raw_pair().p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(raw_pair().p2 == 0); // expected-warning{{TRUE}}
+  }
+
+  void testArray() {
+    raw_pair p[2] = {};
+    clang_analyzer_eval(p[0].p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p[0].p2 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p[1].p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p[1].p2 == 0); // expected-warning{{TRUE}}
+  }
+
+  void testNew() {
+    // FIXME: Pending proper implementation of constructors for 'new'.
+    raw_pair *pp = new raw_pair();
+    clang_analyzer_eval(pp->p1 == 0); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(pp->p2 == 0); // expected-warning{{UNKNOWN}}
+  }
+
+  void testArrayNew() {
+    // FIXME: Pending proper implementation of constructors for 'new[]'.
+    raw_pair *p = new raw_pair[2]();
+    clang_analyzer_eval(p[0].p1 == 0); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(p[0].p2 == 0); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(p[1].p1 == 0); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(p[1].p2 == 0); // expected-warning{{UNKNOWN}}
+  }
+
+  struct initializing_pair {
+  public:
+    int x;
+    raw_pair y;
+    initializing_pair() : x(), y() {}
+  };
+  
+  void testFieldInitializers() {
+    initializing_pair p;
+    clang_analyzer_eval(p.x == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p.y.p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p.y.p2 == 0); // expected-warning{{TRUE}}
+  }
+
+  struct subclass : public raw_pair {
+    subclass() = default;
+  };
+
+  void testSubclass() {
+    subclass p;
+    clang_analyzer_eval(p.p1 == 0); // expected-warning{{garbage}}
+  }
+
+  struct initializing_subclass : public raw_pair {
+    initializing_subclass() : raw_pair() {}
+  };
+
+  void testInitializingSubclass() {
+    initializing_subclass p;
+    clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
+  }
+
+  struct pair_wrapper {
+    pair_wrapper() : p() {}
+    raw_pair p;
+  };
+
+  struct virtual_subclass : public virtual pair_wrapper {
+    virtual_subclass() {}
+  };
+
+  struct double_virtual_subclass : public virtual_subclass {
+    double_virtual_subclass() {
+      // This previously caused a crash because the pair_wrapper subobject was
+      // initialized twice.
+    }
+  };
+}