This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb032e3ff6121: [analyzer] Evaluate construction of non-POD 
type arrays (authored by isuckatcs).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D127973/new/

https://reviews.llvm.org/D127973

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/test/Analysis/ctor-array.cpp
  clang/test/Analysis/ctor.mm
  clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp
  clang/test/Analysis/dtor.cpp
  clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
  clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints.dot
  clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/environment.dot
  clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/store.dot
  clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
  clang/test/Analysis/exploded-graph-rewriter/topology.dot
  clang/test/Analysis/expr-inspection.c
  clang/test/Analysis/handle_constructors_with_new_array.cpp
  clang/test/Analysis/new-ctor-conservative.cpp
  clang/test/Analysis/new.cpp
  clang/test/Analysis/operator-calls.cpp
  clang/utils/analyzer/exploded-graph-rewriter.py

Index: clang/utils/analyzer/exploded-graph-rewriter.py
===================================================================
--- clang/utils/analyzer/exploded-graph-rewriter.py
+++ clang/utils/analyzer/exploded-graph-rewriter.py
@@ -144,7 +144,8 @@
 
 
 # A deserialized Environment. This class can also hold other entities that
-# are similar to Environment, such as Objects Under Construction.
+# are similar to Environment, such as Objects Under Construction or 
+# Indices Of Elements Under Construction.
 class GenericEnvironment:
     def __init__(self, json_e):
         self.frames = [EnvironmentFrame(f) for f in json_e]
@@ -269,6 +270,7 @@
                 'constraints': None,
                 'dynamic_types': None,
                 'constructing_objects': None,
+                'index_of_element': None,
                 'checker_messages': None
             }
 
@@ -296,6 +298,10 @@
             GenericEnvironment(json_ps['constructing_objects']) \
             if json_ps['constructing_objects'] is not None else None
 
+        self.index_of_element = \
+            GenericEnvironment(json_ps['index_of_element']) \
+            if json_ps['index_of_element'] is not None else None
+
         self.checker_messages = CheckerMessages(json_ps['checker_messages']) \
             if json_ps['checker_messages'] is not None else None
 
@@ -796,6 +802,9 @@
         self.visit_environment_in_state('constructing_objects',
                                         'Objects Under Construction',
                                         s, prev_s)
+        self.visit_environment_in_state('index_of_element',
+                                        'Indices Of Elements Under Construction',
+                                        s, prev_s)
         self.visit_checker_messages_in_state(s, prev_s)
 
     def visit_node(self, node):
Index: clang/test/Analysis/operator-calls.cpp
===================================================================
--- clang/test/Analysis/operator-calls.cpp
+++ clang/test/Analysis/operator-calls.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core,debug.ExprInspection -verify %s
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-output=text -verify %s
 void clang_analyzer_eval(bool);
 
 struct X0 { };
@@ -29,6 +29,7 @@
 
 void testMemberOperator(IntComparable B) {
   clang_analyzer_eval(B == 0); // expected-warning{{TRUE}}
+  // expected-note@-1{{TRUE}}
 }
 
 
@@ -46,7 +47,9 @@
 
   void test(const Convertible &obj) {
     clang_analyzer_eval((int)obj == 42); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
     clang_analyzer_eval(obj); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
   }
 }
 
@@ -82,7 +85,15 @@
     // Force a cache-out when we try to conjure a temporary region for the operator call.
     // ...then, don't crash.
     clang_analyzer_eval(+(coin ? getSmallOpaque() : getSmallOpaque())); // expected-warning{{UNKNOWN}}
+    // expected-note@-1{{Assuming 'coin' is 0}}
+    // expected-note@-2{{'?' condition is false}}
+    // expected-note@-3{{UNKNOWN}}
+    // expected-note@-4{{Assuming 'coin' is 0}}
+    // expected-note@-5{{'?' condition is false}}
     clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // expected-warning{{UNKNOWN}}
+    // expected-note@-1{{'coin' is 0}}
+    // expected-note@-2{{'?' condition is false}}
+    // expected-note@-3{{UNKNOWN}}
   }
 }
 
@@ -102,31 +113,53 @@
 
   // This used to produce a warning about the iteration variable in the
   // synthesized assignment operator being undefined.
