[clang] b032e3f - [analyzer] Evaluate construction of non-POD type arrays

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 14 14:32:31 PDT 2022


Author: isuckatcs
Date: 2022-07-14T23:30:21+02:00
New Revision: b032e3ff6121a969b2e90ad7bf493c2d5d7ac3a2

URL: https://github.com/llvm/llvm-project/commit/b032e3ff6121a969b2e90ad7bf493c2d5d7ac3a2
DIFF: https://github.com/llvm/llvm-project/commit/b032e3ff6121a969b2e90ad7bf493c2d5d7ac3a2.diff

LOG: [analyzer] Evaluate construction of non-POD type arrays

Introducing the support for evaluating the constructor
of every element in an array. The idea is to record the
index of the current array member being constructed and
create a loop during the analysis. We looping over the
same CXXConstructExpr as many times as many elements
the array has.

Differential Revision: https://reviews.llvm.org/D127973

Added: 
    clang/test/Analysis/ctor-array.cpp
    clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp

Modified: 
    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.mm
    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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 415fa05586edf..116a5970c341f 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -617,6 +617,11 @@ class ExprEngine {
     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 @@ class ExprEngine {
   /// 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 @@ class ExprEngine {
 
   /// 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 @@ class ExprEngine {
                         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 @@ class ExprEngine {
       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 @@ class ExprEngine {
   ///
   /// 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 @@ class ExprEngine {
                                               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

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 45af22de50ae1..d8f56f2f8cffb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -185,6 +185,17 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
 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 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
   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 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
   }
 }
 
+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 @@ void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
     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 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
   // 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 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
   // 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 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
       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.
   }

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 6d979da2755fa..825b11adad405 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -94,15 +94,17 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
   }
 }
 
-
-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::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
 
 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 @@ SVal ExprEngine::computeObjectUnderConstruction(
       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 @@ SVal ExprEngine::computeObjectUnderConstruction(
       }
 
       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 @@ SVal ExprEngine::computeObjectUnderConstruction(
             // 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 @@ void ExprEngine::handleConstructor(const Expr *E,
     }
   }
 
-  // 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 @@ void ExprEngine::handleConstructor(const Expr *E,
     // 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 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
   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;

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index e1649f0b3df68..ebcca92a3e4e8 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -227,6 +227,13 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
 
   // 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 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
       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 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
 
     // 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 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
     // 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 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
     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 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
   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)

diff  --git a/clang/test/Analysis/ctor-array.cpp b/clang/test/Analysis/ctor-array.cpp
new file mode 100644
index 0000000000000..053669cc2aada
--- /dev/null
+++ b/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 at -1{{Assigned value is garbage or undefined}}
+}
+
+void a2(void) {
+  s arr[3];
+  int x = arr[1].x;
+  // expected-warning at -1{{Assigned value is garbage or undefined}}
+}
+
+void a3(void) {
+  s arr[3];
+  int x = arr[2].x;
+  // expected-warning at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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}}
+}

diff  --git a/clang/test/Analysis/ctor.mm b/clang/test/Analysis/ctor.mm
index 1eafabb443de6..e5cd7afd4a269 100644
--- a/clang/test/Analysis/ctor.mm
+++ b/clang/test/Analysis/ctor.mm
@@ -581,12 +581,11 @@ void testNew() {
   }
 
   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 {

diff  --git a/clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp b/clang/test/Analysis/cxxctr-array-evalcall-analysis-order.cpp
new file mode 100644
index 0000000000000..808d1f61573ff
--- /dev/null
+++ b/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]

