[analyzer] Remove the loop from the exploded graph caused by missing information...
authorisuckatcs <65320245+isuckatcs@users.noreply.github.com>
Fri, 10 Feb 2023 17:07:31 +0000 (18:07 +0100)
committerisuckatcs <65320245+isuckatcs@users.noreply.github.com>
Sat, 4 Mar 2023 01:01:45 +0000 (02:01 +0100)
This patch adds CFGElementRef to ProgramPoints
and helps the analyzer to differentiate between
two otherwise identically looking ProgramPoints.

Fixes #60412

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

13 files changed:
clang/include/clang/Analysis/ProgramPoint.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
clang/test/Analysis/PR60412.cpp [new file with mode: 0644]
clang/test/Analysis/analysis-after-multiple-dtors.cpp [new file with mode: 0644]
clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp

index 6dba058..b933957 100644 (file)
@@ -95,35 +95,33 @@ private:
 
   llvm::PointerIntPair<const ProgramPointTag *, 2, unsigned> Tag;
 
+  CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0};
+
 protected:
   ProgramPoint() = default;
-  ProgramPoint(const void *P,
-               Kind k,
-               const LocationContext *l,
-               const ProgramPointTag *tag = nullptr)
-    : Data1(P),
-      Data2(nullptr, (((unsigned) k) >> 0) & 0x3),
-      L(l, (((unsigned) k) >> 2) & 0x3),
-      Tag(tag, (((unsigned) k) >> 4) & 0x3) {
-        assert(getKind() == k);
-        assert(getLocationContext() == l);
-        assert(getData1() == P);
-      }
-
-  ProgramPoint(const void *P1,
-               const void *P2,
-               Kind k,
-               const LocationContext *l,
-               const ProgramPointTag *tag = nullptr)
-    : Data1(P1),
-      Data2(P2, (((unsigned) k) >> 0) & 0x3),
-      L(l, (((unsigned) k) >> 2) & 0x3),
-      Tag(tag, (((unsigned) k) >> 4) & 0x3) {}
+  ProgramPoint(const void *P, Kind k, const LocationContext *l,
+               const ProgramPointTag *tag = nullptr,
+               CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0})
+      : Data1(P), Data2(nullptr, (((unsigned)k) >> 0) & 0x3),
+        L(l, (((unsigned)k) >> 2) & 0x3), Tag(tag, (((unsigned)k) >> 4) & 0x3),
+        ElemRef(ElemRef) {
+    assert(getKind() == k);
+    assert(getLocationContext() == l);
+    assert(getData1() == P);
+  }
+
+  ProgramPoint(const void *P1, const void *P2, Kind k, const LocationContext *l,
+               const ProgramPointTag *tag = nullptr,
+               CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0})
+      : Data1(P1), Data2(P2, (((unsigned)k) >> 0) & 0x3),
+        L(l, (((unsigned)k) >> 2) & 0x3), Tag(tag, (((unsigned)k) >> 4) & 0x3),
+        ElemRef(ElemRef) {}
 
 protected:
   const void *getData1() const { return Data1; }
   const void *getData2() const { return Data2.getPointer(); }
   void setData2(const void *d) { Data2.setPointer(d); }
+  CFGBlock::ConstCFGElementRef getElementRef() const { return ElemRef; }
 
 public:
   /// Create a new ProgramPoint object that is the same as the original
@@ -190,17 +188,13 @@ public:
   }
 
   bool operator==(const ProgramPoint & RHS) const {
-    return Data1 == RHS.Data1 &&
-           Data2 == RHS.Data2 &&
-           L == RHS.L &&
-           Tag == RHS.Tag;
+    return Data1 == RHS.Data1 && Data2 == RHS.Data2 && L == RHS.L &&
+           Tag == RHS.Tag && ElemRef == RHS.ElemRef;
   }
 
   bool operator!=(const ProgramPoint &RHS) const {
-    return Data1 != RHS.Data1 ||
-           Data2 != RHS.Data2 ||
-           L != RHS.L ||
-           Tag != RHS.Tag;
+    return Data1 != RHS.Data1 || Data2 != RHS.Data2 || L != RHS.L ||
+           Tag != RHS.Tag || ElemRef != RHS.ElemRef;
   }
 
   void Profile(llvm::FoldingSetNodeID& ID) const {
@@ -209,6 +203,8 @@ public:
     ID.AddPointer(getData2());
     ID.AddPointer(getLocationContext());
     ID.AddPointer(getTag());
+    ID.AddPointer(ElemRef.getParent());
+    ID.AddInteger(ElemRef.getIndexInBlock());
   }
 
   void printJson(llvm::raw_ostream &Out, const char *NL = "\n") const;