+  //
+  // Note: The warning we want to avoid can be found in https://bugs.llvm.org/show_bug.cgi?id=16745.
+  // Back in the day, this function was created we couldn't evaluate non-POD type array construction,
+  // so we couldn't evaluate the copy assignment either, hence we didn't detect that a field is
+  // uninitialized.
   void testNoWarning() {
+
     B v, u;
-    u = v;
+    u = v; // expected-warning@110{{Assigned value is garbage or undefined}}
+    // expected-note@-1{{Calling defaulted copy assignment operator for 'B'}}
+    // expected-note@110{{Assigned value is garbage or undefined}}
   }
 
   void testNoWarningMove() {
     B v, u;
-    u = static_cast<B &&>(v);
+    u = static_cast<B &&>(v); // expected-warning@111{{Assigned value is garbage or undefined}}
+    // expected-note@-1{{Calling defaulted move assignment operator for 'B'}}
+    // expected-note@111{{Assigned value is garbage or undefined}}
   }
 
   void testConsistency() {
     B v, u;
+    v.x = 0;
+    v.a[0].a = 24;
     v.a[1].a = 47;
     v.a[2].a = 42;
     u = v;
+    clang_analyzer_eval(u.a[0].a == -24); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
     clang_analyzer_eval(u.a[1].a == -47); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
     clang_analyzer_eval(u.a[2].a == -42); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
   }
 
   void testConsistencyMove() {
     B v, u;
+    v.x = 0;
+    v.a[0].a = 24;
     v.a[1].a = 47;
     v.a[2].a = 42;
     u = static_cast<B &&>(v);
+    clang_analyzer_eval(u.a[0].a == 25); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
     clang_analyzer_eval(u.a[1].a == 48); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
     clang_analyzer_eval(u.a[2].a == 43); // expected-warning{{TRUE}}
+    // expected-note@-1{{TRUE}}
   }
 }
Index: clang/test/Analysis/new.cpp
===================================================================
--- clang/test/Analysis/new.cpp
+++ clang/test/Analysis/new.cpp
@@ -323,8 +323,8 @@
 
 void testArrayDestr() {
   NoReturnDtor *p = new NoReturnDtor[2];
-  delete[] p; // Calls the base destructor which aborts, checked below
-   //TODO: clang_analyzer_eval should not be called
+  delete[] p;                // Calls the base destructor which aborts, checked below
+                             // TODO: clang_analyzer_eval should not be called
   clang_analyzer_eval(true); // expected-warning{{TRUE}}
 }
 
