[clang] [NFC][analyzer] Spread use of 'Expr*' instead of 'Stmt*' (PR #188319)

DonĂ¡t Nagy via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 25 07:07:20 PDT 2026


https://github.com/NagyDonat updated https://github.com/llvm/llvm-project/pull/188319

>From 4225f23a4bd93394a8c1a7004b4e561cef06f829 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Thu, 19 Mar 2026 19:49:31 +0100
Subject: [PATCH 01/15] [NFC] Change first arg type of ProgramState::BindExpr
 to Expr*

The dynamic type of this first argument is always (a subtype of)
`Expr*` (in all calls of this method), but previously it would have
accepted any `Stmt*` which was needlessly broad and confusing
(especially since it contradicted the name of the method).

In `ExprEngine::Visit` I introduced `cast<Expr>` expressions which are
justified by the fact that they are in a switch over the Kind of the
statement (in branches that correspond to expression kinds).

In `processCallExit` I introduced a `dyn_cast` because it wasn't
immediately obvious that `const Stmt *CE` (the call site of a stack
frame) is in fact always an expression.

However, follow-up commits in this series do demonstrate that `CE` is in
fact always an expression, so they eliminate this `dyn_cast` and justify
the NFC label on this commit.
---
 .../Core/PathSensitive/ProgramState.h         |  2 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 19 +++++++++++--------
 .../Core/ExprEngineCallAndReturn.cpp          |  3 ++-
 .../lib/StaticAnalyzer/Core/ProgramState.cpp  |  6 +++---
 4 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index d3dd6ca124b7f..adcb9c86ab1fa 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -280,7 +280,7 @@ class ProgramState : public llvm::FoldingSetNode {
 
   /// Create a new state by binding the value 'V' to the statement 'S' in the
   /// state's environment.
-  [[nodiscard]] ProgramStateRef BindExpr(const Stmt *S,
+  [[nodiscard]] ProgramStateRef BindExpr(const Expr *S,
                                          const LocationContext *LCtx, SVal V,
                                          bool Invalidate = true) const;
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e9522a7975515..497468937adce 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1885,7 +1885,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       // GNU __null is a pointer-width integer, not an actual pointer.
       ProgramStateRef state = Pred->getState();
       state = state->BindExpr(
-          S, Pred->getLocationContext(),
+          cast<Expr>(S), Pred->getLocationContext(),
           svalBuilder.makeIntValWithWidth(getContext().VoidPtrTy, 0));
       Bldr.generateNode(S, Pred, state);
       break;
@@ -2017,7 +2017,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       const LocationContext *LCtx = Pred->getLocationContext();
       for (const auto I : PreVisit) {
         ProgramStateRef State = I->getState();
-        State = State->BindExpr(S, LCtx, *ConstantVal);
+        State = State->BindExpr(cast<Expr>(S), LCtx, *ConstantVal);
         if (IsTemporary)
           State = createTemporaryRegionIfNeeded(State, LCtx,
                                                 cast<Expr>(S),
@@ -2443,13 +2443,15 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       const auto *PE = cast<PseudoObjectExpr>(S);
       if (const Expr *Result = PE->getResultExpr()) {
         SVal V = state->getSVal(Result, Pred->getLocationContext());
-        Bldr.generateNode(S, Pred,
-                          state->BindExpr(S, Pred->getLocationContext(), V));
+        Bldr.generateNode(
+            S, Pred,
+            state->BindExpr(cast<Expr>(S), Pred->getLocationContext(), V));
       }
       else
         Bldr.generateNode(S, Pred,
-                          state->BindExpr(S, Pred->getLocationContext(),
-                                                   UnknownVal()));
+                          state->BindExpr(cast<Expr>(S),
+                                          Pred->getLocationContext(),
+                                          UnknownVal()));
 
       Bldr.addNodes(Dst);
       break;
@@ -2464,8 +2466,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       const auto *OIE = cast<ObjCIndirectCopyRestoreExpr>(S);
       const Expr *E = OIE->getSubExpr();
       SVal V = state->getSVal(E, Pred->getLocationContext());
-      Bldr.generateNode(S, Pred,
-              state->BindExpr(S, Pred->getLocationContext(), V));
+      Bldr.generateNode(
+          S, Pred,
+          state->BindExpr(cast<Expr>(S), Pred->getLocationContext(), V));
       Bldr.addNodes(Dst);
       break;
     }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index b4dc4fa8a077d..eb2c0cfc85b4e 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -311,7 +311,8 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
         }
       }
 
-      State = State->BindExpr(CE, CallerCtx, V);
+      if (const auto *CEExpr = dyn_cast<Expr>(CE))
+        State = State->BindExpr(CEExpr, CallerCtx, V);
     }
 
     // Bind the constructed object value to CXXConstructExpr.
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 87485daa6e5c9..8df5ad11f4c87 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -298,9 +298,9 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
   return V;
 }
 