@@ -266,6 +262,7 @@ private:
   }
 };
 
+// FIXME: Eventually we want to take a CFGElementRef as parameter here too.
 class StmtPoint : public ProgramPoint {
 public:
   StmtPoint(const Stmt *S, const void *p2, Kind k, const LocationContext *L,
@@ -557,8 +554,9 @@ private:
 class ImplicitCallPoint : public ProgramPoint {
 public:
   ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K,
-                    const LocationContext *L, const ProgramPointTag *Tag)
-    : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {}
+                    const LocationContext *L, const ProgramPointTag *Tag,
+                    CFGBlock::ConstCFGElementRef ElemRef)
+      : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag, ElemRef) {}
 
   const Decl *getDecl() const { return static_cast<const Decl *>(getData2()); }
   SourceLocation getLocation() const {
@@ -581,8 +579,9 @@ private:
 class PreImplicitCall : public ImplicitCallPoint {
 public:
   PreImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
+                  CFGBlock::ConstCFGElementRef ElemRef,
                   const ProgramPointTag *Tag = nullptr)
-    : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {}
+      : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag, ElemRef) {}
 
 private:
   friend class ProgramPoint;
@@ -598,8 +597,9 @@ private:
 class PostImplicitCall : public ImplicitCallPoint {
 public:
   PostImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
+                   CFGBlock::ConstCFGElementRef ElemRef,
                    const ProgramPointTag *Tag = nullptr)
-    : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {}
+      : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag, ElemRef) {}
 
 private:
   friend class ProgramPoint;
index 710bc8c..ffd6af5 100644 (file)
@@ -154,6 +154,7 @@ private:
   ProgramStateRef State;
   const LocationContext *LCtx;
   llvm::PointerUnion<const Expr *, const Decl *> Origin;
+  CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0};
   mutable std::optional<bool> Foreign; // Set by CTU analysis.
 
 protected:
@@ -176,16 +177,19 @@ private:
 protected:
   friend class CallEventManager;
 
-  CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx)
-      : State(std::move(state)), LCtx(lctx), Origin(E) {}
+  CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx,
+            CFGBlock::ConstCFGElementRef ElemRef)
+      : State(std::move(state)), LCtx(lctx), Origin(E), ElemRef(ElemRef) {}
 
-  CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx)
-      : State(std::move(state)), LCtx(lctx), Origin(D) {}
+  CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx,
+            CFGBlock::ConstCFGElementRef ElemRef)
+      : State(std::move(state)), LCtx(lctx), Origin(D), ElemRef(ElemRef) {}
 
   // DO NOT MAKE PUBLIC
   CallEvent(const CallEvent &Original)
       : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin),
-        Data(Original.Data), Location(Original.Location) {}
+        ElemRef(Original.ElemRef), Data(Original.Data),
+        Location(Original.Location) {}
 
   /// Copies this CallEvent, with vtable intact, into a new block of memory.
   virtual void cloneTo(void *Dest) const = 0;
@@ -232,6 +236,10 @@ public:
     return LCtx;
   }
 
+  const CFGBlock::ConstCFGElementRef &getCFGElementRef() const {
+    return ElemRef;
+  }
+
   /// Returns the definition of the function or method that will be
   /// called.
   virtual RuntimeDefinition getRuntimeDefinition() const = 0;
@@ -484,11 +492,13 @@ public:
 class AnyFunctionCall : public CallEvent {
 protected:
   AnyFunctionCall(const Expr *E, ProgramStateRef St,
-                  const LocationContext *LCtx)
-      : CallEvent(E, St, LCtx) {}
+                  const LocationContext *LCtx,
+                  CFGBlock::ConstCFGElementRef ElemRef)
+      : CallEvent(E, St, LCtx, ElemRef) {}
   AnyFunctionCall(const Decl *D, ProgramStateRef St,
-                  const LocationContext *LCtx)
-      : CallEvent(D, St, LCtx) {}
+                  const LocationContext *LCtx,
+                  CFGBlock::ConstCFGElementRef ElemRef)
+      : CallEvent(D, St, LCtx, ElemRef) {}
   AnyFunctionCall(const AnyFunctionCall &Other) = default;
 
 public:
@@ -521,8 +531,9 @@ class SimpleFunctionCall : public AnyFunctionCall {
 
 protected:
   SimpleFunctionCall(const CallExpr *CE, ProgramStateRef St,
-                     const LocationContext *LCtx)
-      : AnyFunctionCall(CE, St, LCtx) {}
+                     const LocationContext *LCtx,
+                     CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(CE, St, LCtx, ElemRef) {}
   SimpleFunctionCall(const SimpleFunctionCall &Other) = default;
 
   void cloneTo(void *Dest) const override {
@@ -557,9 +568,9 @@ class BlockCall : public CallEvent {
   friend class CallEventManager;
 
 protected:
-  BlockCall(const CallExpr *CE, ProgramStateRef St,
-            const LocationContext *LCtx)
-      : CallEvent(CE, St, LCtx) {}
+  BlockCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx,
+            CFGBlock::ConstCFGElementRef ElemRef)
+      : CallEvent(CE, St, LCtx, ElemRef) {}
   BlockCall(const BlockCall &Other) = default;
 
   void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); }
@@ -661,11 +672,13 @@ public:
 class CXXInstanceCall : public AnyFunctionCall {
 protected:
   CXXInstanceCall(const CallExpr *CE, ProgramStateRef St,
-                  const LocationContext *LCtx)
-      : AnyFunctionCall(CE, St, LCtx) {}
+                  const LocationContext *LCtx,
+                  CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(CE, St, LCtx, ElemRef) {}
   CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St,
-                  const LocationContext *LCtx)
-      : AnyFunctionCall(D, St, LCtx) {}
+                  const LocationContext *LCtx,
+                  CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(D, St, LCtx, ElemRef) {}
   CXXInstanceCall(const CXXInstanceCall &Other) = default;
 
   void getExtraInvalidatedValues(ValueList &Values,
@@ -699,8 +712,9 @@ class CXXMemberCall : public CXXInstanceCall {
 
 protected:
   CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St,
-                const LocationContext *LCtx)
-      : CXXInstanceCall(CE, St, LCtx) {}
+                const LocationContext *LCtx,
+                CFGBlock::ConstCFGElementRef ElemRef)
+      : CXXInstanceCall(CE, St, LCtx, ElemRef) {}
   CXXMemberCall(const CXXMemberCall &Other) = default;
 
   void cloneTo(void *Dest) const override { new (Dest) CXXMemberCall(*this); }
@@ -741,8 +755,9 @@ class CXXMemberOperatorCall : public CXXInstanceCall {
 
 protected:
   CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
-                        const LocationContext *LCtx)
-      : CXXInstanceCall(CE, St, LCtx) {}
+                        const LocationContext *LCtx,
+                        CFGBlock::ConstCFGElementRef ElemRef)
+      : CXXInstanceCall(CE, St, LCtx, ElemRef) {}
   CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) = default;
 
   void cloneTo(void *Dest) const override {
@@ -808,10 +823,17 @@ protected:
   /// \param Target The object region to be destructed.
   /// \param St The path-sensitive state at this point in the program.
   /// \param LCtx The location context at this point in the program.
+  /// \param ElemRef The reference to this destructor in the CFG.
+  ///
+  /// FIXME: Eventually we want to drop \param Target and deduce it from
+  /// \param ElemRef. To do that we need to migrate the logic for target
+  /// region lookup from ExprEngine::ProcessImplicitDtor() and make it
+  /// independent from ExprEngine.
   CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
                     const MemRegion *Target, bool IsBaseDestructor,
-                    ProgramStateRef St, const LocationContext *LCtx)
-      : CXXInstanceCall(DD, St, LCtx) {
+                    ProgramStateRef St, const LocationContext *LCtx,
+                    CFGBlock::ConstCFGElementRef ElemRef)
+      : CXXInstanceCall(DD, St, LCtx, ElemRef) {
     Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue();
     Location = Trigger->getEndLoc();
   }