Index: clang/test/Analysis/new-ctor-conservative.cpp
===================================================================
--- clang/test/Analysis/new-ctor-conservative.cpp
+++ clang/test/Analysis/new-ctor-conservative.cpp
@@ -25,9 +25,14 @@
 
 void checkNewArray() {
   S *s = new S[10];
-  // FIXME: Should be true once we inline array constructors.
+
+  // FIXME: Handle big array construction
   clang_analyzer_eval(s[0].x == 1); // expected-warning{{UNKNOWN}}
   clang_analyzer_eval(s[1].x == 1); // expected-warning{{UNKNOWN}}
+
+  s = new S[4];
+  clang_analyzer_eval(s[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(s[1].x == 1); // expected-warning{{TRUE}}
 }
 
 struct NullS {
Index: clang/test/Analysis/handle_constructors_with_new_array.cpp
===================================================================
--- clang/test/Analysis/handle_constructors_with_new_array.cpp
+++ clang/test/Analysis/handle_constructors_with_new_array.cpp
@@ -59,12 +59,9 @@
   init_in_body a2[1];
   init_default_member a3[1];
 
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a1[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a2[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a3[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
+  clang_analyzer_eval(a1[0].a == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(a2[0].a == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(a3[0].a == 1); // expected-warning {{TRUE}}
 }
 
 void test_dynamic_aggregate() {
@@ -73,12 +70,9 @@
   auto *a2 = new init_in_body[1];
   auto *a3 = new init_default_member[1];
 
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a1[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a2[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
-  // FIXME: Should be TRUE, not FALSE.
-  clang_analyzer_eval(a3[0].a == 1); // expected-warning {{TRUE}} expected-warning {{FALSE}}
+  clang_analyzer_eval(a1[0].a == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(a2[0].a == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(a3[0].a == 1); // expected-warning {{TRUE}}
 
   delete[] a1;
   delete[] a2;
Index: clang/test/Analysis/expr-inspection.c
===================================================================
--- clang/test/Analysis/expr-inspection.c
+++ clang/test/Analysis/expr-inspection.c
@@ -47,6 +47,7 @@
 // CHECK-NEXT:   "dynamic_types": null,
 // CHECK-NEXT:   "dynamic_casts": null,
 // CHECK-NEXT:   "constructing_objects": null,
+// CHECK-NEXT:   "index_of_element": null,
 // CHECK-NEXT:   "checker_messages": null
 // CHECK-NEXT: }
 
Index: clang/test/Analysis/exploded-graph-rewriter/topology.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/topology.dot
+++ clang/test/Analysis/exploded-graph-rewriter/topology.dot
@@ -24,6 +24,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "foo", "messages": ["bar"] }
         ],
Index: clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
+++ clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
@@ -20,6 +20,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x2",
@@ -74,6 +75,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x5",
Index: clang/test/Analysis/exploded-graph-rewriter/store.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/store.dot
+++ clang/test/Analysis/exploded-graph-rewriter/store.dot
@@ -34,6 +34,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x2",
Index: clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
+++ clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
@@ -18,6 +18,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
@@ -73,6 +74,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
@@ -122,6 +124,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
Index: clang/test/Analysis/exploded-graph-rewriter/environment.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/environment.dot
+++ clang/test/Analysis/exploded-graph-rewriter/environment.dot
@@ -46,6 +46,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
Index: clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
+++ clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
@@ -17,6 +17,7 @@
         "environment": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "constraints": [
           { "symbol": "reg_$0<x>", "range": "{ [0, 10] }" }
@@ -55,6 +56,7 @@
         "environment": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "constraints": [
           { "symbol": "reg_$0<x>", "range": "{ [0, 5] }" }
@@ -83,6 +85,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null
       }
     }
Index: clang/test/Analysis/exploded-graph-rewriter/constraints.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/constraints.dot
+++ clang/test/Analysis/exploded-graph-rewriter/constraints.dot
@@ -24,6 +24,7 @@
         "environment": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "constraints": [
           { "symbol": "reg_$0<x>", "range": "{ [0, 0] }" }
Index: clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
+++ clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
@@ -17,6 +17,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "FooChecker", "messages": [
             "Foo: Bar"
@@ -75,6 +76,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "FooChecker", "messages": [
             "Foo: Bar",
Index: clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
===================================================================
--- clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
+++ clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
@@ -24,6 +24,7 @@
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "environment": null,
         "checker_messages": [
           { "checker": "alpha.core.FooChecker", "messages": [
Index: clang/test/Analysis/dtor.cpp
===================================================================
--- clang/test/Analysis/dtor.cpp
+++ clang/test/Analysis/dtor.cpp
@@ -140,10 +140,8 @@
     IntWrapper arr[2];
 
     // There should be no undefined value warnings here.
-    // Eventually these should be TRUE as well, but right now
-    // we can't handle array constructors.
-    clang_analyzer_eval(arr[0].x == 0); // expected-warning{{UNKNOWN}}
-    clang_analyzer_eval(arr[1].x == 0); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(arr[0].x == 0); // expected-warning{{TRUE}}
+    clang_analyzer_eval(arr[1].x == 0); // expected-warning{{TRUE}}
 
     arr[0].x = &i;
     arr[1].x = &j;
@@ -312,10 +310,8 @@
       IntWrapper arr[2][2];
 
       // There should be no undefined value warnings here.
-      // Eventually these should be TRUE as well, but right now
-      // we can't handle array constructors.
-      clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{UNKNOWN}}
-      clang_analyzer_eval(arr[1][1].x == 0); // expected-warning{{UNKNOWN}}
+      clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{TRUE}}
+      clang_analyzer_eval(arr[1][1].x == 0); // expected-warning{{TRUE}}
 
       arr[0][0].x = &i;
       arr[1][1].x = &j;
@@ -597,5 +593,5 @@
 void overrideDoubleDelete() {
   auto *a = new CustomOperators();
   delete a;
-  delete a; // expected-warning@581 {{Attempt to free released memory}}
+  delete a; // expected-warning@577 {{Attempt to free released memory}}
 }
Index: clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_analyze_cc1 %s \
+// RUN:  -analyzer-checker=debug.AnalysisOrder \
+// RUN:  -analyzer-config debug.AnalysisOrder:PreCall=true \
+// RUN:  -analyzer-config debug.AnalysisOrder:PostCall=true \
+// RUN:  2>&1 | FileCheck %s
+
+// This test ensures that eval::Call event will be triggered for constructors.
+
+class C {
+public:
+  C(){};
+};
+
+void stack() {
+  C arr[4];
+  C *arr2 = new C[4];
+  C arr3[2][2];
+}
+
+// C arr[4];
+// CHECK:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+//
+// C *arr2 = new C[4];
+// CHECK-NEXT:  PreCall (operator new[]) [CXXAllocatorCall]
+// CHECK-NEXT:  PostCall (operator new[]) [CXXAllocatorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+//
+// C arr3[2][2];
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PreCall (C::C) [CXXConstructorCall]
+// CHECK-NEXT:  PostCall (C::C) [CXXConstructorCall]
Index: clang/test/Analysis/ctor.mm
===================================================================
--- clang/test/Analysis/ctor.mm
+++ clang/test/Analysis/ctor.mm
@@ -581,12 +581,11 @@
   }
 
   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}}
+    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}}
   }
 
   struct initializing_pair {
Index: clang/test/Analysis/ctor-array.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/ctor-array.cpp
@@ -0,0 +1,259 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-disable-checker=cplusplus -analyzer-config c++-inlining=constructors -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_eval(bool);
+
+struct s {
+  int x;
+  int y;
+};
+
+void a1(void) {
+  s arr[3];
+  int x = arr[0].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void a2(void) {
+  s arr[3];
+  int x = arr[1].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void a3(void) {
+  s arr[3];
+  int x = arr[2].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+struct s2 {
+  int x;
+  int y = 2;
+};
+
+void b1(void) {
+  s2 arr[3];
+
+  clang_analyzer_eval(arr[0].y == 2); // expected-warning{{TRUE}}
+  int x = arr[0].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void b2(void) {
+  s2 arr[3];
+
+  clang_analyzer_eval(arr[1].y == 2); // expected-warning{{TRUE}}
+  int x = arr[1].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void b3(void) {
+  s2 arr[3];
+
+  clang_analyzer_eval(arr[2].y == 2); // expected-warning{{TRUE}}
+  int x = arr[2].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void c1(void) {
+  {
+    s2 arr[2];
+    arr[1].x = 3;
+
+    clang_analyzer_eval(arr[1].y == 2); // expected-warning{{TRUE}}
+    clang_analyzer_eval(arr[1].x == 3); // expected-warning{{TRUE}}
+  }
+
+  {
+    s2 arr[2];
+
+    clang_analyzer_eval(arr[1].y == 2); // expected-warning{{TRUE}}
+    int x = arr[1].x;
+    // expected-warning@-1{{Assigned value is garbage or undefined}}
+  }
+}
+
+struct s3 {
+  int x = 1;
+  int y = 2;
+};
+
+struct s4 {
+  s3 arr[2];
+  s sarr[2];
+};
+
+void e1(void) {
+  s4 arr[2];
+
+  clang_analyzer_eval(arr[0].arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].arr[0].y == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[0].arr[1].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].arr[1].y == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[1].arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].arr[0].y == 2); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[1].arr[1].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].arr[1].y == 2); // expected-warning{{TRUE}}
+
+  int x = arr[1].sarr[1].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void f1(void) {
+  s2 arr[2][2];
+
+  clang_analyzer_eval(arr[1][1].y == 2); // expected-warning{{TRUE}}
+  int x = arr[1][1].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+struct s5 {
+  static int c;
+  int x;
+
+  s5() : x(c++) {}
+};
+
+void g1(void) {
+  // FIXME: This test requires -analyzer-disable-checker=cplusplus,
+  // because of the checker's weird behaviour in case of arrays.
+  // E.g.:
+  //        s3 *arr = new s3[4];
+  //        s3 *arr2 = new (arr + 1) s3[1];
+  //                   ^~~~~~~~~~~~~~~~~~~
+  //                   warning: 12 bytes is possibly not enough
+  //                            for array allocation which requires
+  //                            4 bytes.
+
+  s5::c = 0;
+  s5 *arr = new s5[4];
+  new (arr + 1) s5[3];
+
+  clang_analyzer_eval(arr[0].x == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].x == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[2].x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[3].x == 6); // expected-warning{{TRUE}}
+}
+
+void g2(void) {
+  s5::c = 0;
+  s5 arr[4];
+
+  clang_analyzer_eval(arr[0].x == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[2].x == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[3].x == 3); // expected-warning{{TRUE}}
+}
+
+void g3(void) {
+  s5::c = 0;
+  s5 arr[2][2];
+
+  clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0][1].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1][0].x == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1][1].x == 3); // expected-warning{{TRUE}}
+}
+
+void h1(void) {
+  s5::c = 0;
+  s5 a[2][2], b[2][2];
+
+  clang_analyzer_eval(a[0][0].x == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a[0][1].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a[1][0].x == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a[1][1].x == 3); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(b[0][0].x == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b[0][1].x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b[1][0].x == 6); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b[1][1].x == 7); // expected-warning{{TRUE}}
+}
+
+void h2(void) {
+  s a[2][2], b[2][2];
+
+  int x = a[1][1].x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+void h3(void) {
+  s a[2][2], b[2][2];
+
+  int x = b[1][1].y;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+}
+
+struct Base {
+  int x;
+  int y;
+
+  Base(int x, int y) : x(x), y(y) {}
+};
+
+struct Derived : public Base {
+  int i;
+  int j;
+
+  Derived(int x, int y, int i, int j) : Base(x, y), i(i), j(j) {}
+};
+
+void delegate(void) {
+  Derived arr[2] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
+
+  clang_analyzer_eval(arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].y == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].i == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].j == 4); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[1].x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].y == 6); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].i == 7); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].j == 8); // expected-warning{{TRUE}}
+}
+
+void delegate_heap(void) {
+  Derived *arr = new Derived[2]{{1, 2, 3, 4}, {5, 6, 7, 8}};
+
+  clang_analyzer_eval(arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].y == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].i == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].j == 4); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[1].x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].y == 6); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].i == 7); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].j == 8); // expected-warning{{TRUE}}
+}
+
+struct Member {
+  int x;
+  int y;
+};
+
+struct Parent {
+  Member arr[2];
+
+  Parent() : arr{{1, 2}, {3, 4}} {}
+};
+
+void member() {
+  Parent arr[2];
+
+  // FIXME: Ideally these are TRUE, but at the moment InitListExpr has no
+  // knowledge about where the initializer list is used, so we can't bind
+  // the initializer list to the required region.
+  clang_analyzer_eval(arr[0].arr[0].x == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[0].arr[0].y == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[0].arr[1].x == 3); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[0].arr[1].y == 4); // expected-warning{{UNKNOWN}}
+
+  clang_analyzer_eval(arr[1].arr[0].x == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[1].arr[0].y == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[1].arr[1].x == 3); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[1].arr[1].y == 4); // expected-warning{{UNKNOWN}}
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -227,6 +227,13 @@
 
   // Step 2: generate node with bound return value: CEBNode -> BindedRetNode.
 