diff  --git a/clang/test/Analysis/dtor.cpp b/clang/test/Analysis/dtor.cpp
index 7935faca868ad..bda7d19522bdd 100644
--- a/clang/test/Analysis/dtor.cpp
+++ b/clang/test/Analysis/dtor.cpp
@@ -140,10 +140,8 @@ void testArrayInvalidation() {
     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 @@ namespace MultidimensionalArrays {
       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 overrideLeak() {
 void overrideDoubleDelete() {
   auto *a = new CustomOperators();
   delete a;
-  delete a; // expected-warning at 581 {{Attempt to free released memory}}
+  delete a; // expected-warning at 577 {{Attempt to free released memory}}
 }

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot b/clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
index 860bdcd620538..e22309d11c7b7 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
@@ -24,6 +24,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "environment": null,
         "checker_messages": [
           { "checker": "alpha.core.FooChecker", "messages": [

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/checker_messages_
diff .dot b/clang/test/Analysis/exploded-graph-rewriter/checker_messages_
diff .dot
index 401bf6ae8c3ad..48638257ff3e1 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/checker_messages_
diff .dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/checker_messages_
diff .dot
@@ -17,6 +17,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "FooChecker", "messages": [
             "Foo: Bar"
@@ -75,6 +76,7 @@ Node0x4 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "FooChecker", "messages": [
             "Foo: Bar",

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/constraints.dot b/clang/test/Analysis/exploded-graph-rewriter/constraints.dot
index d8f64d7518cfb..fa4ab08fe3d88 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/constraints.dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/constraints.dot
@@ -24,6 +24,7 @@ Node0x1 [shape=record,label=
         "environment": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "constraints": [
           { "symbol": "reg_$0<x>", "range": "{ [0, 0] }" }

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/constraints_
diff .dot b/clang/test/Analysis/exploded-graph-rewriter/constraints_
diff .dot
index 221d3702cf700..e7dfc89d9785d 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/constraints_
diff .dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/constraints_
diff .dot
@@ -17,6 +17,7 @@ Node0x1 [shape=record,label=
         "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 @@ Node0x3 [shape=record,label=
         "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 @@ Node0x5 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null
       }
     }

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/environment.dot b/clang/test/Analysis/exploded-graph-rewriter/environment.dot
index c7caedae560d1..d5c1fda74ec88 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/environment.dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/environment.dot
@@ -46,6 +46,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/environment_
diff .dot b/clang/test/Analysis/exploded-graph-rewriter/environment_
diff .dot
index 3e8f13bbf6c84..1df005c18d2dd 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/environment_
diff .dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/environment_
diff .dot
@@ -18,6 +18,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
@@ -73,6 +74,7 @@ Node0x6 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",
@@ -122,6 +124,7 @@ Node0x9 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "environment": {
           "pointer": "0x2",

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/store.dot b/clang/test/Analysis/exploded-graph-rewriter/store.dot
index febc36ebe6988..01e97b649ea8b 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/store.dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/store.dot
@@ -34,6 +34,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x2",

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/store_
diff .dot b/clang/test/Analysis/exploded-graph-rewriter/store_
diff .dot
index 6ea65ff8539f8..5c2fc9c9d088c 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/store_
diff .dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/store_
diff .dot
@@ -20,6 +20,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x2",
@@ -74,6 +75,7 @@ Node0x4 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": null,
         "store": {
           "pointer": "0x5",

diff  --git a/clang/test/Analysis/exploded-graph-rewriter/topology.dot b/clang/test/Analysis/exploded-graph-rewriter/topology.dot
index 85e6d5b37ab9d..56b4089136be3 100644
--- a/clang/test/Analysis/exploded-graph-rewriter/topology.dot
+++ b/clang/test/Analysis/exploded-graph-rewriter/topology.dot
@@ -24,6 +24,7 @@ Node0x1 [shape=record,label=
         "constraints": null,
         "dynamic_types": null,
         "constructing_objects": null,
+        "index_of_element": null,
         "checker_messages": [
           { "checker": "foo", "messages": ["bar"] }
         ],

diff  --git a/clang/test/Analysis/expr-inspection.c b/clang/test/Analysis/expr-inspection.c
index e6dbbabff0ff4..92bd190f7f2a3 100644
--- a/clang/test/Analysis/expr-inspection.c
+++ b/clang/test/Analysis/expr-inspection.c
@@ -47,6 +47,7 @@ void foo(int x) {
 // 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: }
 

diff  --git a/clang/test/Analysis/handle_constructors_with_new_array.cpp b/clang/test/Analysis/handle_constructors_with_new_array.cpp
index 61637afce8d4b..f0027ede93713 100644
--- a/clang/test/Analysis/handle_constructors_with_new_array.cpp
+++ b/clang/test/Analysis/handle_constructors_with_new_array.cpp
@@ -59,12 +59,9 @@ void test_automatic_aggregate() {
   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 @@ void test_dynamic_aggregate() {
   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;

diff  --git a/clang/test/Analysis/new-ctor-conservative.cpp b/clang/test/Analysis/new-ctor-conservative.cpp
index 83c4bed1530c5..bcc33fb8f8970 100644
--- a/clang/test/Analysis/new-ctor-conservative.cpp
+++ b/clang/test/Analysis/new-ctor-conservative.cpp
@@ -25,9 +25,14 @@ void checkNewPOD() {
 
 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 {

diff  --git a/clang/test/Analysis/new.cpp b/clang/test/Analysis/new.cpp
index 7f523b920c6b2..9ebf3809cdfbf 100644
--- a/clang/test/Analysis/new.cpp
+++ b/clang/test/Analysis/new.cpp
@@ -323,8 +323,8 @@ void testArrayNull() {
 
 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}}
 }
 

diff  --git a/clang/test/Analysis/operator-calls.cpp b/clang/test/Analysis/operator-calls.cpp
index 3107229659656..64abda150c5a1 100644
--- a/clang/test/Analysis/operator-calls.cpp
+++ b/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 @@ struct IntComparable {
 
 void testMemberOperator(IntComparable B) {
   clang_analyzer_eval(B == 0); // expected-warning{{TRUE}}
+  // expected-note at -1{{TRUE}}
 }
 
 
@@ -46,7 +47,9 @@ namespace UserDefinedConversions {
 
   void test(const Convertible &obj) {
     clang_analyzer_eval((int)obj == 42); // expected-warning{{TRUE}}
+    // expected-note at -1{{TRUE}}
     clang_analyzer_eval(obj); // expected-warning{{TRUE}}
+    // expected-note at -1{{TRUE}}
   }
 }
 
@@ -82,7 +85,15 @@ namespace RValues {
     // 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 at -1{{Assuming 'coin' is 0}}
+    // expected-note at -2{{'?' condition is false}}
+    // expected-note at -3{{UNKNOWN}}
+    // expected-note at -4{{Assuming 'coin' is 0}}
+    // expected-note at -5{{'?' condition is false}}
     clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // expected-warning{{UNKNOWN}}
+    // expected-note at -1{{'coin' is 0}}
+    // expected-note at -2{{'?' condition is false}}
+    // expected-note at -3{{UNKNOWN}}
   }
 }
 
@@ -102,31 +113,53 @@ namespace SynthesizedAssignment {
 
   // 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 at 110{{Assigned value is garbage or undefined}}
+    // expected-note at -1{{Calling defaulted copy assignment operator for 'B'}}
+    // expected-note at 110{{Assigned value is garbage or undefined}}
   }
 
   void testNoWarningMove() {
     B v, u;
-    u = static_cast<B &&>(v);
+    u = static_cast<B &&>(v); // expected-warning at 111{{Assigned value is garbage or undefined}}
+    // expected-note at -1{{Calling defaulted move assignment operator for 'B'}}
+    // expected-note at 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 at -1{{TRUE}}
     clang_analyzer_eval(u.a[1].a == -47); // expected-warning{{TRUE}}
+    // expected-note at -1{{TRUE}}
     clang_analyzer_eval(u.a[2].a == -42); // expected-warning{{TRUE}}
+    // expected-note at -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 at -1{{TRUE}}
     clang_analyzer_eval(u.a[1].a == 48); // expected-warning{{TRUE}}
+    // expected-note at -1{{TRUE}}
     clang_analyzer_eval(u.a[2].a == 43); // expected-warning{{TRUE}}
+    // expected-note at -1{{TRUE}}
   }
 }

diff  --git a/clang/utils/analyzer/exploded-graph-rewriter.py b/clang/utils/analyzer/exploded-graph-rewriter.py
index 54d87addd3da3..a7503e30a038a 100755
--- a/clang/utils/analyzer/exploded-graph-rewriter.py
+++ b/clang/utils/analyzer/exploded-graph-rewriter.py
@@ -144,7 +144,8 @@ def is_
diff erent(self, prev):
 
 
 # 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 @@ def __init__(self, state_id, json_ps):
                 'constraints': None,
                 'dynamic_types': None,
                 'constructing_objects': None,
+                'index_of_element': None,
                 'checker_messages': None
             }
 
@@ -296,6 +298,10 @@ def __init__(self, state_id, json_ps):
             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 @@ def visit_state(self, s, prev_s):
         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):


        


More information about the cfe-commits mailing list