@@ -847,8 +869,9 @@ public:
 class AnyCXXConstructorCall : public AnyFunctionCall {
 protected:
   AnyCXXConstructorCall(const Expr *E, const MemRegion *Target,
-                        ProgramStateRef St, const LocationContext *LCtx)
-      : AnyFunctionCall(E, St, LCtx) {
+                        ProgramStateRef St, const LocationContext *LCtx,
+                        CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(E, St, LCtx, ElemRef) {
     assert(E && (isa<CXXConstructExpr>(E) || isa<CXXInheritedCtorInitExpr>(E)));
     // Target may be null when the region is unknown.
     Data = Target;
@@ -884,9 +907,14 @@ protected:
   ///               a new symbolic region will be used.
   /// \param St The path-sensitive state at this point in the program.
   /// \param LCtx The location context at this point in the program.
+  /// \param ElemRef The reference to this constructor in the CFG.
+  ///
+  /// FIXME: Eventually we want to drop \param Target and deduce it from
+  /// \param ElemRef.
   CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target,
-                     ProgramStateRef St, const LocationContext *LCtx)
-      : AnyCXXConstructorCall(CE, Target, St, LCtx) {}
+                     ProgramStateRef St, const LocationContext *LCtx,
+                     CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyCXXConstructorCall(CE, Target, St, LCtx, ElemRef) {}
 
   CXXConstructorCall(const CXXConstructorCall &Other) = default;
 
@@ -941,8 +969,9 @@ class CXXInheritedConstructorCall : public AnyCXXConstructorCall {
 protected:
   CXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *CE,
                               const MemRegion *Target, ProgramStateRef St,
-                              const LocationContext *LCtx)
-      : AnyCXXConstructorCall(CE, Target, St, LCtx) {}
+                              const LocationContext *LCtx,
+                              CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyCXXConstructorCall(CE, Target, St, LCtx, ElemRef) {}
 
   CXXInheritedConstructorCall(const CXXInheritedConstructorCall &Other) =
       default;
@@ -1003,8 +1032,9 @@ class CXXAllocatorCall : public AnyFunctionCall {
 
 protected:
   CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St,
-                   const LocationContext *LCtx)
-      : AnyFunctionCall(E, St, LCtx) {}
+                   const LocationContext *LCtx,
+                   CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(E, St, LCtx, ElemRef) {}
   CXXAllocatorCall(const CXXAllocatorCall &Other) = default;
 
   void cloneTo(void *Dest) const override { new (Dest) CXXAllocatorCall(*this); }
@@ -1084,8 +1114,9 @@ class CXXDeallocatorCall : public AnyFunctionCall {
 
 protected:
   CXXDeallocatorCall(const CXXDeleteExpr *E, ProgramStateRef St,
-                     const LocationContext *LCtx)
-      : AnyFunctionCall(E, St, LCtx) {}
+                     const LocationContext *LCtx,
+                     CFGBlock::ConstCFGElementRef ElemRef)
+      : AnyFunctionCall(E, St, LCtx, ElemRef) {}
   CXXDeallocatorCall(const CXXDeallocatorCall &Other) = default;
 
   void cloneTo(void *Dest) const override {
@@ -1136,8 +1167,9 @@ class ObjCMethodCall : public CallEvent {
 
 protected:
   ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St,
-                 const LocationContext *LCtx)
-      : CallEvent(Msg, St, LCtx) {
+                 const LocationContext *LCtx,
+                 CFGBlock::ConstCFGElementRef ElemRef)
+      : CallEvent(Msg, St, LCtx, ElemRef) {
     Data = nullptr;
   }
 
@@ -1265,34 +1297,36 @@ class CallEventManager {
   }
 
   template <typename T, typename Arg>
-  T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx) {
+  T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx,
+            CFGBlock::ConstCFGElementRef ElemRef) {
     static_assert(sizeof(T) == sizeof(CallEventTemplateTy),
                   "CallEvent subclasses are not all the same size");
-    return new (allocate()) T(A, St, LCtx);
+    return new (allocate()) T(A, St, LCtx, ElemRef);
   }
 
   template <typename T, typename Arg1, typename Arg2>
-  T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx) {
+  T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx,
+            CFGBlock::ConstCFGElementRef ElemRef) {
     static_assert(sizeof(T) == sizeof(CallEventTemplateTy),
                   "CallEvent subclasses are not all the same size");
-    return new (allocate()) T(A1, A2, St, LCtx);
+    return new (allocate()) T(A1, A2, St, LCtx, ElemRef);
   }
 
   template <typename T, typename Arg1, typename Arg2, typename Arg3>
   T *create(Arg1 A1, Arg2 A2, Arg3 A3, ProgramStateRef St,
-            const LocationContext *LCtx) {
+            const LocationContext *LCtx, CFGBlock::ConstCFGElementRef ElemRef) {
     static_assert(sizeof(T) == sizeof(CallEventTemplateTy),
                   "CallEvent subclasses are not all the same size");
-    return new (allocate()) T(A1, A2, A3, St, LCtx);
+    return new (allocate()) T(A1, A2, A3, St, LCtx, ElemRef);
   }
 
   template <typename T, typename Arg1, typename Arg2, typename Arg3,
             typename Arg4>
   T *create(Arg1 A1, Arg2 A2, Arg3 A3, Arg4 A4, ProgramStateRef St,
-            const LocationContext *LCtx) {
+            const LocationContext *LCtx, CFGBlock::ConstCFGElementRef ElemRef) {
     static_assert(sizeof(T) == sizeof(CallEventTemplateTy),
                   "CallEvent subclasses are not all the same size");
-    return new (allocate()) T(A1, A2, A3, A4, St, LCtx);
+    return new (allocate()) T(A1, A2, A3, A4, St, LCtx, ElemRef);
   }
 
 public:
@@ -1304,50 +1338,57 @@ public:
 
   /// Gets a call event for a function call, Objective-C method call,
   /// a 'new', or a 'delete' call.
-  CallEventRef<>
-  getCall(const Stmt *S, ProgramStateRef State,
-          const LocationContext *LC);
+  CallEventRef<> getCall(const Stmt *S, ProgramStateRef State,
+                         const LocationContext *LC,
+                         CFGBlock::ConstCFGElementRef ElemRef);
 
-  CallEventRef<>
-  getSimpleCall(const CallExpr *E, ProgramStateRef State,
-                const LocationContext *LCtx);
+  CallEventRef<> getSimpleCall(const CallExpr *E, ProgramStateRef State,
+                               const LocationContext *LCtx,
+                               CFGBlock::ConstCFGElementRef ElemRef);
 
   CallEventRef<ObjCMethodCall>
   getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State,
-                    const LocationContext *LCtx) {
-    return create<ObjCMethodCall>(E, State, LCtx);
+                    const LocationContext *LCtx,
+                    CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<ObjCMethodCall>(E, State, LCtx, ElemRef);
   }
 
   CallEventRef<CXXConstructorCall>
   getCXXConstructorCall(const CXXConstructExpr *E, const MemRegion *Target,
-                        ProgramStateRef State, const LocationContext *LCtx) {
-    return create<CXXConstructorCall>(E, Target, State, LCtx);
+                        ProgramStateRef State, const LocationContext *LCtx,
+                        CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<CXXConstructorCall>(E, Target, State, LCtx, ElemRef);
   }
 
   CallEventRef<CXXInheritedConstructorCall>
   getCXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *E,
                                  const MemRegion *Target, ProgramStateRef State,