+  // If this variable is set to 'true' the analyzer will evaluate the call
+  // statement we are about to exit again, instead of continuing the execution
+  // from the statement after the call. This is useful for non-POD type array
+  // construction where the CXXConstructExpr is referenced only once in the CFG,
+  // but we want to evaluate it as many times as many elements the array has.
+  bool ShouldRepeatCall = false;
+
   // If the callee returns an expression, bind its value to CallExpr.
   if (CE) {
     if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
@@ -255,6 +262,12 @@
       SVal ThisV = state->getSVal(This);
       ThisV = state->getSVal(ThisV.castAs<Loc>());
       state = state->BindExpr(CCE, callerCtx, ThisV);
+
+      ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx);
+
+      if (!ShouldRepeatCall &&
+          getIndexOfElementToConstruct(state, CCE, callerCtx))
+        state = removeIndexOfElementToConstruct(state, CCE, callerCtx);
     }
 
     if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) {
@@ -358,9 +371,10 @@
 
     // Enqueue the next element in the block.
     for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
-                                   PSI != PSE; ++PSI) {
-      Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
-                                    calleeCtx->getIndex()+1);
+         PSI != PSE; ++PSI) {
+      unsigned Idx = calleeCtx->getIndex() + (ShouldRepeatCall ? 0 : 1);
+
+      Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), Idx);
     }
   }
 }