-ProgramStateRef ProgramState::BindExpr(const Stmt *S,
-                                           const LocationContext *LCtx,
-                                           SVal V, bool Invalidate) const{
+ProgramStateRef ProgramState::BindExpr(const Expr *S,
+                                       const LocationContext *LCtx, SVal V,
+                                       bool Invalidate) const {
   Environment NewEnv =
     getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V,
                                       Invalidate);

>From 2cd77b264c711727a00701a09d1912a99c83087f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 14:00:35 +0100
Subject: [PATCH 02/15] [NFC] Take Expr* in
 AnalysisDecContextManager::getStackFrame

...instead of taking `Stmt*`; because this function is only called once
and there the argument is `Expr*`.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 2368b8ed911e7..103619d5b7684 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -475,9 +475,9 @@ class AnalysisDeclContextManager {
   /// \copydoc LocationContextManager::getStackFrame()
   const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC,
                                          const LocationContext *Parent,
-                                         const Stmt *S, const CFGBlock *Block,
+                                         const Expr *E, const CFGBlock *Block,
                                          unsigned BlockCount, unsigned Index) {
-    return LocCtxMgr.getStackFrame(ADC, Parent, S, Block, BlockCount, Index);
+    return LocCtxMgr.getStackFrame(ADC, Parent, E, Block, BlockCount, Index);
   }
 
   BodyFarm &getBodyFarm();

>From 567f576f3f25fa0fede626960f86fffdefe77a37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 14:13:42 +0100
Subject: [PATCH 03/15] [NFC] Take Expr* in AnalysisDecContext::getStackFrame

...instead of taking `Stmt*`; because this function is only called once
and there the argument is `Expr*`.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 2 +-
 clang/lib/Analysis/AnalysisDeclContext.cpp         | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 103619d5b7684..298aa300fb770 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -179,7 +179,7 @@ class AnalysisDeclContext {
 
   /// \copydoc LocationContextManager::getStackFrame()
   const StackFrameContext *getStackFrame(LocationContext const *ParentLC,
-                                         const Stmt *S, const CFGBlock *Blk,
+                                         const Expr *E, const CFGBlock *Blk,
                                          unsigned BlockCount, unsigned Index);
 
   /// \copydoc LocationContextManager::getBlockInvocationContext()
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index 6f153e7e65255..2aadde41b31b5 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -312,9 +312,9 @@ BodyFarm &AnalysisDeclContextManager::getBodyFarm() { return FunctionBodyFarm; }
 
 const StackFrameContext *
 AnalysisDeclContext::getStackFrame(const LocationContext *ParentLC,
-                                   const Stmt *S, const CFGBlock *Blk,
+                                   const Expr *E, const CFGBlock *Blk,
                                    unsigned BlockCount, unsigned Index) {
-  return getLocationContextManager().getStackFrame(this, ParentLC, S, Blk,
+  return getLocationContextManager().getStackFrame(this, ParentLC, E, Blk,
                                                    BlockCount, Index);
 }
 

>From 20c295e587a2fe97a8eb550678b774d4ef314f99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 14:55:59 +0100
Subject: [PATCH 04/15] [NFC] Remove 6-parameter
 AnalysisDecContextManager::getStackFrame

...because it was redundant with `AnalysisDeclContext::getStackFrame`.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 8 --------
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp        | 2 +-
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 298aa300fb770..73a6ed219c2b4 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -472,14 +472,6 @@ class AnalysisDeclContextManager {
                                    0);
   }
 
-  /// \copydoc LocationContextManager::getStackFrame()
-  const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC,
-                                         const LocationContext *Parent,
-                                         const Expr *E, const CFGBlock *Block,
-                                         unsigned BlockCount, unsigned Index) {
-    return LocCtxMgr.getStackFrame(ADC, Parent, E, Block, BlockCount, Index);
-  }
-
   BodyFarm &getBodyFarm();
 
   /// Discard all previously created AnalysisDeclContexts.
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index cd52083a278ae..478da1cd75b7e 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -186,7 +186,7 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
         break;
   assert(Idx < Sz);
 
-  return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
+  return ADC->getStackFrame(LCtx, E, B, BlockCount, Idx);
 }
 
 const ParamVarRegion