-                                 const LocationContext *LCtx) {
-    return create<CXXInheritedConstructorCall>(E, Target, State, LCtx);
+                                 const LocationContext *LCtx,
+                                 CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<CXXInheritedConstructorCall>(E, Target, State, LCtx, ElemRef);
   }
 
   CallEventRef<CXXDestructorCall>
   getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
                        const MemRegion *Target, bool IsBase,
-                       ProgramStateRef State, const LocationContext *LCtx) {
-    return create<CXXDestructorCall>(DD, Trigger, Target, IsBase, State, LCtx);
+                       ProgramStateRef State, const LocationContext *LCtx,
+                       CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<CXXDestructorCall>(DD, Trigger, Target, IsBase, State, LCtx,
+                                     ElemRef);
   }
 
   CallEventRef<CXXAllocatorCall>
   getCXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef State,
-                      const LocationContext *LCtx) {
-    return create<CXXAllocatorCall>(E, State, LCtx);
+                      const LocationContext *LCtx,
+                      CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<CXXAllocatorCall>(E, State, LCtx, ElemRef);
   }
 
   CallEventRef<CXXDeallocatorCall>
   getCXXDeallocatorCall(const CXXDeleteExpr *E, ProgramStateRef State,
-                        const LocationContext *LCtx) {
-    return create<CXXDeallocatorCall>(E, State, LCtx);
+                        const LocationContext *LCtx,
+                        CFGBlock::ConstCFGElementRef ElemRef) {
+    return create<CXXDeallocatorCall>(E, State, LCtx, ElemRef);
   }
 };
 
index d736615..8c3d912 100644 (file)
@@ -234,6 +234,11 @@ public:
     return (*G.roots_begin())->getLocation().getLocationContext();
   }
 
+  CFGBlock::ConstCFGElementRef getCFGElementRef() const {
+    const CFGBlock *blockPtr = currBldrCtx ? currBldrCtx->getBlock() : nullptr;
+    return {blockPtr, currStmtIdx};
+  }
+
   void GenerateAutoTransition(ExplodedNode *N);
   void enqueueEndOfPath(ExplodedNodeSet &S);
   void GenerateCallExitNode(ExplodedNode *N);
index f05cd92..67e8afb 100644 (file)
@@ -3446,7 +3446,8 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
               OS << OpCallE->getDirectCallee()->getDeclName();
             } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
               auto &CEMgr = BRC.getStateManager().getCallEventManager();
-              CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+              CallEventRef<> Call =
+                  CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
               if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
                 OS << D->getDeclName();
               else
index e11e509..379163e 100644 (file)
@@ -234,8 +234,8 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
     os << "Operator 'new'";
   } else {
     assert(isa<ObjCMessageExpr>(S));
-    CallEventRef<ObjCMethodCall> Call =
-        Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+    CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(
+        cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0});
 
     switch (Call->getMessageKind()) {
     case OCM_Message:
@@ -250,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
     }
   }
 