@@ -800,8 +814,11 @@
     // initializers for array fields in default move/copy constructors.
     // We still allow construction into ElementRegion targets when they don't
     // represent array elements.
-    if (CallOpts.IsArrayCtorOrDtor)
-      return CIP_DisallowedOnce;
+    if (CallOpts.IsArrayCtorOrDtor) {
+      if (!shouldInlineArrayConstruction(
+              dyn_cast<ArrayType>(CtorExpr->getType())))
+        return CIP_DisallowedOnce;
+    }
 
     // Inlining constructors requires including initializers in the CFG.
     const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
@@ -852,7 +869,7 @@
     assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors");
     (void)ADC;
 
-    // FIXME: We don't handle constructors or destructors for arrays properly.
+    // FIXME: We don't handle destructors for arrays properly.
     if (CallOpts.IsArrayCtorOrDtor)
       return CIP_DisallowedOnce;
 
@@ -1065,6 +1082,38 @@
   return true;
 }
 
+bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) {
+  if (!Type)
+    return false;
+
+  // FIXME: Handle other arrays types.
+  if (const auto *CAT = dyn_cast<ConstantArrayType>(Type)) {
+    unsigned Size = getContext().getConstantArrayElementCount(CAT);
+
+    return Size <= AMgr.options.maxBlockVisitOnPath;
+  }
+
+  return false;
+}
+
+bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State,
+                                      const CXXConstructExpr *E,
+                                      const LocationContext *LCtx) {
+
+  if (!E)
+    return false;
+
+  auto Ty = E->getType();
+
+  // FIXME: Handle non constant array types
+  if (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+    unsigned Size = getContext().getConstantArrayElementCount(CAT);
+    return Size > getIndexOfElementToConstruct(State, E, LCtx);
+  }
+
+  return false;
+}
+
 static bool isTrivialObjectAssignment(const CallEvent &Call) {
   const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call);
   if (!ICall)
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -94,15 +94,17 @@
   }
 }
 
-
-SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
-                                       QualType &Ty, bool &IsArray) {
+SVal ExprEngine::makeElementRegion(ProgramStateRef State, SVal LValue,
+                                   QualType &Ty, bool &IsArray, unsigned Idx) {
   SValBuilder &SVB = State->getStateManager().getSValBuilder();
   ASTContext &Ctx = SVB.getContext();
 
-  while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
-    Ty = AT->getElementType();
-    LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue);
+  if (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
+    while (AT) {
+      Ty = AT->getElementType();
+      AT = dyn_cast<ArrayType>(AT->getElementType());
+    }
+    LValue = State->getLValue(Ty, SVB.makeArrayIndex(Idx), LValue);
     IsArray = true;
   }
 
@@ -111,7 +113,7 @@
 
 SVal ExprEngine::computeObjectUnderConstruction(
     const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
-    const ConstructionContext *CC, EvalCallOptions &CallOpts) {
+    const ConstructionContext *CC, EvalCallOptions &CallOpts, unsigned Idx) {
   SValBuilder &SVB = getSValBuilder();
   MemRegionManager &MRMgr = SVB.getRegionManager();
   ASTContext &ACtx = SVB.getContext();
@@ -125,8 +127,8 @@
       const auto *DS = DSCC->getDeclStmt();
       const auto *Var = cast<VarDecl>(DS->getSingleDecl());
       QualType Ty = Var->getType();
-      return makeZeroElementRegion(State, State->getLValue(Var, LCtx), Ty,
-                                   CallOpts.IsArrayCtorOrDtor);
+      return makeElementRegion(State, State->getLValue(Var, LCtx), Ty,
+                               CallOpts.IsArrayCtorOrDtor, Idx);
     }
     case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
     case ConstructionContext::SimpleConstructorInitializerKind: {
@@ -158,8 +160,8 @@
       }
 
       QualType Ty = Field->getType();
-      return makeZeroElementRegion(State, FieldVal, Ty,
-                                   CallOpts.IsArrayCtorOrDtor);
+      return makeElementRegion(State, FieldVal, Ty, CallOpts.IsArrayCtorOrDtor,
+                               Idx);
     }
     case ConstructionContext::NewAllocatedObjectKind: {
       if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
@@ -172,8 +174,12 @@
             // TODO: In fact, we need to call the constructor for every
             // allocated element, not just the first one!
             CallOpts.IsArrayCtorOrDtor = true;
-            return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
-                MR, NE->getType()->getPointeeType()));
+
+            auto R = MRMgr.getElementRegion(NE->getType()->getPointeeType(),
+                                            svalBuilder.makeArrayIndex(Idx), MR,
+                                            SVB.getContext());
+
+            return loc::MemRegionVal(R);
           }
           return  V;
         }