>From 683029a59eb2fdaf6e9e04aef863a67f4d97a92a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 15:09:30 +0100
Subject: [PATCH 05/15] [NFC] Take Expr* in
 LocationContextManager::getStackFrame

...instead of taking `Stmt*`; because this function is only called
twice, with an `Expr*` and a nullpointer passed to this argument.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 4 ++--
 clang/lib/Analysis/AnalysisDeclContext.cpp         | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 73a6ed219c2b4..1b14913ae0145 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -397,14 +397,14 @@ class LocationContextManager {
   ///
   /// \param ADC        The AnalysisDeclContext.
   /// \param ParentLC   The parent context of this newly created context.
-  /// \param S          The call.
+  /// \param E          The call expression.
   /// \param Block      The basic block.
   /// \param BlockCount The current count of entering into \p Blk.
   /// \param Index      The index of \p Blk.
   /// \returns The context for \p D with parent context \p ParentLC.
   const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC,
                                          const LocationContext *ParentLC,
-                                         const Stmt *S, const CFGBlock *Block,
+                                         const Expr *E, const CFGBlock *Block,
                                          unsigned BlockCount, unsigned Index);
 
   /// Obtain a context of the block invocation using its parent context.
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index 2aadde41b31b5..93d9039c0ae84 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -428,15 +428,15 @@ void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) {
 //===----------------------------------------------------------------------===//
 
 const StackFrameContext *LocationContextManager::getStackFrame(
-    AnalysisDeclContext *ctx, const LocationContext *parent, const Stmt *s,
+    AnalysisDeclContext *ctx, const LocationContext *parent, const Expr *E,
     const CFGBlock *blk, unsigned blockCount, unsigned idx) {
   llvm::FoldingSetNodeID ID;
-  StackFrameContext::Profile(ID, ctx, parent, s, blk, blockCount, idx);
+  StackFrameContext::Profile(ID, ctx, parent, E, blk, blockCount, idx);
   void *InsertPos;
   auto *L =
    cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos));
   if (!L) {
-    L = new StackFrameContext(ctx, parent, s, blk, blockCount, idx, ++NewID);
+    L = new StackFrameContext(ctx, parent, E, blk, blockCount, idx, ++NewID);
     Contexts.InsertNode(L, InsertPos);
   }
   return L;

>From 908c0a5cbc170e6cd202b4c2f1d26d7114eac762 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 15:29:18 +0100
Subject: [PATCH 06/15] [NFC] Capitalize variable names in
 LocationContextManager::getStackFrame

As these lines are already touched in this PR, let's align them with the
coding style.
---
 clang/lib/Analysis/AnalysisDeclContext.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index 93d9039c0ae84..241b2ac8f6d05 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -428,15 +428,15 @@ void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) {
 //===----------------------------------------------------------------------===//
 
 const StackFrameContext *LocationContextManager::getStackFrame(
-    AnalysisDeclContext *ctx, const LocationContext *parent, const Expr *E,
-    const CFGBlock *blk, unsigned blockCount, unsigned idx) {
+    AnalysisDeclContext *Ctx, const LocationContext *Parent, const Expr *E,
+    const CFGBlock *Blk, unsigned BlockCount, unsigned Idx) {
   llvm::FoldingSetNodeID ID;
-  StackFrameContext::Profile(ID, ctx, parent, E, blk, blockCount, idx);
+  StackFrameContext::Profile(ID, Ctx, Parent, E, Blk, BlockCount, Idx);
   void *InsertPos;
   auto *L =
    cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos));
   if (!L) {
-    L = new StackFrameContext(ctx, parent, E, blk, blockCount, idx, ++NewID);
+    L = new StackFrameContext(Ctx, Parent, E, Blk, BlockCount, Idx, ++NewID);
     Contexts.InsertNode(L, InsertPos);
   }
   return L;

>From a210ed8c5ac49526b4abdd98bb0b5dbe4ffaf3a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 15:41:01 +0100
Subject: [PATCH 07/15] [NFC] Clarify that this index is a _statement_ index