-  std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
+  std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0});
   auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
 
   // If index is not found, we assume that the symbol was returned.
index 8516e36..195940e 100644 (file)
@@ -287,6 +287,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
 
 ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
                                         const ProgramPointTag *Tag) const {
+
   if (const Expr *E = getOriginExpr()) {
     if (IsPreVisit)
       return PreStmt(E, getLocationContext(), Tag);
@@ -295,11 +296,13 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
 
   const Decl *D = getDecl();
   assert(D && "Cannot get a program point without a statement or decl");
+  assert(ElemRef.getParent() &&
+         "Cannot get a program point without a CFGElementRef");
 
   SourceLocation Loc = getSourceRange().getBegin();
   if (IsPreVisit)
-    return PreImplicitCall(D, Loc, getLocationContext(), Tag);
-  return PostImplicitCall(D, Loc, getLocationContext(), Tag);
+    return PreImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag);
+  return PostImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag);
 }
 
 SVal CallEvent::getArgSVal(unsigned Index) const {
@@ -1373,23 +1376,24 @@ void ObjCMethodCall::getInitialStackFrameContents(
 
 CallEventRef<>
 CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
-                                const LocationContext *LCtx) {
+                                const LocationContext *LCtx,
+                                CFGBlock::ConstCFGElementRef ElemRef) {
   if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE))
-    return create<CXXMemberCall>(MCE, State, LCtx);
+    return create<CXXMemberCall>(MCE, State, LCtx, ElemRef);
 
   if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
     const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
     if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
       if (MD->isInstance())
-        return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+        return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
 
   } else if (CE->getCallee()->getType()->isBlockPointerType()) {
-    return create<BlockCall>(CE, State, LCtx);
+    return create<BlockCall>(CE, State, LCtx, ElemRef);
   }
 
   // Otherwise, it's a normal function call, static member function call, or
   // something we can't reason about.
-  return create<SimpleFunctionCall>(CE, State, LCtx);
+  return create<SimpleFunctionCall>(CE, State, LCtx, ElemRef);
 }
 
 CallEventRef<>
@@ -1397,12 +1401,14 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
                             ProgramStateRef State) {
   const LocationContext *ParentCtx = CalleeCtx->getParent();
   const LocationContext *CallerCtx = ParentCtx->getStackFrame();
+  CFGBlock::ConstCFGElementRef ElemRef = {CalleeCtx->getCallSiteBlock(),
+                                          CalleeCtx->getIndex()};
   assert(CallerCtx && "This should not be used for top-level stack frames");
 
   const Stmt *CallSite = CalleeCtx->getCallSite();
 
   if (CallSite) {
-    if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
+    if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx, ElemRef))
       return Out;
 
     SValBuilder &SVB = State->getStateManager().getSValBuilder();
@@ -1411,10 +1417,11 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
     SVal ThisVal = State->getSVal(ThisPtr);
 
     if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
-      return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
+      return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx,
+                                   ElemRef);
     else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
       return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
-                                            CallerCtx);
+                                            CallerCtx, ElemRef);
     else {
       // All other cases are handled by getCall.
       llvm_unreachable("This is not an inlineable statement");
@@ -1444,19 +1451,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
 
   return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
                               E.getAs<CFGBaseDtor>().has_value(), State,
-                              CallerCtx);
+                              CallerCtx, ElemRef);
 }
 
 CallEventRef<> CallEventManager::getCall(const Stmt *S, ProgramStateRef State,
-                                         const LocationContext *LC) {
+                                         const LocationContext *LC,
+                                         CFGBlock::ConstCFGElementRef ElemRef) {
   if (const auto *CE = dyn_cast<CallExpr>(S)) {
-    return getSimpleCall(CE, State, LC);
+    return getSimpleCall(CE, State, LC, ElemRef);
   } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
-    return getCXXAllocatorCall(NE, State, LC);
+    return getCXXAllocatorCall(NE, State, LC, ElemRef);
   } else if (const auto *DE = dyn_cast<CXXDeleteExpr>(S)) {
-    return getCXXDeallocatorCall(DE, State, LC);
+    return getCXXDeallocatorCall(DE, State, LC, ElemRef);
   } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
-    return getObjCMethodCall(ME, State, LC);
+    return getObjCMethodCall(ME, State, LC, ElemRef);
   } else {
     return nullptr;
   }
index 977c2b7..bd5781a 100644 (file)
@@ -1313,7 +1313,8 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
   else {
     NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
     const LocationContext *LCtx = Pred->getLocationContext();
-    PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx);
+    PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx,
+                        getCFGElementRef());
     Bldr.generateNode(PP, Pred->getState(), Pred);
   }
   Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