@@ -484,10 +490,6 @@
     }
   }
 
-  // FIXME: Handle arrays, which run the same constructor for every element.
-  // For now, we just run the first constructor (which should still invalidate
-  // the entire array).
-
   EvalCallOptions CallOpts;
   auto C = getCurrentCFGElement().getAs<CFGConstructor>();
   assert(C || getCurrentCFGElement().getAs<CFGStmt>());
@@ -500,9 +502,15 @@
     // Inherited constructors are always base class constructors.
     assert(CE && !CIE && "A complete constructor is inherited?!");
 
+    unsigned Idx = 0;
+    if (CE->getType()->isArrayType()) {
+      Idx = getIndexOfElementToConstruct(State, CE, LCtx).getValueOr(0u);
+      State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1);
+    }
+
     // The target region is found from construction context.
     std::tie(State, Target) =
-        handleConstructionContext(CE, State, LCtx, CC, CallOpts);
+        handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx);
     break;
   }
   case CXXConstructExpr::CK_VirtualBase: {
@@ -894,14 +902,39 @@
   SVal Result = symVal;
 
   if (CNE->isArray()) {
-    // FIXME: allocating an array requires simulating the constructors.
-    // For now, just return a symbolicated region.
+
     if (const auto *NewReg = cast_or_null<SubRegion>(symVal.getAsRegion())) {
-      QualType ObjTy = CNE->getType()->getPointeeType();
+      // If each element is initialized by their default constructor, the field
+      // values are properly placed inside the required region, however if an
+      // initializer list is used, this doesn't happen automatically.
+      auto *Init = CNE->getInitializer();
+      bool isInitList = dyn_cast_or_null<InitListExpr>(Init);
+
+      QualType ObjTy =
+          isInitList ? Init->getType() : CNE->getType()->getPointeeType();
       const ElementRegion *EleReg =
-          getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+          MRMgr.getElementRegion(ObjTy, svalBuilder.makeArrayIndex(0), NewReg,
+                                 svalBuilder.getContext());
       Result = loc::MemRegionVal(EleReg);
+
+      // If the array is list initialized, we bind the initializer list to the
+      // memory region here, otherwise we would lose it.
+      if (isInitList) {
+        Bldr.takeNodes(Pred);
+        Pred = Bldr.generateNode(CNE, Pred, State);
+
+        SVal V = State->getSVal(Init, LCtx);
+        ExplodedNodeSet evaluated;
+        evalBind(evaluated, CNE, Pred, Result, V, true);
+
+        Bldr.takeNodes(Pred);
+        Bldr.addNodes(evaluated);
+
+        Pred = *evaluated.begin();
+        State = Pred->getState();
+      }
     }
+
     State = State->BindExpr(CNE, Pred->getLocationContext(), Result);
     Bldr.generateNode(CNE, Pred, State);
     return;
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -185,6 +185,17 @@
 REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
                                  ObjectsUnderConstructionMap)
 
+// This trait is responsible for storing the index of the element that is to be
+// constructed in the next iteration. As a result a CXXConstructExpr is only
+// stored if it is array type. Also the index is the index of the continous
+// memory region, which is important for multi-dimensional arrays. E.g:: int
+// arr[2][2]; assume arr[1][1] will be the next element under construction, so
+// the index is 3.
+typedef llvm::ImmutableMap<
+    std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
+    IndexOfElementToConstructMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct,
+                                 IndexOfElementToConstructMap)
 //===----------------------------------------------------------------------===//
 // Engine construction and deletion.
 //===----------------------------------------------------------------------===//
@@ -441,16 +452,65 @@
   return State;
 }
 
+ProgramStateRef ExprEngine::setIndexOfElementToConstruct(
+    ProgramStateRef State, const CXXConstructExpr *E,
+    const LocationContext *LCtx, unsigned Idx) {
+  auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+  assert(!State->contains<IndexOfElementToConstruct>(Key) || Idx > 0);
+
+  return State->set<IndexOfElementToConstruct>(Key, Idx);
+}
+
+Optional<unsigned>
+ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State,
+                                         const CXXConstructExpr *E,
+                                         const LocationContext *LCtx) {
+
+  return Optional<unsigned>::create(
+      State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()}));
+}
+
+ProgramStateRef
+ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State,
+                                            const CXXConstructExpr *E,
+                                            const LocationContext *LCtx) {
+  auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+  assert(E && State->contains<IndexOfElementToConstruct>(Key));
+  return State->remove<IndexOfElementToConstruct>(Key);
+}
+
 ProgramStateRef
 ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
                                        const ConstructionContextItem &Item,
                                        const LocationContext *LC, SVal V) {
   ConstructedObjectKey Key(Item, LC->getStackFrame());
+
+  const CXXConstructExpr *E = nullptr;
+
+  if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) {
+    if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl()))
+      E = dyn_cast<CXXConstructExpr>(VD->getInit());
+  }
+
+  if (!E && !Item.getStmtOrNull()) {
+    auto CtorInit = Item.getCXXCtorInitializer();
+    E = dyn_cast<CXXConstructExpr>(CtorInit->getInit());
+  }
+
   // FIXME: Currently the state might already contain the marker due to
   // incorrect handling of temporaries bound to default parameters.