... and fix a comment that was outright wrong about its role.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 8 ++++----
 clang/lib/Analysis/AnalysisDeclContext.cpp         | 7 ++++---
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 1b14913ae0145..fe7abe832425c 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -399,13 +399,13 @@ class LocationContextManager {
   /// \param ParentLC   The parent context of this newly created context.
   /// \param E          The call expression.
   /// \param Block      The basic block.
-  /// \param BlockCount The current count of entering into \p Blk.
-  /// \param Index      The index of \p Blk.
-  /// \returns The context for \p D with parent context \p ParentLC.
+  /// \param BlockCount The current count of entering into \p Block.
+  /// \param StmtIdx    The index of the call expression within \p Block.
+  /// \returns The stack frame context corresponding to the call.
   const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC,
                                          const LocationContext *ParentLC,
                                          const Expr *E, const CFGBlock *Block,
-                                         unsigned BlockCount, unsigned Index);
+                                         unsigned BlockCount, unsigned StmtIdx);
 
   /// Obtain a context of the block invocation using its parent context.
   ///
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index 241b2ac8f6d05..72521303f4aa2 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -429,14 +429,15 @@ void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) {
 
 const StackFrameContext *LocationContextManager::getStackFrame(
     AnalysisDeclContext *Ctx, const LocationContext *Parent, const Expr *E,
-    const CFGBlock *Blk, unsigned BlockCount, unsigned Idx) {
+    const CFGBlock *Blk, unsigned BlockCount, unsigned StmtIdx) {
   llvm::FoldingSetNodeID ID;
-  StackFrameContext::Profile(ID, Ctx, Parent, E, Blk, BlockCount, Idx);
+  StackFrameContext::Profile(ID, Ctx, Parent, E, Blk, BlockCount, StmtIdx);
   void *InsertPos;
   auto *L =
    cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos));
   if (!L) {
-    L = new StackFrameContext(Ctx, Parent, E, Blk, BlockCount, Idx, ++NewID);
+    L = new StackFrameContext(Ctx, Parent, E, Blk, BlockCount, StmtIdx,
+                              ++NewID);
     Contexts.InsertNode(L, InsertPos);
   }
   return L;

>From aaebee234d5d81dc7875786275ee511f95f3a8cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 16:09:03 +0100
Subject: [PATCH 08/15] [NFC] Update type of StackFrameContext::CallSite to
 Expr*