@@ -1361,7 +1362,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
         static SimpleProgramPointTag PT(
             "ExprEngine", "Skipping automatic 0 length array destruction, "
                           "which shouldn't be in the CFG.");
-        PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT);
+        PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx,
+                            getCFGElementRef(), &PT);
         NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
         Bldr.generateSink(PP, Pred->getState(), Pred);
         return;
@@ -1378,7 +1380,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
 
   static SimpleProgramPointTag PT("ExprEngine",
                                   "Prepare for object destruction");
-  PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT);
+  PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, getCFGElementRef(),
+                     &PT);
   Pred = Bldr.generateNode(PP, state, Pred);
 
   if (!Pred)
@@ -1406,7 +1409,7 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
     const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl();
     const CXXDestructorDecl *Dtor = RD->getDestructor();
 
-    PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx);
+    PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx, getCFGElementRef());
     NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
     Bldr.generateNode(PP, Pred->getState(), Pred);
     return;
@@ -1439,7 +1442,8 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
 
         static SimpleProgramPointTag PT(
             "ExprEngine", "Skipping 0 length array delete destruction");
-        PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT);
+        PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx,
+                            getCFGElementRef(), &PT);
         NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
         Bldr.generateNode(PP, Pred->getState(), Pred);
         return;
@@ -1453,7 +1457,8 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
   NodeBuilder Bldr(Pred, Dst, getBuilderContext());
   static SimpleProgramPointTag PT("ExprEngine",
                                   "Prepare for object destruction");
-  PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT);
+  PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx,
+                     getCFGElementRef(), &PT);
   Pred = Bldr.generateNode(PP, State, Pred);
 
   if (!Pred)
@@ -1513,7 +1518,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
         static SimpleProgramPointTag PT(
             "ExprEngine", "Skipping member 0 length array destruction, which "
                           "shouldn't be in the CFG.");
-        PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT);
+        PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx,
+                            getCFGElementRef(), &PT);
         NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
         Bldr.generateSink(PP, Pred->getState(), Pred);
         return;
@@ -1529,7 +1535,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
 
   static SimpleProgramPointTag PT("ExprEngine",
                                   "Prepare for object destruction");
-  PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT);
+  PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, getCFGElementRef(),
+                     &PT);
   Pred = Bldr.generateNode(PP, State, Pred);
 
   if (!Pred)
@@ -1565,7 +1572,7 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
     NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
     PostImplicitCall PP(D.getDestructorDecl(getContext()),
                         D.getBindTemporaryExpr()->getBeginLoc(),
-                        Pred->getLocationContext());
+                        Pred->getLocationContext(), getCFGElementRef());
     Bldr.generateNode(PP, State, Pred);
     return;
   }
index 6eb3728..e876397 100644 (file)
@@ -357,7 +357,8 @@ SVal ExprEngine::computeObjectUnderConstruction(
       };
 
       if (const auto *CE = dyn_cast<CallExpr>(E)) {
-        CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
+        CallEventRef<> Caller =
+            CEMgr.getSimpleCall(CE, State, LCtx, getCFGElementRef());
         if (std::optional<SVal> V = getArgLoc(Caller))
           return *V;
         else
@@ -365,14 +366,15 @@ SVal ExprEngine::computeObjectUnderConstruction(
       } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
         // Don't bother figuring out the target region for the future
         // constructor because we won't need it.
-        CallEventRef<> Caller =
-            CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx);
+        CallEventRef<> Caller = CEMgr.getCXXConstructorCall(
+            CCE, /*Target=*/nullptr, State, LCtx, getCFGElementRef());
         if (std::optional<SVal> V = getArgLoc(Caller))
           return *V;
         else
           break;
       } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
-        CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
+        CallEventRef<> Caller =
+            CEMgr.getObjCMethodCall(ME, State, LCtx, getCFGElementRef());
         if (std::optional<SVal> V = getArgLoc(Caller))
           return *V;
         else
@@ -726,9 +728,9 @@ void ExprEngine::handleConstructor(const Expr *E,
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
   CallEventRef<> Call =
       CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall(
-                CIE, TargetRegion, State, LCtx)
+                CIE, TargetRegion, State, LCtx, getCFGElementRef())
           : (CallEventRef<>)CEMgr.getCXXConstructorCall(
-                CE, TargetRegion, State, LCtx);
+                CE, TargetRegion, State, LCtx, getCFGElementRef());
 
   ExplodedNodeSet DstPreVisit;
   getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this);
@@ -869,7 +871,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
     // it would interrupt the analysis instead.
     static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor");
     // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway.