-  assert(!State->get<ObjectsUnderConstruction>(Key) ||
-         Key.getItem().getKind() ==
-             ConstructionContextItem::TemporaryDestructorKind);
+  // The state will already contain the marker if we construct elements
+  // in an array, as we visit the same statement multiple times before
+  // the array declaration. The marker is removed when we exit the
+  // constructor call.
+  assert((!State->get<ObjectsUnderConstruction>(Key) ||
+          Key.getItem().getKind() ==
+              ConstructionContextItem::TemporaryDestructorKind ||
+          State->contains<IndexOfElementToConstruct>({E, LC})) &&
+         "The object is already marked as `UnderConstruction`, when it's not "
+         "supposed to!");
   return State->set<ObjectsUnderConstruction>(Key, V);
 }
 
@@ -582,6 +642,69 @@
   }
 }
 
+static void printIndicesOfElementsToConstructJson(
+    raw_ostream &Out, ProgramStateRef State, const char *NL,
+    const LocationContext *LCtx, const ASTContext &Context,
+    unsigned int Space = 0, bool IsDot = false) {
+  using KeyT = std::pair<const Expr *, const LocationContext *>;
+
+  PrintingPolicy PP =
+      LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+
+  ++Space;
+  bool HasItem = false;
+
+  // Store the last key.
+  KeyT LastKey;
+  for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+    const KeyT &Key = I.first;
+    if (Key.second != LCtx)
+      continue;
+
+    if (!HasItem) {
+      Out << "[" << NL;
+      HasItem = true;
+    }
+
+    LastKey = Key;
+  }
+
+  for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+    const KeyT &Key = I.first;
+    unsigned Value = I.second;
+    if (Key.second != LCtx)
+      continue;
+
+    Indent(Out, Space, IsDot) << "{ ";
+
+    // Expr
+    const Expr *E = Key.first;
+    Out << "\"stmt_id\": " << E->getID(Context);
+
+    // Kind - hack to display the current index
+    Out << ", \"kind\": \"Cur: " << Value - 1 << "\"";
+
+    // Pretty-print
+    Out << ", \"pretty\": ";
+    Out << "\"" << E->getStmtClassName() << " "
+        << E->getSourceRange().printToString(Context.getSourceManager()) << " '"
+        << QualType::getAsString(E->getType().split(), PP);
+    Out << "'\"";
+
+    Out << ", \"value\": \"Next: " << Value << "\" }";
+
+    if (Key != LastKey)
+      Out << ',';
+    Out << NL;
+  }
+
+  if (HasItem)
+    Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+  else {
+    Out << "null ";
+  }
+}
+
 void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
                            const LocationContext *LCtx, const char *NL,
                            unsigned int Space, bool IsDot) const {
@@ -600,6 +723,23 @@
     Out << "null," << NL;
   }
 
+  Indent(Out, Space, IsDot) << "\"index_of_element\": ";
+  if (LCtx && !State->get<IndexOfElementToConstruct>().isEmpty()) {
+    ++Space;
+
+    auto &Context = getContext();
+    Out << '[' << NL;
+    LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
+      printIndicesOfElementsToConstructJson(Out, State, NL, LC, Context, Space,
+                                            IsDot);
+    });
+
+    --Space;
+    Indent(Out, Space, IsDot) << "]," << NL; // End of "index_of_element".
+  } else {
+    Out << "null," << NL;
+  }
+
   getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
                                                    IsDot);
 }
@@ -961,8 +1101,9 @@
   // This workaround will just run the first destructor (which will still
   // invalidate the entire array).
   EvalCallOptions CallOpts;