Previously it was `Stmt*`, but it was always initialized with an
`Expr*`.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index fe7abe832425c..9c0bce1d7eeba 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -300,7 +300,7 @@ class StackFrameContext : public LocationContext {
   friend class LocationContextManager;
 
   // The call site where this stack frame is established.
-  const Stmt *CallSite;
+  const Expr *CallSite;
 
   // The parent block of the call site.
   const CFGBlock *Block;
@@ -314,9 +314,9 @@ class StackFrameContext : public LocationContext {
   const unsigned Index;
 
   StackFrameContext(AnalysisDeclContext *ADC, const LocationContext *ParentLC,
-                    const Stmt *S, const CFGBlock *Block, unsigned BlockCount,
+                    const Expr *E, const CFGBlock *Block, unsigned BlockCount,
                     unsigned Index, int64_t ID)
-      : LocationContext(StackFrame, ADC, ParentLC, ID), CallSite(S),
+      : LocationContext(StackFrame, ADC, ParentLC, ID), CallSite(E),
         Block(Block), BlockCount(BlockCount), Index(Index) {}
 
 public:
@@ -335,10 +335,10 @@ class StackFrameContext : public LocationContext {
   void Profile(llvm::FoldingSetNodeID &ID) override;
 
   static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ADC,
-                      const LocationContext *ParentLC, const Stmt *S,
+                      const LocationContext *ParentLC, const Expr *E,
                       const CFGBlock *Block, unsigned BlockCount,
                       unsigned Index) {
-    ProfileCommon(ID, StackFrame, ADC, ParentLC, S);
+    ProfileCommon(ID, StackFrame, ADC, ParentLC, E);
     ID.AddPointer(Block);
     ID.AddInteger(BlockCount);
     ID.AddInteger(Index);

>From 9d637ed2caf63d7967ee8c62eb4e4c0a01911d6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 17:21:42 +0100
Subject: [PATCH 09/15] [NFC] Let StackFrameContext::getCallSite return Expr*

Previously the static return type of `StackFrameContext::getCallSite`
was `Stmt*`, but the dynamic type was always some subtype of `Expr*`.
This commit changes the return type to `Expr*` and updates most
code that calls `getCallSite()`.

Most of these changes are just cosmetical (the new `Expr *E` will be
used in the same way as the old `Stmt *S`), but I also eliminated a few
`cast<Expr>` and `dyn_cast<Expr>` expressions which were made redundant
by this change.

Note that there are still some places where the return value of
`getCallSite()` is stored in a `Stmt*`: there other uses of that
variable can store non-`Expr` statements in it.

There is also one tricky case with the ReplayWithoutInlining flag which
will be handled in a separate commit.
---
 clang/include/clang/Analysis/AnalysisDeclContext.h    |  2 +-
 clang/lib/Analysis/AnalysisDeclContext.cpp            |  8 ++++----
 .../UninitializedObjectChecker.cpp                    |  2 +-
 clang/lib/StaticAnalyzer/Core/BugReporter.cpp         |  2 +-
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp           |  2 +-
 clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp       |  6 +++---
 .../StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp   | 11 ++++-------
 clang/lib/StaticAnalyzer/Core/MemRegion.cpp           |  8 ++++----
 8 files changed, 19 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h
index 9c0bce1d7eeba..edbb8ffb96fcd 100644
--- a/clang/include/clang/Analysis/AnalysisDeclContext.h
+++ b/clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -322,7 +322,7 @@ class StackFrameContext : public LocationContext {
 public:
   ~StackFrameContext() override = default;
 
-  const Stmt *getCallSite() const { return CallSite; }
+  const Expr *getCallSite() const { return CallSite; }
 
   const CFGBlock *getCallSiteBlock() const { return Block; }
 
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index 72521303f4aa2..266d20632e3cc 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -515,9 +515,9 @@ void LocationContext::dumpStack(raw_ostream &Out) const {
         Out << "Calling " << AnalysisDeclContext::getFunctionName(D);
       else
         Out << "Calling anonymous code";
-      if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
+      if (const Expr *E = cast<StackFrameContext>(LCtx)->getCallSite()) {
         Out << " at line ";
-        printLocation(Out, SM, S->getBeginLoc());
+        printLocation(Out, SM, E->getBeginLoc());
       }
       break;
     case Block:
@@ -557,8 +557,8 @@ void LocationContext::printJson(raw_ostream &Out, const char *NL,
         Out << "anonymous code";
 
       Out << "\", \"location\": ";
-      if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
-        printSourceLocationAsJson(Out, S->getBeginLoc(), SM);
+      if (const Expr *E = cast<StackFrameContext>(LCtx)->getCallSite()) {
+        printSourceLocationAsJson(Out, E->getBeginLoc(), SM);
       } else {
         Out << "null";
       }
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 98b0fbeb72fbb..6d4389fda8753 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -172,7 +172,7 @@ void UninitializedObjectChecker::checkEndFunction(
     return;
 
   PathDiagnosticLocation LocUsedForUniqueing;
-  const Stmt *CallSite = Context.getStackFrame()->getCallSite();
+  const Expr *CallSite = Context.getStackFrame()->getCallSite();
   if (CallSite)
     LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
         CallSite, Context.getSourceManager(), Node->getLocationContext());
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 4c066520b668f..51039da20afba 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -323,7 +323,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
   CallExitEnd CExit = P.castAs<CallExitEnd>();
 
   // FIXME: Use CallEvent to abstract this over all calls.
-  const Stmt *CallSite = CExit.getCalleeContext()->getCallSite();
+  const Expr *CallSite = CExit.getCalleeContext()->getCallSite();
   const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
   if (!CE)
     return {};
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 478da1cd75b7e..2c3da53cdaeba 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -1478,7 +1478,7 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
                                           CalleeCtx->getIndex()};
   assert(CallerCtx && "This should not be used for top-level stack frames");
 
-  const Stmt *CallSite = CalleeCtx->getCallSite();
+  const Expr *CallSite = CalleeCtx->getCallSite();
 
   if (CallSite) {
     if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx, ElemRef))
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 701c7fdc88497..5807b4db61fef 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -231,7 +231,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
 
         unsigned NVCaller = getNumVisited(CallerLCtx, SFC->getCallSiteBlock());
         return computeObjectUnderConstruction(
-            cast<Expr>(SFC->getCallSite()), State, NVCaller, CallerLCtx,
+            SFC->getCallSite(), State, NVCaller, CallerLCtx,
             RTC->getConstructionContext(), CallOpts);
       } else {
         // We are on the top frame of the analysis. We do not know where is the
@@ -454,8 +454,8 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
         assert(!isa<BlockInvocationContext>(CallerLCtx));
       }
 
-      return updateObjectsUnderConstruction(V,
-          cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+      return updateObjectsUnderConstruction(
+          V, SFC->getCallSite(), State, CallerLCtx,
           RTC->getConstructionContext(), CallOpts);
     }
     case ConstructionContext::ElidedTemporaryObjectKind: {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index eb2c0cfc85b4e..58d889c94e5d0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -259,7 +259,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
   // look up the first enclosing stack frame.
   const StackFrameContext *CallerCtx = CalleeCtx->getParent()->getStackFrame();
 
-  const Stmt *CE = CalleeCtx->getCallSite();
+  const Expr *CE = CalleeCtx->getCallSite();
   ProgramStateRef State = CEBNode->getState();
   // Find the last statement in the function and the corresponding basic block.
   auto [LastSt, Blk] = getLastStmt(CEBNode);
@@ -304,15 +304,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
         QualType ReturnedTy =
             CallEvent::getDeclaredResultType(CalleeCtx->getDecl());
         if (!ReturnedTy.isNull()) {
-          if (const Expr *Ex = dyn_cast<Expr>(CE)) {
-            V = adjustReturnValue(V, Ex->getType(), ReturnedTy,
-                                  getStoreManager());
-          }
+          V = adjustReturnValue(V, CE->getType(), ReturnedTy,
+                                getStoreManager());
         }
       }
 
-      if (const auto *CEExpr = dyn_cast<Expr>(CE))
-        State = State->BindExpr(CEExpr, CallerCtx, V);
+      State = State->BindExpr(CE, CallerCtx, V);
     }
 
     // Bind the constructed object value to CXXConstructExpr.
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index f20e79ae675a4..29a8fbfd8f52b 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1044,19 +1044,19 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
   if (PVD) {
     unsigned Index = PVD->getFunctionScopeIndex();
     const StackFrameContext *SFC = LC->getStackFrame();
-    const Stmt *CallSite = SFC->getCallSite();
+    const Expr *CallSite = SFC->getCallSite();
     if (CallSite) {
       const Decl *D = SFC->getDecl();
       if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
         if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
-          return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+          return getSubRegion<ParamVarRegion>(CallSite, Index,
                                               getStackArgumentsRegion(SFC));
       } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
         if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
-          return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+          return getSubRegion<ParamVarRegion>(CallSite, Index,
                                               getStackArgumentsRegion(SFC));
       } else {
-        return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+        return getSubRegion<ParamVarRegion>(CallSite, Index,
                                             getStackArgumentsRegion(SFC));
       }
     }

>From b6033567e48842639371fa0aa27e8812617c09f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 17:59:45 +0100
Subject: [PATCH 10/15] [NFC] Use Expr* in ReplayWithoutInlining logic

`ReplayWithoutInlining` is a program state trait which ensures that if
the inlined evaluation of a function fails and the analyzer "jumps back
to" the point before the call, the problematic function won't be inlined
again.

The value associated with this type is a `const void *`, but it is
primarily used as a boolean: if the inlining logic sees a non-null
value, then it doesn't inline the call (and removes this trait).

The actual pointer value is only used in an assertion which verifies
that the next call after the "jump back to" point is indeed the
problematic call.

This "sanity check" pointer is the origin expression of the call.
Before this commit the code converted `const Stmt*` to `const void*`
(both when storing the pointer and when checking it), now it converts
`const Expr*` to `const void*` (in both places).

I also added a FIXME comment to highlight the fact that this expression
pointer may be null (certain calls -- IIUC constructor calls --
originate from declarations and their `OriginExpr` is null), and I
strongly suspect that in that case this "do not inline" logic may fail
to activate.

However, note that `replayWithoutInlining()` is only called after
calling `markShouldNotInline()` on the problematic function, so the
function won't end up being inlined even if this process fails.
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp           | 10 +++++++---
 .../StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp    |  8 ++++----
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 497468937adce..f242728652406 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2481,7 +2481,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
   const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame();
   assert(CalleeSF && CallerSF);
   ExplodedNode *BeforeProcessingCall = nullptr;
-  const Stmt *CE = CalleeSF->getCallSite();
+  const Expr *CE = CalleeSF->getCallSite();
 
   // Find the first node before we started processing the call expression.
   while (N) {
@@ -2518,9 +2518,13 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
       BeforeProcessingCall->getLocationContext(), CE, nullptr, &PT);
   // Add the special flag to GDM to signal retrying with no inlining.
   // Note, changing the state ensures that we are not going to cache out.
+  // NOTE: This stores the call site (CE) in the state trait, but only the
+  // the actual pointer value is only checked by an assertion; for the analysis
+  // only the presence or absence of this trait matters.
+  // FIXME: I suspect that CE may be a nullpointer, which will be interpreted
+  // as the absence of this state trait (and does not prevent caching out).
   ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
-  NewNodeState =
-    NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE));
+  NewNodeState = NewNodeState->set<ReplayWithoutInlining>(CE);
 
   // Make the new node a successor of BeforeProcessingCall.
   bool IsNew = false;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 58d889c94e5d0..aa9adb19540b8 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -585,7 +585,7 @@ void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call,
 }
 
 static ProgramStateRef getInlineFailedState(ProgramStateRef State,
-                                            const Stmt *CallE) {
+                                            const Expr *CallE) {
   const void *ReplayState = State->get<ReplayWithoutInlining>();
   if (!ReplayState)
     return nullptr;
@@ -1223,11 +1223,11 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
     return;
   }
 