-    PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T);
+    PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx,
+                        getCFGElementRef(), &T);
     NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
     Bldr.generateNode(PP, Pred->getState(), Pred);
     return;
@@ -894,8 +897,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
   }
 
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
-  CallEventRef<CXXDestructorCall> Call =
-      CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
+  CallEventRef<CXXDestructorCall> Call = CEMgr.getCXXDestructorCall(
+      DtorDecl, S, Dest, IsBaseDtor, State, LCtx, getCFGElementRef());
 
   PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
                                 Call->getSourceRange().getBegin(),
@@ -925,7 +928,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
                                 "Error evaluating New Allocator Call");
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
   CallEventRef<CXXAllocatorCall> Call =
-    CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+      CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef());
 
   ExplodedNodeSet DstPreCall;
   getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
@@ -1023,7 +1026,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
 
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
   CallEventRef<CXXAllocatorCall> Call =
-    CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+      CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef());
 
   if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
     // Invalidate placement args.
@@ -1124,7 +1127,7 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
 
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
   CallEventRef<CXXDeallocatorCall> Call = CEMgr.getCXXDeallocatorCall(
-      CDE, Pred->getState(), Pred->getLocationContext());
+      CDE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
 
   ExplodedNodeSet DstPreCall;
   getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this);
index 5452847..ac8b500 100644 (file)
@@ -610,8 +610,8 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
   // Get the call in its initial state. We use this as a template to perform
   // all the checks.
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
-  CallEventRef<> CallTemplate
-    = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
+  CallEventRef<> CallTemplate = CEMgr.getSimpleCall(
+      CE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
 
   // Evaluate the function call.  We try each of the checkers
   // to see if the can evaluate the function call.
@@ -837,7 +837,8 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
   State = bindReturnValue(Call, Pred->getLocationContext(), State);
 
   // And make the result node.
-  Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+  static SimpleProgramPointTag PT("ExprEngine", "Conservative eval call");
+  Bldr.generateNode(Call.getProgramPoint(false, &PT), State, Pred);
 }
 
 ExprEngine::CallInlinePolicy
index 25c36e9..8072531 100644 (file)
@@ -148,8 +148,8 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
                                   ExplodedNode *Pred,
                                   ExplodedNodeSet &Dst) {
   CallEventManager &CEMgr = getStateManager().getCallEventManager();
-  CallEventRef<ObjCMethodCall> Msg =
-    CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+  CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(
+      ME, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
 
   // There are three cases for the receiver:
   //   (1) it is definitely nil,
diff --git a/clang/test/Analysis/PR60412.cpp b/clang/test/Analysis/PR60412.cpp
new file mode 100644 (file)
index 0000000..da56c68
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.deadcode.UnreachableCode -verify %s
+// expected-no-diagnostics
+
+struct Test {
+  Test() {}
+  ~Test();
+};
+
+int foo() {
+  struct a {
+    Test b, c;
+  } d;
+  return 1;
+}
+
+int main() {
+  if (foo()) return 1; // <- this used to warn as unreachable
+}
diff --git a/clang/test/Analysis/analysis-after-multiple-dtors.cpp b/clang/test/Analysis/analysis-after-multiple-dtors.cpp
new file mode 100644 (file)
index 0000000..a8f6d38
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct Test {
+  Test() {}
+  ~Test();
+};
+
+int foo() {
+  struct a {
+  // The dtor invocation of 'b' and 'c' used to create
+  // a loop in the egraph and the analysis stopped after
+  // this point.
+    Test b, c;
+  } d;
+  return 1;
+}
+
+int main() {
+  if (foo()) {
+  }
+
+  int x;
+  int y = x;
+  // expected-warning@-1{{Assigned value is garbage or undefined}}
+  (void)y;
+}
index 14fb035..e8b237b 100644 (file)
@@ -89,11 +89,14 @@ class CallDescriptionConsumer : public ExprEngineConsumer {
 
     CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager();
     CallEventRef<> Call = [=, &CEMgr]() -> CallEventRef<CallEvent> {
+      CFGBlock::ConstCFGElementRef ElemRef = {SFC->getCallSiteBlock(),
+                                              SFC->getIndex()};
       if (std::is_base_of<CallExpr, T>::value)
-        return CEMgr.getCall(E, State, SFC);
+        return CEMgr.getCall(E, State, SFC, ElemRef);
       if (std::is_same<T, CXXConstructExpr>::value)
         return CEMgr.getCXXConstructorCall(cast<CXXConstructExpr>(E),
-                                           /*Target=*/nullptr, State, SFC);
+                                           /*Target=*/nullptr, State, SFC,
+                                           ElemRef);
       llvm_unreachable("Only these expressions are supported for now.");
     }();