-  Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
-                                 CallOpts.IsArrayCtorOrDtor).getAsRegion();
+  Region = makeElementRegion(state, loc::MemRegionVal(Region), varType,
+                             CallOpts.IsArrayCtorOrDtor)
+               .getAsRegion();
 
   VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
                      /*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1045,8 +1186,7 @@
   // This workaround will just run the first destructor (which will still
   // invalidate the entire array).
   EvalCallOptions CallOpts;
-  FieldVal = makeZeroElementRegion(State, FieldVal, T,
-                                   CallOpts.IsArrayCtorOrDtor);
+  FieldVal = makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor);
 
   VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
                      /*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1105,7 +1245,7 @@
       CallOpts.IsArrayCtorOrDtor = true;
     }
   } else {
-    // We'd eventually need to makeZeroElementRegion() trick here,
+    // We'd eventually need to makeElementRegion() trick here,
     // but for now we don't have the respective construction contexts,
     // so MR would always be null in this case. Do nothing for now.
   }
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -617,6 +617,11 @@
     return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T);
   }
 
+  /// Retreives which element is being constructed in a non POD type array.
+  static Optional<unsigned>
+  getIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E,
+                               const LocationContext *LCtx);
+
   /// By looking at a certain item that may be potentially part of an object's
   /// ConstructionContext, retrieve such object's location. A particular
   /// statement can be transparently passed as \p Item in most cases.
@@ -708,10 +713,19 @@
   /// fully implemented it sometimes indicates that it failed via its
   /// out-parameter CallOpts; in such cases a fake temporary region is
   /// returned, which is better than nothing but does not represent
-  /// the actual behavior of the program.
-  SVal computeObjectUnderConstruction(
-      const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
-      const ConstructionContext *CC, EvalCallOptions &CallOpts);
+  /// the actual behavior of the program. The Idx parameter is used if we
+  /// construct an array of objects. In that case it points to the index
+  /// of the continous memory region.
+  /// E.g.:
+  /// For `int arr[4]` this index can be 0,1,2,3.
+  /// For `int arr2[3][3]` this index can be 0,1,...,7,8.
+  /// A multi-dimensional array is also a continous memory location in a
+  /// row major order, so for arr[0][0] Idx is 0 and for arr[2][2] Idx is 8.
+  SVal computeObjectUnderConstruction(const Expr *E, ProgramStateRef State,
+                                      const LocationContext *LCtx,
+                                      const ConstructionContext *CC,
+                                      EvalCallOptions &CallOpts,
+                                      unsigned Idx = 0);
 
   /// Update the program state with all the path-sensitive information
   /// that's necessary to perform construction of an object with a given
@@ -724,12 +738,16 @@
 
   /// A convenient wrapper around computeObjectUnderConstruction
   /// and updateObjectsUnderConstruction.
-  std::pair<ProgramStateRef, SVal> handleConstructionContext(
-      const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
-      const ConstructionContext *CC, EvalCallOptions &CallOpts) {
-    SVal V = computeObjectUnderConstruction(E, State, LCtx, CC, CallOpts);
-    return std::make_pair(
-        updateObjectsUnderConstruction(V, E, State, LCtx, CC, CallOpts), V);
+  std::pair<ProgramStateRef, SVal>
+  handleConstructionContext(const Expr *E, ProgramStateRef State,
+                            const LocationContext *LCtx,
+                            const ConstructionContext *CC,
+                            EvalCallOptions &CallOpts, unsigned Idx = 0) {
+
+    SVal V = computeObjectUnderConstruction(E, State, LCtx, CC, CallOpts, Idx);
+    State = updateObjectsUnderConstruction(V, E, State, LCtx, CC, CallOpts);
+
+    return std::make_pair(State, V);
   }
 
 private:
@@ -796,6 +814,15 @@
                         const ExplodedNode *Pred,
                         const EvalCallOptions &CallOpts = {});
 
+  /// Checks whether our policies allow us to inline a non-POD type array
+  /// construction.
+  bool shouldInlineArrayConstruction(const ArrayType *Type);
+
+  /// Checks whether we construct an array of non-POD type, and decides if the
+  /// constructor should be inkoved once again.
+  bool shouldRepeatCtorCall(ProgramStateRef State, const CXXConstructExpr *E,
+                            const LocationContext *LCtx);
+
   void inlineCall(WorkList *WList, const CallEvent &Call, const Decl *D,
                   NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State);
 
@@ -838,7 +865,7 @@
       const Expr *InitWithAdjustments, const Expr *Result = nullptr,
       const SubRegion **OutRegionWithAdjustments = nullptr);
 
-  /// Returns a region representing the first element of a (possibly
+  /// Returns a region representing the `Idx`th element of a (possibly
   /// multi-dimensional) array, for the purposes of element construction or
   /// destruction.
   ///
@@ -846,8 +873,8 @@
   ///
   /// If the type is not an array type at all, the original value is returned.
   /// Otherwise the "IsArray" flag is set.
-  static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue,
-                                    QualType &Ty, bool &IsArray);
+  static SVal makeElementRegion(ProgramStateRef State, SVal LValue,
+                                QualType &Ty, bool &IsArray, unsigned Idx = 0);
 
   /// For a DeclStmt or CXXInitCtorInitializer, walk backward in the current CFG
   /// block to find the constructor expression that directly constructed into
@@ -878,6 +905,17 @@
                                               const ObjCForCollectionStmt *O,
                                               const LocationContext *LC);
 private:
+  /// Assuming we construct an array of non-POD types, this method allows us
+  /// to store which element is to be constructed next.
+  static ProgramStateRef
+  setIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E,
+                               const LocationContext *LCtx, unsigned Idx);
+
+  static ProgramStateRef
+  removeIndexOfElementToConstruct(ProgramStateRef State,
+                                  const CXXConstructExpr *E,
+                                  const LocationContext *LCtx);
+
   /// Store the location of a C++ object corresponding to a statement
   /// until the statement is actually encountered. For example, if a DeclStmt
   /// has CXXConstructExpr as its initializer, the object would be considered
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to