-  // Try to inline the call.
-  // The origin expression here is just used as a kind of checksum;
-  // this should still be safe even for CallEvents that don't come from exprs.
   const Expr *E = Call.getOriginExpr();
 
+  // Try to inline the call. Note that `getInlineFailedStates` only uses its
+  // second argument in an assertion, so this should still be safe even for
+  // calls that don't come from exprs.
   ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
   if (InlinedFailedState) {
     // If we already tried once and failed, make sure we don't retry later.

>From 847f491e6c56deed3f9dc24cc46fcd9fe27aacfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 19:22:11 +0100
Subject: [PATCH 11/15] [NFC] Drive-by cleanup in
 MemRegionManager::getVarRegion

This is just drive-by cleanup unrelated to the main goals of this PR.

I noticed that the same `getSubRegion` call appears multiple times, so I
unified the execution paths leading to it.

I also eliminated confusing a name collision (there is already a `D` in
this function, we shouldn't reuse its name) and added a FIXME comment
because I suspect that the logic which I refactored is actually
irrelevant.
---
 clang/lib/StaticAnalyzer/Core/MemRegion.cpp | 25 ++++++++++++---------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 29a8fbfd8f52b..5544d254929e5 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1046,19 +1046,24 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
     const StackFrameContext *SFC = LC->getStackFrame();
     const Expr *CallSite = SFC->getCallSite();
     if (CallSite) {
-      const Decl *D = SFC->getDecl();
-      if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
-        if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
-          return getSubRegion<ParamVarRegion>(CallSite, Index,
-                                              getStackArgumentsRegion(SFC));
-      } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
-        if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
-          return getSubRegion<ParamVarRegion>(CallSite, Index,
-                                              getStackArgumentsRegion(SFC));
-      } else {
+      const Decl *CalleeDecl = SFC->getDecl();
+      bool ValidParam = true;
+      if (const auto *FD = dyn_cast<FunctionDecl>(CalleeDecl)) {
+        ValidParam =
+            (Index < FD->param_size() && FD->getParamDecl(Index) == PVD);
+      } else if (const auto *BD = dyn_cast<BlockDecl>(CalleeDecl)) {
+        ValidParam =
+            (Index < BD->param_size() && BD->getParamDecl(Index) == PVD);
+      }
+
+      if (ValidParam) {
         return getSubRegion<ParamVarRegion>(CallSite, Index,
                                             getStackArgumentsRegion(SFC));
       }
+      // FIXME: If ValidParam was false, this method would "fall through" and
+      // eventually return a NonParamVarRegion for this ParamVarDecl. That
+      // seems to be buggy, so I strongly suspect that ValidParam always ends
+      // up being true.
     }
   }
 

>From 2c4411b21e80479d2f69128329d73cb6e0535d7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Tue, 24 Mar 2026 19:34:11 +0100
Subject: [PATCH 12/15] [NFC] Drive-by cleanup: delete 'else' after 'return'

Another trivial unrelated change in code that was adjacent to the one
modified by earlier commits.
---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 2c3da53cdaeba..7bfc1697b6e4c 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -1492,13 +1492,11 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
     if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
       return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx,
                                    ElemRef);
-    else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
+    if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
       return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
                                             CallerCtx, ElemRef);
-    else {
-      // All other cases are handled by getCall.
-      llvm_unreachable("This is not an inlineable statement");
-    }
+    // All other cases are handled by getCall.
+    llvm_unreachable("This is not an inlineable statement");
   }
 
   // Fall back to the CFG. The only thing we haven't handled yet is

>From dccc881cfa931b121771e00d3b0d2d55d55c145f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Wed, 25 Mar 2026 13:37:05 +0100
Subject: [PATCH 13/15] [NFC] Update variable names and a commit

---
 .../clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h    | 4 ++--
 clang/lib/StaticAnalyzer/Core/ProgramState.cpp                | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index adcb9c86ab1fa..94139b1788fa2 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -278,9 +278,9 @@ class ProgramState : public llvm::FoldingSetNode {
   // Binding and retrieving values to/from the environment and symbolic store.
   //==---------------------------------------------------------------------==//
 
-  /// Create a new state by binding the value 'V' to the statement 'S' in the
+  /// Create a new state by binding the value 'V' to the expression 'E' in the
   /// state's environment.
-  [[nodiscard]] ProgramStateRef BindExpr(const Expr *S,
+  [[nodiscard]] ProgramStateRef BindExpr(const Expr *E,
                                          const LocationContext *LCtx, SVal V,
                                          bool Invalidate = true) const;
 
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 8df5ad11f4c87..19ea96a7488f6 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -298,11 +298,11 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
   return V;
 }
 
-ProgramStateRef ProgramState::BindExpr(const Expr *S,
+ProgramStateRef ProgramState::BindExpr(const Expr *E,
                                        const LocationContext *LCtx, SVal V,
                                        bool Invalidate) const {
   Environment NewEnv =
-    getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V,
+    getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(E, LCtx), V,
                                       Invalidate);
   if (NewEnv == Env)
     return this;

>From f6d408ccc4a5a2f1b5c128f4106fb445e7145797 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Wed, 25 Mar 2026 13:43:15 +0100
Subject: [PATCH 14/15] [NFC] Describe planned follow-up in FIXME comments

These comments are admittedly verbose but they will be (hopefully)
eliminated within a few weeks or months.
---
 .../clang/StaticAnalyzer/Core/PathSensitive/Environment.h   | 6 ++++++
 clang/lib/StaticAnalyzer/Core/Environment.cpp               | 5 +++++
 clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp   | 4 ++++
 3 files changed, 15 insertions(+)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h
index 498e36e1431fa..8d7e2039fc98b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h
@@ -32,6 +32,12 @@ class SymbolReaper;
 /// This allows the environment to manage context-sensitive bindings,
 /// which is essentially for modeling recursive function analysis, among
 /// other things.
+/// FIXME: Use 'Expr' instead of 'Stmt' because associating a result with a
+/// non-expression statement does not make sense. Currently the environment
+/// never contains non-expression statements; and there is only one
+/// easy-to-eliminate hack in 'processCallExit' and 'Environment::getSVal' that
+/// constructs and handles 'EnvironmentEntry' instances with a 'ReturnStmt' as
+/// the 'first' part.
 class EnvironmentEntry : public std::pair<const Stmt *,
                                           const StackFrameContext *> {
 public:
diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 5d9cbf681a5e9..bfe973afc3210 100644
--- a/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -121,6 +121,11 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
     return *svalBuilder.getConstantVal(cast<Expr>(S));
 
   case Stmt::ReturnStmtClass: {
+    // FIXME: Move this logic to ExprEngine::processCallExit (the only location
+    // passes a ReturnStmt to this method) and then there will be no need to
+    // accept non-expression statements in getSVal (in fact, it will be
+    // possible to change the first member of EnvironmentEntry from const Stmt*
+    // to const Expr*).
     const auto *RS = cast<ReturnStmt>(S);
     if (const Expr *RE = RS->getRetValue())
       return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index aa9adb19540b8..4a5d7c9bb9bcd 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -297,6 +297,10 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
   if (CE) {
     if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
       const LocationContext *LCtx = CEBNode->getLocationContext();
+      // FIXME: This tries to look up the return statement in the environment,
+      // which is special cased to look up the subexpression RS->getRetValue()
+      // in environment. Instead of relying on this hack, pass
+      // RS->getRetValue() to getSVal() after checking it for nullness.
       SVal V = State->getSVal(RS, LCtx);
 
       // Ensure that the return type matches the type of the returned Expr.

>From fec4d71d618c6e8f8199f4a323f3782d8d1be345 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Wed, 25 Mar 2026 15:06:58 +0100
Subject: [PATCH 15/15] Satisfy git-clang-format

---
 clang/lib/StaticAnalyzer/Core/ProgramState.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 19ea96a7488f6..297719af34209 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -301,9 +301,8 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
 ProgramStateRef ProgramState::BindExpr(const Expr *E,
                                        const LocationContext *LCtx, SVal V,
                                        bool Invalidate) const {
-  Environment NewEnv =
-    getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(E, LCtx), V,
-                                      Invalidate);
+  Environment NewEnv = getStateManager().EnvMgr.bindExpr(
+      Env, EnvironmentEntry(E, LCtx), V, Invalidate);
   if (NewEnv == Env)
     return this;
 



More information about the cfe-commits mailing list