[clang] [NFC][analyzer] Cleanup checker calls in `ExprEngine::ProcessStmt`, `ExprEngine::Visit*` (PR #218691)

Gábor Tóthvári via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 1 07:13:51 PDT 2026


https://github.com/tigbr updated https://github.com/llvm/llvm-project/pull/218691

>From cd8de4a86d37c856cb2b4f9025ae9cf10f48c791 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 5 Aug 2026 14:12:55 +0200
Subject: [PATCH 01/39] Introduce refactored algorithmic scheme in
 `ExprEngine::ProcessStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 36 ++++++++++++++++----
 1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e6349eb4eba2a..1054129110698 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1100,6 +1100,10 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() {
   return &cleanupTag;
 }
 
+static bool justRunCheckersAsPreVisit(const Stmt *S) { return false; }
+
+static bool justRunCheckersAsPostVisit(const Stmt *S) { return false; }
+
 void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
   // Reclaim any unnecessary nodes in the ExplodedGraph.
   G.reclaimRecentlyAllocatedNodes();
@@ -1115,17 +1119,35 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
   } else
     CleanedStates.insert(Pred);
 
-  // Visit the statement.
-  ExplodedNodeSet Dst;
+  ExplodedNodeSet PreVisited;
   for (const auto I : CleanedStates) {
-    ExplodedNodeSet DstI;
-    // Visit the statement.
-    Visit(currStmt, I, DstI);
-    Dst.insert(DstI);
+    ExplodedNodeSet Tmp;
+    if (justRunCheckersAsPreVisit(currStmt)) {
+      getCheckerManager().runCheckersForPreStmt(Tmp, I, currStmt, *this);
+      PreVisited.insert(Tmp);
+    } else
+      PreVisited.insert(I);
+  }
+
+  ExplodedNodeSet Visited;
+  for (const auto I : PreVisited) {
+    ExplodedNodeSet Tmp;
+    Visit(currStmt, I, Tmp);
+    Visited.insert(Tmp);
+  }
+
+  ExplodedNodeSet PostVisited;
+  for (const auto I : Visited) {
+    ExplodedNodeSet Tmp;
+    if (justRunCheckersAsPostVisit(currStmt)) {
+      getCheckerManager().runCheckersForPostStmt(Tmp, I, currStmt, *this);
+      PostVisited.insert(Tmp);
+    } else
+      PostVisited.insert(I);
   }
 
   // Enqueue the new nodes onto the work list.
-  Engine.enqueueStmtNodes(Dst, getCurrBlock(), currStmtIdx);
+  Engine.enqueueStmtNodes(PostVisited, getCurrBlock(), currStmtIdx);
 }
 
 void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) {

>From 9477afa996830090f15e54bd681af1ea0007662a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 5 Aug 2026 15:19:47 +0200
Subject: [PATCH 02/39] `ExprEngine::VisitCXXBindTemporaryExpr`

---
 .../Core/PathSensitive/ExprEngine.h           |  3 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 51 +++++++++++--------
 2 files changed, 30 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 68d4362aca941..63447450a8962 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -628,8 +628,7 @@ class ExprEngine {
                                        ExplodedNodeSet &Dst);
 
   void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
-                                 ExplodedNodeSet &PreVisit,
-                                 ExplodedNodeSet &Dst);
+                                 ExplodedNode *Pred, ExplodedNodeSet &Dst);
 
   void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
                          ExplodedNodeSet &Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1054129110698..d7ff19ace0cde 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1100,9 +1100,23 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() {
   return &cleanupTag;
 }
 
-static bool justRunCheckersAsPreVisit(const Stmt *S) { return false; }
+static bool justRunCheckersAsPreVisit(const Stmt *S) {
+  switch (S->getStmtClass()) {
+  default:
+    return false;
+  case Stmt::CXXBindTemporaryExprClass:
+    return true;
+  }
+}
 
-static bool justRunCheckersAsPostVisit(const Stmt *S) { return false; }
+static bool justRunCheckersAsPostVisit(const Stmt *S) {
+  switch (S->getStmtClass()) {
+  default:
+    return false;
+  case Stmt::CXXBindTemporaryExprClass:
+    return true;
+  }
+}
 
 void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
   // Reclaim any unnecessary nodes in the ExplodedGraph.
@@ -1648,7 +1662,7 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
 }
 
 void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
-                                           ExplodedNodeSet &PreVisit,
+                                           ExplodedNode *Pred,
                                            ExplodedNodeSet &Dst) {
   // This is a fallback solution in case we didn't have a construction
   // context when we were constructing the temporary. Otherwise the map should
@@ -1656,21 +1670,19 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
   if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) {
     // In case we don't have temporary destructors in the CFG, do not mark
     // the initialization - we would otherwise never clean it up.
-    Dst = PreVisit;
+    Dst.insert(Pred);
     return;
   }
-  for (ExplodedNode *Node : PreVisit) {
-    ProgramStateRef State = Node->getState();
-    const StackFrame *SF = Node->getStackFrame();
-    if (!getObjectUnderConstruction(State, BTE, SF)) {
-      // FIXME: Currently the state might also already contain the marker due to
-      // incorrect handling of temporaries bound to default parameters; for
-      // those, we currently skip the CXXBindTemporaryExpr but rely on adding
-      // temporary destructor nodes.
-      State = addObjectUnderConstruction(State, BTE, SF, UnknownVal());
-    }
-    Dst.insert(Engine.makePostStmtNode(BTE, State, Node));
+  ProgramStateRef State = Pred->getState();
+  const StackFrame *SF = Pred->getStackFrame();
+  if (!getObjectUnderConstruction(State, BTE, SF)) {
+    // FIXME: Currently the state might also already contain the marker due to
+    // incorrect handling of temporaries bound to default parameters; for
+    // those, we currently skip the CXXBindTemporaryExpr but rely on adding
+    // temporary destructor nodes.
+    State = addObjectUnderConstruction(State, BTE, SF, UnknownVal());
   }
+  Dst.insert(Engine.makePostStmtNode(BTE, State, Pred));
 }
 
 ProgramStateRef ExprEngine::escapeValues(ProgramStateRef State,
@@ -1898,14 +1910,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       // Handled due to fully linearised CFG.
       break;
 
-    case Stmt::CXXBindTemporaryExprClass: {
-      ExplodedNodeSet PreVisit;
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-      ExplodedNodeSet Next;
-      VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
-      getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
+    case Stmt::CXXBindTemporaryExprClass:
+      VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::ArrayInitLoopExprClass:
       VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), Pred, Dst);

>From c24938ee56578c57d1055a8719b5eca52f09cf51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Fri, 7 Aug 2026 17:19:10 +0200
Subject: [PATCH 03/39] `ExprEngine::VisitStmtExpr`

---
 .../Core/PathSensitive/ExprEngine.h              |  3 +++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp     | 16 ++--------------
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp    | 15 +++++++++++++++
 3 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 63447450a8962..ca48f72c475bb 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -618,6 +618,9 @@ class ExprEngine {
   void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
                                      ExplodedNode *Pred, ExplodedNodeSet &Dst);
 
+  void VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+                     ExplodedNodeSet &Dst);
+
   /// VisitUnaryOperator - Transfer function logic for unary operators.
   void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred,
                           ExplodedNodeSet &Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d7ff19ace0cde..a64d8eb3feddb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2300,21 +2300,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
                                     Dst);
       break;
 
-    case Stmt::StmtExprClass: {
-      const auto *SE = cast<StmtExpr>(S);
-
-      if (SE->getSubStmt()->body_empty()) {
-        // Empty statement expression.
-        assert(SE->getType() == getContext().VoidTy
-               && "Empty statement expression must have void type.");
-      } else if (const auto *LastExpr =
-                     dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
-        SVal Val = Pred->getState()->getSVal(LastExpr, Pred->getStackFrame());
-        Pred = Engine.makeNodeWithBinding(Pred, SE, Val);
-      }
-      Dst.insert(Pred);
+    case Stmt::StmtExprClass:
+      VisitStmtExpr(cast<StmtExpr>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::UnaryOperatorClass: {
       const auto *U = cast<UnaryOperator>(S);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 6127328cefe23..aaadd4c9167ed 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -855,6 +855,21 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
   getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
 }
 
+void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+                               ExplodedNodeSet &Dst) {
+  const auto *SE = cast<StmtExpr>(S);
+  if (SE->getSubStmt()->body_empty()) {
+    // Empty statement expression.
+    assert(SE->getType() == getContext().VoidTy &&
+           "Empty statement expression must have void type.");
+  } else if (const auto *LastExpr =
+                 dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
+    SVal Val = Pred->getState()->getSVal(LastExpr, Pred->getStackFrame());
+    Pred = Engine.makeNodeWithBinding(Pred, SE, Val);
+  }
+  Dst.insert(Pred);
+}
+
 void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
                                     ExplodedNodeSet &Dst) {
   // FIXME: Prechecks eventually go in ::Visit().

>From 669559997cd4ca7b6cef84c45936cf1de879e863 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Fri, 7 Aug 2026 17:39:22 +0200
Subject: [PATCH 04/39] `ExprEngine::OffsetOfExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a64d8eb3feddb..646c917a86fad 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::OffsetOfExprClass:
     return true;
   }
 }
@@ -1114,6 +1115,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::OffsetOfExprClass:
     return true;
   }
 }
@@ -2283,17 +2285,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst);
       break;
 
-    case Stmt::OffsetOfExprClass: {
-      ExplodedNodeSet PreVisit;
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-
-      ExplodedNodeSet PostVisit;
-      for (const auto Node : PreVisit)
-        VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit);
-
-      getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+    case Stmt::OffsetOfExprClass:
+      VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::UnaryExprOrTypeTraitExprClass:
       VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), Pred,

>From 19d7f0cd055ab130057c2ed6d6f378acbab661cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 14:42:21 +0200
Subject: [PATCH 05/39] `ExprEngine::VisitUnaryOperator`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 14 +--
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 93 +++++++++----------
 2 files changed, 50 insertions(+), 57 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 646c917a86fad..b59023468685e 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1106,6 +1106,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
     return false;
   case Stmt::CXXBindTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
+  case Stmt::UnaryOperatorClass:
     return true;
   }
 }
@@ -1116,6 +1117,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
     return false;
   case Stmt::CXXBindTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
+  case Stmt::UnaryOperatorClass:
     return true;
   }
 }
@@ -2298,17 +2300,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitStmtExpr(cast<StmtExpr>(S), Pred, Dst);
       break;
 
-    case Stmt::UnaryOperatorClass: {
-      const auto *U = cast<UnaryOperator>(S);
-      if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
-        ExplodedNodeSet Tmp;
-        VisitUnaryOperator(U, Pred, Tmp);
-        evalEagerlyAssumeBifurcation(Dst, Tmp, U);
-      }
-      else
-        VisitUnaryOperator(U, Pred, Dst);
+    case Stmt::UnaryOperatorClass:
+      VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::PseudoObjectExprClass: {
       const auto *PE = cast<PseudoObjectExpr>(S);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index aaadd4c9167ed..771009b9139a6 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -870,13 +870,8 @@ void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
   Dst.insert(Pred);
 }
 
-void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
+void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
                                     ExplodedNodeSet &Dst) {
-  // FIXME: Prechecks eventually go in ::Visit().
-  ExplodedNodeSet CheckedSet;
-  getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, U, *this);
-
-  ExplodedNodeSet EvalSet;
 
   // Lambda for handling the case when the operand is returned unchanged.
   auto MakeNodeForIdentityOp = [U, &Engine = Engine](ExplodedNode *N) {
@@ -885,12 +880,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
     return Engine.makeNodeWithBinding(N, U, SV);
   };
 
-  for (ExplodedNode *N : CheckedSet) {
+  auto VisitUnaryOperatorImpl = [U, Pred, &Engine = Engine, this,
+                                 MakeNodeForIdentityOp](ExplodedNodeSet &Dst) {
     switch (U->getOpcode()) {
     default: {
-      ExplodedNodeSet Tmp;
-      VisitIncrementDecrementOperator(U, N, Tmp);
-      EvalSet.insert(Tmp);
+      VisitIncrementDecrementOperator(U, Pred, Dst);
       break;
     }
     case UO_Real: {
@@ -899,13 +893,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
       // FIXME: We don't have complex SValues yet.
       if (Ex->getType()->isAnyComplexType()) {
         // Just report "Unknown."
-        EvalSet.insert(N);
+        Dst.insert(Pred);
         break;
       }
 
       // For all other types, UO_Real is an identity operation.
       assert (U->getType() == Ex->getType());
-      EvalSet.insert(MakeNodeForIdentityOp(N));
+      Dst.insert(MakeNodeForIdentityOp(Pred));
       break;
     }
 
@@ -914,12 +908,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
       // FIXME: We don't have complex SValues yet.
       if (Ex->getType()->isAnyComplexType()) {
         // Just report "Unknown."
-        EvalSet.insert(N);
+        Dst.insert(Pred);
         break;
       }
       // For all other types, UO_Imag returns 0.
       SVal X = svalBuilder.makeZeroVal(Ex->getType());
-      EvalSet.insert(Engine.makeNodeWithBinding(N, U, X));
+      Dst.insert(Engine.makeNodeWithBinding(Pred, U, X));
       break;
     }
 
@@ -931,12 +925,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
 
         if (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
           SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
-          EvalSet.insert(Engine.makeNodeWithBinding(N, U, SV));
+          Dst.insert(Engine.makeNodeWithBinding(Pred, U, SV));
           break;
         }
       }
       // Explicitly proceed with default handler for this case cascade.
-      EvalSet.insert(MakeNodeForIdentityOp(N));
+      Dst.insert(MakeNodeForIdentityOp(Pred));
       break;
     }
     case UO_Plus:
@@ -944,7 +938,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
       [[fallthrough]];
     case UO_Deref:
     case UO_Extension: {
-      EvalSet.insert(MakeNodeForIdentityOp(N));
+      Dst.insert(MakeNodeForIdentityOp(Pred));
       break;
     }
 
@@ -953,57 +947,62 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
     case UO_Not: {
       assert (!U->isGLValue());
       const Expr *Ex = U->getSubExpr()->IgnoreParens();
-      ProgramStateRef state = N->getState();
-      const StackFrame *SF = N->getStackFrame();
+      ProgramStateRef state = Pred->getState();
+      const StackFrame *SF = Pred->getStackFrame();
 
       // Get the value of the subexpression.
       SVal V = state->getSVal(Ex, SF);
 
       if (V.isUnknownOrUndef()) {
-        EvalSet.insert(Engine.makeNodeWithBinding(N, U, V));
+        Dst.insert(Engine.makeNodeWithBinding(Pred, U, V));
         break;
       }
 
       switch (U->getOpcode()) {
-        default:
-          llvm_unreachable("Invalid Opcode.");
-        case UO_Not:
-          // FIXME: Do we need to handle promotions?
-          state = state->BindExpr(
-              U, SF, svalBuilder.evalComplement(V.castAs<NonLoc>()));
-          break;
-        case UO_Minus:
-          // FIXME: Do we need to handle promotions?
-          state =
-              state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
-          break;
-        case UO_LNot:
-          // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
-          //
-          //  Note: technically we do "E == 0", but this is the same in the
-          //    transfer functions as "0 == E".
-          SVal Result;
-          if (std::optional<Loc> LV = V.getAs<Loc>()) {
+      default:
+        llvm_unreachable("Invalid Opcode.");
+      case UO_Not:
+        // FIXME: Do we need to handle promotions?
+        state = state->BindExpr(U, SF,
+                                svalBuilder.evalComplement(V.castAs<NonLoc>()));
+        break;
+      case UO_Minus:
+        // FIXME: Do we need to handle promotions?
+        state =
+            state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
+        break;
+      case UO_LNot:
+        // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
+        //
+        //  Note: technically we do "E == 0", but this is the same in the
+        //    transfer functions as "0 == E".
+        SVal Result;
+        if (std::optional<Loc> LV = V.getAs<Loc>()) {
           Loc X = svalBuilder.makeNullWithType(Ex->getType());
           Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
-          } else if (Ex->getType()->isFloatingType()) {
+        } else if (Ex->getType()->isFloatingType()) {
           // FIXME: handle floating point types.
           Result = UnknownVal();
-          } else {
+        } else {
           nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
           Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType());
-          }
+        }
 
-          state = state->BindExpr(U, SF, Result);
-          break;
+        state = state->BindExpr(U, SF, Result);
+        break;
       }
-      EvalSet.insert(Engine.makePostStmtNode(U, state, N));
+      Dst.insert(Engine.makePostStmtNode(U, state, Pred));
       break;
     }
     }
-  }
+  };
 
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this);
+  if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
+    ExplodedNodeSet Tmp;
+    VisitUnaryOperatorImpl(Tmp);
+    evalEagerlyAssumeBifurcation(Dst, Tmp, U);
+  } else
+    VisitUnaryOperatorImpl(Dst);
 }
 
 void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,

>From f26640fd93b0de980e72b727fded2953dcf07926 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 14:59:53 +0200
Subject: [PATCH 06/39] `ExprEngine::VisitPseudoObjectExpr`

---
 .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 3 +++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp             | 9 ++-------
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp            | 9 +++++++++
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index ca48f72c475bb..2b49948adce59 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -625,6 +625,9 @@ class ExprEngine {
   void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred,
                           ExplodedNodeSet &Dst);
 
+  void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred,
+                             ExplodedNodeSet &Dst);
+
   /// Handle ++ and -- (both pre- and post-increment).
   void VisitIncrementDecrementOperator(const UnaryOperator* U,
                                        ExplodedNode *Pred,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index b59023468685e..348944b1a5040 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2304,14 +2304,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst);
       break;
 
-    case Stmt::PseudoObjectExprClass: {
-      const auto *PE = cast<PseudoObjectExpr>(S);
-      SVal V = UnknownVal();
-      if (const Expr *Result = PE->getResultExpr())
-        V = Pred->getState()->getSVal(Result, Pred->getStackFrame());
-      Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V));
+    case Stmt::PseudoObjectExprClass:
+      VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S), Pred, Dst);
       break;
-    }
 
     case Expr::ObjCIndirectCopyRestoreExprClass: {
       // ObjCIndirectCopyRestoreExpr implies passing a temporary for
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 771009b9139a6..f72340e07028f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -1005,6 +1005,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
     VisitUnaryOperatorImpl(Dst);
 }
 
+void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,
+                                       ExplodedNode *Pred,
+                                       ExplodedNodeSet &Dst) {
+  SVal V = UnknownVal();
+  if (const Expr *Result = PE->getResultExpr())
+    V = Pred->getState()->getSVal(Result, Pred->getStackFrame());
+  Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V));
+}
+
 void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
                                                  ExplodedNode *Pred,
                                                  ExplodedNodeSet &Dst) {

>From 4876853f8f0fa3f4c958fa79b7e69312736d0409 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 15:17:49 +0200
Subject: [PATCH 07/39] `ExprEngine::VisitObjCIndirectCopyRestoreClass`

---
 .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h   |  4 ++++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp         | 12 +++---------
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp        | 11 +++++++++++
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 2b49948adce59..d9e626b51f38e 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -628,6 +628,10 @@ class ExprEngine {
   void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred,
                              ExplodedNodeSet &Dst);
 
+  void VisitObjCIndirectCopyRestoreClass(const ObjCIndirectCopyRestoreExpr *OIE,
+                                         ExplodedNode *Pred,
+                                         ExplodedNodeSet &Dst);
+
   /// Handle ++ and -- (both pre- and post-increment).
   void VisitIncrementDecrementOperator(const UnaryOperator* U,
                                        ExplodedNode *Pred,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 348944b1a5040..61c911495e4ec 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2308,17 +2308,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S), Pred, Dst);
       break;
 
-    case Expr::ObjCIndirectCopyRestoreExprClass: {
-      // ObjCIndirectCopyRestoreExpr implies passing a temporary for
-      // correctness of lifetime management.  Due to limited analysis
-      // of ARC, this is implemented as direct arg passing.
-      const auto *OIE = cast<ObjCIndirectCopyRestoreExpr>(S);
-      const Expr *E = OIE->getSubExpr();
-      SVal V = Pred->getState()->getSVal(E, Pred->getStackFrame());
-      Dst.insert(Engine.makeNodeWithBinding(Pred, OIE, V));
+    case Expr::ObjCIndirectCopyRestoreExprClass:
+      VisitObjCIndirectCopyRestoreClass(cast<ObjCIndirectCopyRestoreExpr>(S),
+                                        Pred, Dst);
       break;
     }
-  }
 }
 
 bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index f72340e07028f..c2abbed0942bd 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -1014,6 +1014,17 @@ void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,
   Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V));
 }
 
+void ExprEngine::VisitObjCIndirectCopyRestoreClass(
+    const ObjCIndirectCopyRestoreExpr *OIE, ExplodedNode *Pred,
+    ExplodedNodeSet &Dst) {
+  // ObjCIndirectCopyRestoreExpr implies passing a temporary for
+  // correctness of lifetime management.  Due to limited analysis
+  // of ARC, this is implemented as direct arg passing.
+  const Expr *E = OIE->getSubExpr();
+  SVal V = Pred->getState()->getSVal(E, Pred->getStackFrame());
+  Dst.insert(Engine.makeNodeWithBinding(Pred, OIE, V));
+}
+
 void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
                                                  ExplodedNode *Pred,
                                                  ExplodedNodeSet &Dst) {

>From ab222e44277d10e5f2b0a5a7fc173824f2e33300 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 15:56:56 +0200
Subject: [PATCH 08/39] `ExprEngine::VisitMaterializeTemporaryExpr`

---
 .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h |  5 ++---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp       | 14 +++++---------
 clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp    | 12 ++++++------
 3 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index d9e626b51f38e..08cac076ffad4 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -668,9 +668,8 @@ class ExprEngine {
                           ExplodedNodeSet &Dst);
 
   /// Create a C++ temporary object for an rvalue.
-  void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
-                                ExplodedNode *Pred,
-                                ExplodedNodeSet &Dst);
+  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE,
+                                     ExplodedNode *Pred, ExplodedNodeSet &Dst);
 
   void ConstructInitList(const Expr *Source, ArrayRef<Expr *> Args,
                          bool IsTransparent, ExplodedNode *Pred,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 61c911495e4ec..181900465d6a4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryOperatorClass:
     return true;
@@ -1116,6 +1117,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryOperatorClass:
     return true;
@@ -2232,16 +2234,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       break;
     }
 
-    case Expr::MaterializeTemporaryExprClass: {
-      const auto *MTE = cast<MaterializeTemporaryExpr>(S);
-      ExplodedNodeSet dstPrevisit;
-      getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
-      ExplodedNodeSet dstExpr;
-      for (const auto i : dstPrevisit)
-        CreateCXXTemporaryObject(MTE, i, dstExpr);
-      getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
+    case Expr::MaterializeTemporaryExprClass:
+      VisitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(S), Pred,
+                                    Dst);
       break;
-    }
 
     case Stmt::InitListExprClass: {
       const InitListExpr *E = cast<InitListExpr>(S);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 3df2d3d9e3674..4c6da8bc84c93 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -30,15 +30,15 @@
 using namespace clang;
 using namespace ento;
 
-void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
-                                          ExplodedNode *Pred,
-                                          ExplodedNodeSet &Dst) {
-  const Expr *tempExpr = ME->getSubExpr()->IgnoreParens();
+void ExprEngine::VisitMaterializeTemporaryExpr(
+    const MaterializeTemporaryExpr *MTE, ExplodedNode *Pred,
+    ExplodedNodeSet &Dst) {
+  const Expr *tempExpr = MTE->getSubExpr()->IgnoreParens();
   ProgramStateRef state = Pred->getState();
   const StackFrame *SF = Pred->getStackFrame();
 
-  state = createTemporaryRegionIfNeeded(state, SF, tempExpr, ME);
-  Dst.insert(Engine.makePostStmtNode(ME, state, Pred));
+  state = createTemporaryRegionIfNeeded(state, SF, tempExpr, MTE);
+  Dst.insert(Engine.makePostStmtNode(MTE, state, Pred));
 }
 
 void ExprEngine::performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred,

>From 6db5fb6bed563b30029ebd40b9d01750cdc52673 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 16:15:59 +0200
Subject: [PATCH 09/39] `ExprEngine::VisitCXXParenListInitExpr`

---
 .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h   | 3 +++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp               | 7 ++-----
 clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp            | 6 ++++++
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 08cac076ffad4..9abf64a75b808 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -643,6 +643,9 @@ class ExprEngine {
   void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
                          ExplodedNodeSet &Dst);
 
+  void VisitCXXParenListInitExpr(const CXXParenListInitExpr *E,
+                                 ExplodedNode *Pred, ExplodedNodeSet &Dst);
+
   void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
                         ExplodedNodeSet & Dst);
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 181900465d6a4..01dd5a4659817 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2245,12 +2245,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       break;
     }
 
-    case Expr::CXXParenListInitExprClass: {
-      const CXXParenListInitExpr *E = cast<CXXParenListInitExpr>(S);
-      ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred,
-                        Dst);
+    case Expr::CXXParenListInitExprClass:
+      VisitCXXParenListInitExpr(cast<CXXParenListInitExpr>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::MemberExprClass:
       VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 4c6da8bc84c93..2f459f76c3fd0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1102,6 +1102,12 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
   Dst.insert(Engine.makePostStmtNode(CS, state, Pred));
 }
 
+void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E,
+                                           ExplodedNode *Pred,
+                                           ExplodedNodeSet &Dst) {
+  ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, Dst);
+}
+
 void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
                                   ExplodedNodeSet &Dst) {
   // Get the this object region from StoreManager.

>From da0c6f92054c5058da050dc98c1c6b63fb99fa2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 18:15:47 +0200
Subject: [PATCH 10/39] `ExprEngine::VisitCastExpr`

---
 .../Core/PathSensitive/ExprEngine.h           |   7 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  30 +-
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 452 +++++++++---------
 3 files changed, 248 insertions(+), 241 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 9abf64a75b808..52d94d25ee096 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -546,9 +546,10 @@ class ExprEngine {
   void VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
                      ExplodedNodeSet &Dst);
 
-  /// VisitCast - Transfer function logic for all casts (implicit and explicit).
-  void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred,
-                 ExplodedNodeSet &Dst);
+  /// VisitCastExpr - Transfer function logic for all casts (implicit and
+  /// explicit).
+  void VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
+                     ExplodedNodeSet &Dst);
 
   /// VisitCompoundLiteralExpr - Transfer function logic for compound literals.
   void VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 01dd5a4659817..f244eb164e5de 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1104,6 +1104,16 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ImplicitCastExprClass:
+  case Stmt::CStyleCastExprClass:
+  case Stmt::CXXStaticCastExprClass:
+  case Stmt::CXXDynamicCastExprClass:
+  case Stmt::CXXReinterpretCastExprClass:
+  case Stmt::CXXConstCastExprClass:
+  case Stmt::CXXFunctionalCastExprClass:
+  case Stmt::BuiltinBitCastExprClass:
+  case Stmt::ObjCBridgedCastExprClass:
+  case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
@@ -1116,6 +1126,16 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ImplicitCastExprClass:
+  case Stmt::CStyleCastExprClass:
+  case Stmt::CXXStaticCastExprClass:
+  case Stmt::CXXDynamicCastExprClass:
+  case Stmt::CXXReinterpretCastExprClass:
+  case Stmt::CXXConstCastExprClass:
+  case Stmt::CXXFunctionalCastExprClass:
+  case Stmt::BuiltinBitCastExprClass:
+  case Stmt::ObjCBridgedCastExprClass:
+  case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
@@ -2224,15 +2244,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::CXXFunctionalCastExprClass:
     case Stmt::BuiltinBitCastExprClass:
     case Stmt::ObjCBridgedCastExprClass:
-    case Stmt::CXXAddrspaceCastExprClass: {
-      const auto *C = cast<CastExpr>(S);
-      ExplodedNodeSet dstExpr;
-      VisitCast(C, C->getSubExpr(), Pred, dstExpr);
-
-      // Handle the postvisit checks.
-      getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);
+    case Stmt::CXXAddrspaceCastExprClass:
+      VisitCastExpr(cast<CastExpr>(S), Pred, Dst);
       break;
-    }
 
     case Expr::MaterializeTemporaryExprClass:
       VisitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(S), Pred,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c2abbed0942bd..45c3fa14f5cf8 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -250,18 +250,14 @@ ExprEngine::handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
   return state;
 }
 
-void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
-                           ExplodedNode *Pred, ExplodedNodeSet &Dst) {
-
-  ExplodedNodeSet DstPreStmt;
-  getCheckerManager().runCheckersForPreStmt(DstPreStmt, Pred, CastE, *this);
+void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
+                               ExplodedNodeSet &Dst) {
+  const Expr *Ex = CastE->getSubExpr();
 
   if (CastE->getCastKind() == CK_LValueToRValue) {
-    for (ExplodedNode *Node : DstPreStmt) {
-      ProgramStateRef State = Node->getState();
-      const StackFrame *SF = Node->getStackFrame();
-      evalLoad(Dst, CastE, CastE, Node, State, State->getSVal(Ex, SF));
-    }
+    ProgramStateRef State = Pred->getState();
+    const StackFrame *SF = Pred->getStackFrame();
+    evalLoad(Dst, CastE, CastE, Pred, State, State->getSVal(Ex, SF));
     return;
   }
   if (CastE->getCastKind() == CK_LValueToRValueBitCast) {
@@ -269,12 +265,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
     ExplodedNodeSet DstEvalLoc;
 
     // Simulate the lvalue-to-rvalue conversion on `Ex`:
-    for (ExplodedNode *Node : DstPreStmt) {
-      ProgramStateRef State = Node->getState();
-      const StackFrame *SF = Node->getStackFrame();
-      evalLocation(DstEvalLoc, CastE, Ex, Node, State, State->getSVal(Ex, SF),
-                   true);
-    }
+    ProgramStateRef State = Pred->getState();
+    const StackFrame *SF = Pred->getStackFrame();
+    evalLocation(DstEvalLoc, CastE, Ex, Pred, State, State->getSVal(Ex, SF),
+                 true);
     // Simulate the operation that actually casts the original value to a new
     // value of the destination type :
 
@@ -303,228 +297,226 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
   if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
     T = ExCast->getTypeAsWritten();
 
-  for (ExplodedNode *Pred : DstPreStmt) {
+  ProgramStateRef state = Pred->getState();
+  const StackFrame *SF = Pred->getStackFrame();
+
+  switch (CastE->getCastKind()) {
+  case CK_LValueToRValue:
+  case CK_LValueToRValueBitCast:
+    llvm_unreachable("LValueToRValue casts handled earlier.");
+  case CK_ToVoid:
+    Dst.insert(Pred);
+    return;
+    // The analyzer doesn't do anything special with these casts,
+    // since it understands retain/release semantics already.
+  case CK_ARCProduceObject:
+  case CK_ARCConsumeObject:
+  case CK_ARCReclaimReturnedObject:
+  case CK_ARCExtendBlockObject: // Fall-through.
+  case CK_CopyAndAutoreleaseBlockObject:
+    // The analyser can ignore atomic casts for now, although some future
+    // checkers may want to make certain that you're not modifying the same
+    // value through atomic and nonatomic pointers.
+  case CK_AtomicToNonAtomic:
+  case CK_NonAtomicToAtomic:
+    // True no-ops.
+  case CK_NoOp:
+  case CK_ConstructorConversion:
+  case CK_UserDefinedConversion:
+  case CK_FunctionToPointerDecay:
+  case CK_BuiltinFnToFnPtr:
+  case CK_HLSLArrayRValue: {
+    // Copy the SVal of Ex to CastE.
     ProgramStateRef state = Pred->getState();
     const StackFrame *SF = Pred->getStackFrame();
-
-    switch (CastE->getCastKind()) {
-      case CK_LValueToRValue:
-      case CK_LValueToRValueBitCast:
-        llvm_unreachable("LValueToRValue casts handled earlier.");
-      case CK_ToVoid:
-        Dst.insert(Pred);
-        continue;
-        // The analyzer doesn't do anything special with these casts,
-        // since it understands retain/release semantics already.
-      case CK_ARCProduceObject:
-      case CK_ARCConsumeObject:
-      case CK_ARCReclaimReturnedObject:
-      case CK_ARCExtendBlockObject: // Fall-through.
-      case CK_CopyAndAutoreleaseBlockObject:
-        // The analyser can ignore atomic casts for now, although some future
-        // checkers may want to make certain that you're not modifying the same
-        // value through atomic and nonatomic pointers.
-      case CK_AtomicToNonAtomic:
-      case CK_NonAtomicToAtomic:
-        // True no-ops.
-      case CK_NoOp:
-      case CK_ConstructorConversion:
-      case CK_UserDefinedConversion:
-      case CK_FunctionToPointerDecay:
-      case CK_BuiltinFnToFnPtr:
-      case CK_HLSLArrayRValue: {
-        // Copy the SVal of Ex to CastE.
-        ProgramStateRef state = Pred->getState();
-        const StackFrame *SF = Pred->getStackFrame();
-        SVal V = state->getSVal(Ex, SF);
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
-        continue;
-      }
-      case CK_MemberPointerToBoolean:
-      case CK_PointerToBoolean: {
-        SVal V = state->getSVal(Ex, SF);
-        auto PTMSV = V.getAs<nonloc::PointerToMember>();
-        if (PTMSV)
-          V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
-        if (V.isUndef() || PTMSV) {
-          Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
-          continue;
-        }
-        // Explicitly proceed with default handler for this case cascade.
-        state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
-        continue;
-      }
-      case CK_Dependent:
-      case CK_ArrayToPointerDecay:
-      case CK_BitCast:
-      case CK_AddressSpaceConversion:
-      case CK_BooleanToSignedIntegral:
-      case CK_IntegralToPointer:
-      case CK_PointerToIntegral: {
-        SVal V = state->getSVal(Ex, SF);
-        if (isa<nonloc::PointerToMember>(V)) {
-          Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, UnknownVal()));
-          continue;
-        }
-        // Explicitly proceed with default handler for this case cascade.
-        state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
-        continue;
-      }
-      case CK_IntegralToBoolean:
-      case CK_IntegralToFloating:
-      case CK_FloatingToIntegral:
-      case CK_FloatingToBoolean:
-      case CK_FloatingCast:
-      case CK_FloatingRealToComplex:
-      case CK_FloatingComplexToReal:
-      case CK_FloatingComplexToBoolean:
-      case CK_FloatingComplexCast:
-      case CK_FloatingComplexToIntegralComplex:
-      case CK_IntegralRealToComplex:
-      case CK_IntegralComplexToReal:
-      case CK_IntegralComplexToBoolean:
-      case CK_IntegralComplexCast:
-      case CK_IntegralComplexToFloatingComplex:
-      case CK_CPointerToObjCPointerCast:
-      case CK_BlockPointerToObjCPointerCast:
-      case CK_AnyPointerToBlockPointerCast:
-      case CK_ObjCObjectLValueCast:
-      case CK_ZeroToOCLOpaqueType:
-      case CK_IntToOCLSampler:
-      case CK_LValueBitCast:
-      case CK_FloatingToFixedPoint:
-      case CK_FixedPointToFloating:
-      case CK_FixedPointCast:
-      case CK_FixedPointToBoolean:
-      case CK_FixedPointToIntegral:
-      case CK_IntegralToFixedPoint: {
-        state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
-        continue;
-      }
-      case CK_IntegralCast: {
-        // Delegate to SValBuilder to process.
-        SVal V = state->getSVal(Ex, SF);
-        if (AMgr.options.analyzerSymbolicIntegerCasts())
-          V = svalBuilder.evalCast(V, T, ExTy);
-        else
-          V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
-        continue;
-      }
-      case CK_DerivedToBase:
-      case CK_UncheckedDerivedToBase: {
-        // For DerivedToBase cast, delegate to the store manager.
-        SVal val = state->getSVal(Ex, SF);
-        val = getStoreManager().evalDerivedToBase(val, CastE);
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val));
-        continue;
-      }
-      // Handle C++ dyn_cast.
-      case CK_Dynamic: {
-        SVal val = state->getSVal(Ex, SF);
-
-        // Compute the type of the result.
-        QualType resultType = CastE->getType();
-        if (CastE->isGLValue())
-          resultType = getContext().getPointerType(resultType);
-
-        bool Failed = true;
-
-        // Check if the value being cast does not evaluates to 0.
-        if (!val.isZeroConstant())
-          if (std::optional<SVal> V =
-                  StateMgr.getStoreManager().evalBaseToDerived(val, T)) {
-          val = *V;
-          Failed = false;
-          }
-
-        if (Failed) {
-          if (T->isReferenceType()) {
-            // A bad_cast exception is thrown if input value is a reference.
-            // Currently, we model this, by generating a sink.
-            Engine.makePostStmtNode(CastE, state, Pred, /*MarkAsSink=*/true);
-            continue;
-          } else {
-            // If the cast fails on a pointer, bind to 0.
-            state = state->BindExpr(CastE, SF,
-                                    svalBuilder.makeNullWithType(resultType));
-          }
-        } else {
-          // If we don't know if the cast succeeded, conjure a new symbol.
-          if (val.isUnknown()) {
-            DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal(
-                /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
-                getNumVisitedCurrent());
-            state = state->BindExpr(CastE, SF, NewSym);
-          } else
-            // Else, bind to the derived region value.
-            state = state->BindExpr(CastE, SF, val);
-        }
-        Dst.insert(Engine.makePostStmtNode(CastE, state, Pred));
-        continue;
+    SVal V = state->getSVal(Ex, SF);
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
+    return;
+  }
+  case CK_MemberPointerToBoolean:
+  case CK_PointerToBoolean: {
+    SVal V = state->getSVal(Ex, SF);
+    auto PTMSV = V.getAs<nonloc::PointerToMember>();
+    if (PTMSV)
+      V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
+    if (V.isUndef() || PTMSV) {
+      Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
+      return;
+    }
+    // Explicitly proceed with default handler for this case cascade.
+    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    return;
+  }
+  case CK_Dependent:
+  case CK_ArrayToPointerDecay:
+  case CK_BitCast:
+  case CK_AddressSpaceConversion:
+  case CK_BooleanToSignedIntegral:
+  case CK_IntegralToPointer:
+  case CK_PointerToIntegral: {
+    SVal V = state->getSVal(Ex, SF);
+    if (isa<nonloc::PointerToMember>(V)) {
+      Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, UnknownVal()));
+      return;
+    }
+    // Explicitly proceed with default handler for this case cascade.
+    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    return;
+  }
+  case CK_IntegralToBoolean:
+  case CK_IntegralToFloating:
+  case CK_FloatingToIntegral:
+  case CK_FloatingToBoolean:
+  case CK_FloatingCast:
+  case CK_FloatingRealToComplex:
+  case CK_FloatingComplexToReal:
+  case CK_FloatingComplexToBoolean:
+  case CK_FloatingComplexCast:
+  case CK_FloatingComplexToIntegralComplex:
+  case CK_IntegralRealToComplex:
+  case CK_IntegralComplexToReal:
+  case CK_IntegralComplexToBoolean:
+  case CK_IntegralComplexCast:
+  case CK_IntegralComplexToFloatingComplex:
+  case CK_CPointerToObjCPointerCast:
+  case CK_BlockPointerToObjCPointerCast:
+  case CK_AnyPointerToBlockPointerCast:
+  case CK_ObjCObjectLValueCast:
+  case CK_ZeroToOCLOpaqueType:
+  case CK_IntToOCLSampler:
+  case CK_LValueBitCast:
+  case CK_FloatingToFixedPoint:
+  case CK_FixedPointToFloating:
+  case CK_FixedPointCast:
+  case CK_FixedPointToBoolean:
+  case CK_FixedPointToIntegral:
+  case CK_IntegralToFixedPoint: {
+    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    return;
+  }
+  case CK_IntegralCast: {
+    // Delegate to SValBuilder to process.
+    SVal V = state->getSVal(Ex, SF);
+    if (AMgr.options.analyzerSymbolicIntegerCasts())
+      V = svalBuilder.evalCast(V, T, ExTy);
+    else
+      V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
+    return;
+  }
+  case CK_DerivedToBase:
+  case CK_UncheckedDerivedToBase: {
+    // For DerivedToBase cast, delegate to the store manager.
+    SVal val = state->getSVal(Ex, SF);
+    val = getStoreManager().evalDerivedToBase(val, CastE);
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val));
+    return;
+  }
+  // Handle C++ dyn_cast.
+  case CK_Dynamic: {
+    SVal val = state->getSVal(Ex, SF);
+
+    // Compute the type of the result.
+    QualType resultType = CastE->getType();
+    if (CastE->isGLValue())
+      resultType = getContext().getPointerType(resultType);
+
+    bool Failed = true;
+
+    // Check if the value being cast does not evaluates to 0.
+    if (!val.isZeroConstant())
+      if (std::optional<SVal> V =
+              StateMgr.getStoreManager().evalBaseToDerived(val, T)) {
+        val = *V;
+        Failed = false;
       }
-      case CK_BaseToDerived: {
-        SVal val = state->getSVal(Ex, SF);
-        QualType resultType = CastE->getType();
-        if (CastE->isGLValue())
-          resultType = getContext().getPointerType(resultType);
-
-        if (!val.isConstant()) {
-          std::optional<SVal> V = getStoreManager().evalBaseToDerived(val, T);
-          val = V ? *V : UnknownVal();
-        }
 
-        // Failed to cast or the result is unknown, fall back to conservative.
-        if (val.isUnknown()) {
-          val = svalBuilder.conjureSymbolVal(
-              /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
-              getNumVisitedCurrent());
-        }
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val));
-        continue;
-      }
-      case CK_NullToPointer: {
-        SVal V = svalBuilder.makeNullWithType(CastE->getType());
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
-        continue;
-      }
-      case CK_NullToMemberPointer: {
-        SVal V = svalBuilder.getMemberPointer(nullptr);
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
-        continue;
-      }
-      case CK_DerivedToBaseMemberPointer:
-      case CK_BaseToDerivedMemberPointer:
-      case CK_ReinterpretMemberPointer: {
-        SVal V = state->getSVal(Ex, SF);
-        if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
-          SVal CastedPTMSV =
-              svalBuilder.makePointerToMember(getBasicVals().accumCXXBase(
-                  CastE->path(), *PTMSV, CastE->getCastKind()));
-          Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, CastedPTMSV));
-          continue;
-        }
-        // Explicitly proceed with default handler for this case cascade.
+    if (Failed) {
+      if (T->isReferenceType()) {
+        // A bad_cast exception is thrown if input value is a reference.
+        // Currently, we model this, by generating a sink.
+        Engine.makePostStmtNode(CastE, state, Pred, /*MarkAsSink=*/true);
+        return;
+      } else {
+        // If the cast fails on a pointer, bind to 0.
+        state = state->BindExpr(CastE, SF,
+                                svalBuilder.makeNullWithType(resultType));
       }
-        [[fallthrough]];
-      // Various C++ casts that are not handled yet.
-      case CK_ToUnion:
-      case CK_MatrixCast:
-      case CK_VectorSplat:
-      case CK_HLSLElementwiseCast:
-      case CK_HLSLAggregateSplatCast:
-      case CK_HLSLMatrixTruncation:
-      case CK_HLSLVectorTruncation: {
-        QualType resultType = CastE->getType();
-        if (CastE->isGLValue())
-          resultType = getContext().getPointerType(resultType);
-        SVal result = svalBuilder.conjureSymbolVal(
+    } else {
+      // If we don't know if the cast succeeded, conjure a new symbol.
+      if (val.isUnknown()) {
+        DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal(
             /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
             getNumVisitedCurrent());
-        Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, result));
-        continue;
-      }
+        state = state->BindExpr(CastE, SF, NewSym);
+      } else
+        // Else, bind to the derived region value.
+        state = state->BindExpr(CastE, SF, val);
+    }
+    Dst.insert(Engine.makePostStmtNode(CastE, state, Pred));
+    return;
+  }
+  case CK_BaseToDerived: {
+    SVal val = state->getSVal(Ex, SF);
+    QualType resultType = CastE->getType();
+    if (CastE->isGLValue())
+      resultType = getContext().getPointerType(resultType);
+
+    if (!val.isConstant()) {
+      std::optional<SVal> V = getStoreManager().evalBaseToDerived(val, T);
+      val = V ? *V : UnknownVal();
+    }
+
+    // Failed to cast or the result is unknown, fall back to conservative.
+    if (val.isUnknown()) {
+      val = svalBuilder.conjureSymbolVal(
+          /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
+          getNumVisitedCurrent());
     }
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val));
+    return;
+  }
+  case CK_NullToPointer: {
+    SVal V = svalBuilder.makeNullWithType(CastE->getType());
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
+    return;
+  }
+  case CK_NullToMemberPointer: {
+    SVal V = svalBuilder.getMemberPointer(nullptr);
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
+    return;
+  }
+  case CK_DerivedToBaseMemberPointer:
+  case CK_BaseToDerivedMemberPointer:
+  case CK_ReinterpretMemberPointer: {
+    SVal V = state->getSVal(Ex, SF);
+    if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
+      SVal CastedPTMSV =
+          svalBuilder.makePointerToMember(getBasicVals().accumCXXBase(
+              CastE->path(), *PTMSV, CastE->getCastKind()));
+      Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, CastedPTMSV));
+      return;
+    }
+    // Explicitly proceed with default handler for this case cascade.
+  }
+    [[fallthrough]];
+  // Various C++ casts that are not handled yet.
+  case CK_ToUnion:
+  case CK_MatrixCast:
+  case CK_VectorSplat:
+  case CK_HLSLElementwiseCast:
+  case CK_HLSLAggregateSplatCast:
+  case CK_HLSLMatrixTruncation:
+  case CK_HLSLVectorTruncation: {
+    QualType resultType = CastE->getType();
+    if (CastE->isGLValue())
+      resultType = getContext().getPointerType(resultType);
+    SVal result = svalBuilder.conjureSymbolVal(
+        /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
+        getNumVisitedCurrent());
+    Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, result));
+    return;
+  }
   }
 }
 

>From 17716d65a33025e142e8d5d277d874781b2a4198 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 14:24:28 +0200
Subject: [PATCH 11/39] `ExprEngine::VisitCXXNewExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f244eb164e5de..0b9000c67a545 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1115,6 +1115,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::ObjCBridgedCastExprClass:
   case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryOperatorClass:
@@ -1137,6 +1138,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::ObjCBridgedCastExprClass:
   case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryOperatorClass:
@@ -2172,18 +2174,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
                                     Dst);
       break;
 
-    case Stmt::CXXNewExprClass: {
-
-      ExplodedNodeSet PreVisit;
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-
-      ExplodedNodeSet PostVisit;
-      for (const auto i : PreVisit)
-        VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit);
-
-      getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+    case Stmt::CXXNewExprClass:
+      VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::CXXDeleteExprClass: {
       ExplodedNodeSet PreVisit;

>From 1db773a6300323f2ea67a26b09a64e98a35f1f9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:24:46 +0200
Subject: [PATCH 12/39] `ExprEngine::VisitLambdaExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp    | 8 +-------
 clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 8 ++++++++
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 0b9000c67a545..11cc3aabd9200 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2120,13 +2120,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       break;
 
     case Stmt::LambdaExprClass:
-      if (AMgr.options.ShouldInlineLambdas) {
-        VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst);
-      } else {
-        const ExplodedNode *Node = Engine.makePostStmtNode(
-            S, Pred->getState(), Pred, /*MarkAsSink=*/true);
-        Engine.addAbortedBlock(Node, getCurrBlock());
-      }
+      VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst);
       break;
 
     case Stmt::BinaryOperatorClass: {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 2f459f76c3fd0..1d525e416b568 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1122,6 +1122,14 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
 
 void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
                                  ExplodedNodeSet &Dst) {
+
+  if (!AMgr.options.ShouldInlineLambdas) {
+    const ExplodedNode *Node = Engine.makePostStmtNode(
+        LE, Pred->getState(), Pred, /*MarkAsSink=*/true);
+    Engine.addAbortedBlock(Node, getCurrBlock());
+    return;
+  }
+
   const StackFrame *SF = Pred->getStackFrame();
 
   // Get the region of the lambda itself.

>From 5717201bda33fdcf799fea70219409dd3af8947b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:44:42 +0200
Subject: [PATCH 13/39] `ExprEngine::VisitGCCAsmStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 11cc3aabd9200..0eab61cc49e53 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1115,6 +1115,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::ObjCBridgedCastExprClass:
   case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::GCCAsmStmtClass:
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
@@ -1138,6 +1139,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::ObjCBridgedCastExprClass:
   case Stmt::CXXAddrspaceCastExprClass:
   case Stmt::CXXBindTemporaryExprClass:
+  case Stmt::GCCAsmStmtClass:
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
@@ -2101,15 +2103,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       llvm_unreachable("Support for MatrixSubscriptExpr is not implemented.");
       break;
 
-    case Stmt::GCCAsmStmtClass: {
-      ExplodedNodeSet PreVisit;
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-      ExplodedNodeSet PostVisit;
-      for (ExplodedNode *const N : PreVisit)
-        VisitGCCAsmStmt(cast<GCCAsmStmt>(S), N, PostVisit);
-      getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+    case Stmt::GCCAsmStmtClass:
+      VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::MSAsmStmtClass:
       VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst);

>From 7b68df1c087f12c23103ec4495f98c54ad38596b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:59:14 +0200
Subject: [PATCH 14/39] `ExprEngine::VisitUnaryExprOrTypeTraitExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  2 +
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 56 ++++++++-----------
 2 files changed, 25 insertions(+), 33 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 0eab61cc49e53..e968ab8d5fa8d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1119,6 +1119,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
+  case Stmt::UnaryExprOrTypeTraitExprClass:
   case Stmt::UnaryOperatorClass:
     return true;
   }
@@ -1143,6 +1144,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
   case Stmt::OffsetOfExprClass:
+  case Stmt::UnaryExprOrTypeTraitExprClass:
   case Stmt::UnaryOperatorClass:
     return true;
   }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 45c3fa14f5cf8..da7bbb768ef3d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -807,44 +807,34 @@ void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred,
   }
 }
 
-void ExprEngine::
-VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
-                              ExplodedNode *Pred,
-                              ExplodedNodeSet &Dst) {
-  // FIXME: Prechecks eventually go in ::Visit().
-  ExplodedNodeSet CheckedSet;
-  getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, Ex, *this);
-
-  ExplodedNodeSet EvalSet;
+void ExprEngine::VisitUnaryExprOrTypeTraitExpr(
+    const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred,
+    ExplodedNodeSet &Dst) {
   QualType T = Ex->getTypeOfArgument();
 
-  for (ExplodedNode *N : CheckedSet) {
-    if (Ex->getKind() == UETT_SizeOf || Ex->getKind() == UETT_DataSizeOf ||
-        Ex->getKind() == UETT_CountOf) {
-      if (!T->isIncompleteType() && !T->isConstantSizeType()) {
-        assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
-
-        // FIXME: Add support for VLA type arguments and VLA expressions.
-        // When that happens, we should probably refactor VLASizeChecker's code.
-        EvalSet.insert(N);
-        continue;
-      } else if (T->getAs<ObjCObjectType>()) {
-        // Some code tries to take the sizeof an ObjCObjectType, relying that
-        // the compiler has laid out its representation.  Just report Unknown
-        // for these.
-        EvalSet.insert(N);
-        continue;
-      }
-    }
+  if (Ex->getKind() == UETT_SizeOf || Ex->getKind() == UETT_DataSizeOf ||
+      Ex->getKind() == UETT_CountOf) {
+    if (!T->isIncompleteType() && !T->isConstantSizeType()) {
+      assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
 
-    APSInt Value = Ex->EvaluateKnownConstInt(getContext());
-    CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
-
-    SVal V = svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType());
-    EvalSet.insert(Engine.makeNodeWithBinding(N, Ex, V));
+      // FIXME: Add support for VLA type arguments and VLA expressions.
+      // When that happens, we should probably refactor VLASizeChecker's code.
+      Dst.insert(Pred);
+      return;
+    } else if (T->getAs<ObjCObjectType>()) {
+      // Some code tries to take the sizeof an ObjCObjectType, relying that
+      // the compiler has laid out its representation.  Just report Unknown
+      // for these.
+      Dst.insert(Pred);
+      return;
+    }
   }
 
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
+  APSInt Value = Ex->EvaluateKnownConstInt(getContext());
+  CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
+
+  SVal V = svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType());
+  Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V));
 }
 
 void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,

>From d6a329efc0105a78b22a6307ab58a1ca390086a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:14:34 +0200
Subject: [PATCH 15/39] `ExprEngine::VisitReturnStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp             | 1 +
 .../lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp  | 9 ++-------
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e968ab8d5fa8d..6ccb51beeef1a 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1118,6 +1118,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::GCCAsmStmtClass:
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
+  case Stmt::ReturnStmtClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryExprOrTypeTraitExprClass:
   case Stmt::UnaryOperatorClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index e0139484b2b48..983e45e80fd04 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -1304,14 +1304,9 @@ void ExprEngine::dynDispatchBifurcate(const MemRegion *BifurReg,
 
 void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
                                  ExplodedNodeSet &Dst) {
-  ExplodedNodeSet DstPreVisit;
-  getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, RS, *this);
-
   if (RS->getRetValue()) {
-    for (ExplodedNode *N : DstPreVisit) {
-      Dst.insert(Engine.makePostStmtNode(RS, N->getState(), N));
-    }
+    Dst.insert(Engine.makePostStmtNode(RS, Pred->getState(), Pred));
   } else {
-    Dst.insert(DstPreVisit);
+    Dst.insert(Pred);
   }
 }

>From 1fea98fa136115ce6cd027d069719e79eb522662 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:45:16 +0200
Subject: [PATCH 16/39] `ExprEngine::VisitAtomicExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 36 ++++++++------------
 1 file changed, 14 insertions(+), 22 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 6ccb51beeef1a..a702d74c90f44 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
   case Stmt::CXXStaticCastExprClass:
@@ -1130,6 +1131,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
   case Stmt::CXXStaticCastExprClass:
@@ -3396,34 +3398,24 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
 
 void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
                                  ExplodedNodeSet &Dst) {
-  ExplodedNodeSet AfterPreSet;
-  getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this);
-
   // For now, treat all the arguments to C11 atomics as escaping.
   // FIXME: Ideally we should model the behavior of the atomics precisely here.
 
-  ExplodedNodeSet AfterInvalidateSet;
-
-  for (const auto I : AfterPreSet) {
-    ProgramStateRef State = I->getState();
-    const StackFrame *SF = I->getStackFrame();
-
-    SmallVector<SVal, 8> ValuesToInvalidate;
-    for (const Stmt *SubExpr : AE->children()) {
-      SVal SubExprVal = State->getSVal(cast<Expr>(SubExpr), SF);
-      ValuesToInvalidate.push_back(SubExprVal);
-    }
-
-    State = State->invalidateRegions(ValuesToInvalidate, getCFGElementRef(),
-                                     getNumVisitedCurrent(), SF,
-                                     /*CausedByPointerEscape*/ true,
-                                     /*Symbols=*/nullptr);
+  ProgramStateRef State = Pred->getState();
+  const StackFrame *SF = Pred->getStackFrame();
 
-    AfterInvalidateSet.insert(
-        Engine.makeNodeWithBinding(I, AE, UnknownVal(), State));
+  SmallVector<SVal, 8> ValuesToInvalidate;
+  for (const Stmt *SubExpr : AE->children()) {
+    SVal SubExprVal = State->getSVal(cast<Expr>(SubExpr), SF);
+    ValuesToInvalidate.push_back(SubExprVal);
   }
 
-  getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this);
+  State = State->invalidateRegions(ValuesToInvalidate, getCFGElementRef(),
+                                   getNumVisitedCurrent(), SF,
+                                   /*CausedByPointerEscape*/ true,
+                                   /*Symbols=*/nullptr);
+
+  Dst.insert(Engine.makeNodeWithBinding(Pred, AE, UnknownVal(), State));
 }
 
 // A value escapes in four possible cases:

>From a543f02365f72b7f6fac352a74bc4d5c5833a1cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:53:59 +0200
Subject: [PATCH 17/39] `ExprEngine::VisitMemberExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 116 +++++++++----------
 1 file changed, 54 insertions(+), 62 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a702d74c90f44..3df3b081cdffc 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1119,6 +1119,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::GCCAsmStmtClass:
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
+  case Stmt::MemberExprClass:
   case Stmt::ReturnStmtClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryExprOrTypeTraitExprClass:
@@ -1146,6 +1147,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::GCCAsmStmtClass:
   case Stmt::CXXNewExprClass:
   case Stmt::MaterializeTemporaryExprClass:
+  case Stmt::MemberExprClass:
   case Stmt::OffsetOfExprClass:
   case Stmt::UnaryExprOrTypeTraitExprClass:
   case Stmt::UnaryOperatorClass:
@@ -3315,85 +3317,75 @@ a vector and not a forbidden lvalue type");
 /// VisitMemberExpr - Transfer function for member expressions.
 void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
                                  ExplodedNodeSet &Dst) {
-  // FIXME: Prechecks eventually go in ::Visit().
-  ExplodedNodeSet CheckedSet;
-  getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this);
-
-  ExplodedNodeSet EvalSet;
   ValueDecl *Member = M->getMemberDecl();
 
   // Handle static member variables and enum constants accessed via
   // member syntax.
   if (isa<VarDecl, EnumConstantDecl>(Member)) {
-    for (const auto I : CheckedSet)
-      VisitCommonDeclRefExpr(M, Member, I, EvalSet);
+    VisitCommonDeclRefExpr(M, Member, Pred, Dst);
   } else {
 
-    for (const auto I : CheckedSet) {
-      ProgramStateRef state = I->getState();
-      const StackFrame *SF = I->getStackFrame();
-      Expr *BaseExpr = M->getBase();
-
-      // Handle C++ method calls.
-      if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
-        if (MD->isImplicitObjectMemberFunction())
-          state = createTemporaryRegionIfNeeded(state, SF, BaseExpr);
+    ProgramStateRef state = Pred->getState();
+    const StackFrame *SF = Pred->getStackFrame();
+    Expr *BaseExpr = M->getBase();
 
-        SVal MDVal = svalBuilder.getFunctionPointer(MD);
+    // Handle C++ method calls.
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
+      if (MD->isImplicitObjectMemberFunction())
+        state = createTemporaryRegionIfNeeded(state, SF, BaseExpr);
 
-        EvalSet.insert(Engine.makeNodeWithBinding(I, M, MDVal, state));
-        continue;
-      }
+      SVal MDVal = svalBuilder.getFunctionPointer(MD);
 
-      // Handle regular struct fields / member variables.
-      const SubRegion *MR = nullptr;
-      state = createTemporaryRegionIfNeeded(state, SF, BaseExpr,
-                                            /*Result=*/nullptr,
-                                            /*OutRegionWithAdjustments=*/&MR);
-      SVal baseExprVal =
-          MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, SF);
-
-      // FIXME: Copied from RegionStoreManager::bind()
-      if (const auto *SR =
-              dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) {
-        QualType T = SR->getPointeeStaticType();
-        baseExprVal =
-            loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T));
-      }
+      Dst.insert(Engine.makeNodeWithBinding(Pred, M, MDVal, state));
+      return;
+    }
 
-      const auto *field = cast<FieldDecl>(Member);
-      SVal L = state->getLValue(field, baseExprVal);
-
-      if (M->isGLValue() || M->getType()->isArrayType()) {
-        // We special-case rvalues of array type because the analyzer cannot
-        // reason about them, since we expect all regions to be wrapped in Locs.
-        // We instead treat these as lvalues and assume that they will decay to
-        // pointers as soon as they are used.
-        if (!M->isGLValue()) {
-          assert(M->getType()->isArrayType());
-          const auto *PE =
-            dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M));
-          if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) {
-            llvm_unreachable("should always be wrapped in ArrayToPointerDecay");
-          }
-        }
+    // Handle regular struct fields / member variables.
+    const SubRegion *MR = nullptr;
+    state = createTemporaryRegionIfNeeded(state, SF, BaseExpr,
+                                          /*Result=*/nullptr,
+                                          /*OutRegionWithAdjustments=*/&MR);
+    SVal baseExprVal =
+        MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, SF);
+
+    // FIXME: Copied from RegionStoreManager::bind()
+    if (const auto *SR =
+            dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) {
+      QualType T = SR->getPointeeStaticType();
+      baseExprVal =
+          loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T));
+    }
 
-        if (field->getType()->isReferenceType()) {
-          if (const MemRegion *R = L.getAsRegion())
-            L = state->getSVal(R);
-          else
-            L = UnknownVal();
+    const auto *field = cast<FieldDecl>(Member);
+    SVal L = state->getLValue(field, baseExprVal);
+
+    if (M->isGLValue() || M->getType()->isArrayType()) {
+      // We special-case rvalues of array type because the analyzer cannot
+      // reason about them, since we expect all regions to be wrapped in Locs.
+      // We instead treat these as lvalues and assume that they will decay to
+      // pointers as soon as they are used.
+      if (!M->isGLValue()) {
+        assert(M->getType()->isArrayType());
+        const auto *PE =
+          dyn_cast<ImplicitCastExpr>(Pred->getParentMap().getParentIgnoreParens(M));
+        if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) {
+          llvm_unreachable("should always be wrapped in ArrayToPointerDecay");
         }
+      }
 
-        EvalSet.insert(Engine.makeNodeWithBinding(
-            I, M, L, state, ProgramPoint::PostLValueKind));
-      } else {
-        evalLoad(EvalSet, M, M, I, state, L);
+      if (field->getType()->isReferenceType()) {
+        if (const MemRegion *R = L.getAsRegion())
+          L = state->getSVal(R);
+        else
+          L = UnknownVal();
       }
+
+      Dst.insert(Engine.makeNodeWithBinding(
+          Pred, M, L, state, ProgramPoint::PostLValueKind));
+    } else {
+      evalLoad(Dst, M, M, Pred, state, L);
     }
   }
-
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this);
 }
 
 void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,

>From eddd69459b8be072b91b60bcd8281695a64ae5f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 17:58:53 +0200
Subject: [PATCH 18/39] `ExprEngine::VisitBlockExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 1 +
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 7 ++-----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 3df3b081cdffc..32174b46bbc34 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1133,6 +1133,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::AtomicExprClass:
+  case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
   case Stmt::CXXStaticCastExprClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index da7bbb768ef3d..4e6f93d783fc1 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -214,11 +214,8 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
     }
   }
 
-  ExplodedNode *N = Engine.makeNodeWithBinding(Pred, BE, V, State,
-                                               ProgramPoint::PostLValueKind);
-
-  // FIXME: Move all post/pre visits to ::Visit().
-  getCheckerManager().runCheckersForPostStmt(Dst, N, BE, *this);
+  Dst.insert(Engine.makeNodeWithBinding(Pred, BE, V, State,
+                                        ProgramPoint::PostLValueKind));
 }
 
 ProgramStateRef

>From b5ff694431054e5bdae7d220ac79908757d49fdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 18:16:44 +0200
Subject: [PATCH 19/39] `ExprEngine::VisitArraySubscriptExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 57 +++++++++-----------
 1 file changed, 25 insertions(+), 32 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 32174b46bbc34..1de0a0a7e68a2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ArraySubscriptExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
@@ -1132,6 +1133,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ArraySubscriptExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
@@ -3268,16 +3270,11 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex,
 
 /// VisitArraySubscriptExpr - Transfer function for array accesses
 void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
-                                             ExplodedNode *Pred,
-                                             ExplodedNodeSet &Dst){
+                                         ExplodedNode *Pred,
+                                         ExplodedNodeSet &Dst) {
   const Expr *Base = A->getBase()->IgnoreParens();
   const Expr *Idx  = A->getIdx()->IgnoreParens();
 
-  ExplodedNodeSet CheckerPreStmt;
-  getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
-
-  ExplodedNodeSet EvalSet;
-
   bool IsVectorType = A->getBase()->getType()->isVectorType();
 
   // The "like" case is for situations where C standard prohibits the type to
@@ -3286,33 +3283,29 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
   bool IsGLValueLike = A->isGLValue() ||
     (A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus);
 
-  for (auto *Node : CheckerPreStmt) {
-    const StackFrame *SF = Node->getStackFrame();
-    ProgramStateRef state = Node->getState();
+  const StackFrame *SF = Pred->getStackFrame();
+  ProgramStateRef state = Pred->getState();
 
-    if (IsGLValueLike) {
-      QualType T = A->getType();
-
-      // One of the forbidden LValue types! We still need to have sensible
-      // symbolic locations to represent this stuff. Note that arithmetic on
-      // void pointers is a GCC extension.
-      if (T->isVoidType())
-        T = getContext().CharTy;
-
-      SVal V = state->getLValue(T, state->getSVal(Idx, SF),
-                                state->getSVal(Base, SF));
-      EvalSet.insert(
-          Engine.makeNodeWithBinding(Node, A, V, ProgramPoint::PostLValueKind));
-    } else if (IsVectorType) {
-      // FIXME: non-glvalue vector reads are not modelled.
-      EvalSet.insert(Engine.makePostStmtNode(A, state, Node));
-    } else {
-      llvm_unreachable("Array subscript should be an lValue when not \
-a vector and not a forbidden lvalue type");
-    }
-  }
+  if (IsGLValueLike) {
+    QualType T = A->getType();
 
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this);
+    // One of the forbidden LValue types! We still need to have sensible
+    // symbolic locations to represent this stuff. Note that arithmetic on
+    // void pointers is a GCC extension.
+    if (T->isVoidType())
+      T = getContext().CharTy;
+
+    SVal V =
+        state->getLValue(T, state->getSVal(Idx, SF), state->getSVal(Base, SF));
+    Dst.insert(
+        Engine.makeNodeWithBinding(Pred, A, V, ProgramPoint::PostLValueKind));
+  } else if (IsVectorType) {
+    // FIXME: non-glvalue vector reads are not modelled.
+    Dst.insert(Engine.makePostStmtNode(A, state, Pred));
+  } else {
+    llvm_unreachable("Array subscript should be an lValue when not \
+a ctor and not a forbidden lvalue type");
+  }
 }
 
 /// VisitMemberExpr - Transfer function for member expressions.

>From a5be3d4a30e81d4188dd7bae5e44a16065d8d9ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 18:38:14 +0200
Subject: [PATCH 20/39] `ExprEngine::VisitAttributedStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  5 ++--
 .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 30 +++++++------------
 2 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1de0a0a7e68a2..c7089452ed7eb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::ArraySubscriptExprClass:
+  case Stmt::AttributedStmtClass:
   case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
@@ -1134,6 +1135,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   default:
     return false;
   case Stmt::ArraySubscriptExprClass:
+  case Stmt::AttributedStmtClass:
   case Stmt::AtomicExprClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
@@ -2017,10 +2019,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       break;
     }
 
-    case Stmt::AttributedStmtClass: {
+    case Stmt::AttributedStmtClass:
       VisitAttributedStmt(cast<AttributedStmt>(S), Pred, Dst);
       break;
-    }
 
     case Stmt::CXXDefaultArgExprClass:
     case Stmt::CXXDefaultInitExprClass: {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 1d525e416b568..a6edc861c291f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1201,28 +1201,20 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
 void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
                                      ExplodedNode *Pred, ExplodedNodeSet &Dst) {
   const StackFrame *SF = Pred->getStackFrame();
-  ExplodedNodeSet CheckerPreStmt;
-  getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
-
-  ExplodedNodeSet EvalSet;
-
-  for (ExplodedNode *N : CheckerPreStmt) {
-    ProgramStateRef State = N->getState();
-    for (const auto *Attr : getSpecificAttrs<CXXAssumeAttr>(A->getAttrs())) {
-      SVal AssumedVal = State->getSVal(Attr->getAssumption(), SF);
-      // This code ignores assumptions that evaluate to UndefinedVal.
-      // Perhaps there should be a checker that reports this situation.
-      if (auto ValidAssumedVal = AssumedVal.getAs<DefinedOrUnknownSVal>()) {
-        State = State->assume(*ValidAssumedVal, true);
-      }
+  ProgramStateRef State = Pred->getState();
 
-      if (!State)
-        break;
+  for (const auto *Attr : getSpecificAttrs<CXXAssumeAttr>(A->getAttrs())) {
+    SVal AssumedVal = State->getSVal(Attr->getAssumption(), SF);
+    // This code ignores assumptions that evaluate to UndefinedVal.
+    // Perhaps there should be a checker that reports this situation.
+    if (auto ValidAssumedVal = AssumedVal.getAs<DefinedOrUnknownSVal>()) {
+      State = State->assume(*ValidAssumedVal, true);
     }
 
-    if (State)
-      EvalSet.insert(Engine.makePostStmtNode(A, State, N));
+    if (!State)
+      break;
   }
 
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this);
+  if (State)
+    Dst.insert(Engine.makePostStmtNode(A, State, Pred));
 }

>From 01b503f7b4e3f0dc19ede3f48a80667638cd2b9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 14:45:05 +0200
Subject: [PATCH 21/39] `ExprEngine::VisitArrayInitLoopExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 152 +++++++++----------
 1 file changed, 73 insertions(+), 79 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index c7089452ed7eb..fa53f51d8f82e 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
   case Stmt::AtomicExprClass:
@@ -1134,6 +1135,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+  case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
   case Stmt::AtomicExprClass:
@@ -3177,96 +3179,88 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex,
                                         ExplodedNodeSet &Dst) {
   const Expr *Arr = Ex->getCommonExpr()->getSourceExpr();
 
-  ExplodedNodeSet CheckerPreStmt;
-  getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, Ex, *this);
-
-  ExplodedNodeSet EvalSet;
+  // The constructor visitor has already handled everything, so let's skip
+  // forward to PostStmt handling.
   if (isa<CXXConstructExpr>(Ex->getSubExpr())) {
-    // The constructor visitor has already handled everything, so let's skip
-    // forward to PostStmt handling by clearing the range of the 'for' loop.
-    EvalSet.insert(CheckerPreStmt);
-    CheckerPreStmt.clear();
+    Dst.insert(Pred);
+    return;
   }
 
-  for (auto *Node : CheckerPreStmt) {
-    const StackFrame *SF = Node->getStackFrame();
-    ProgramStateRef state = Node->getState();
-
-    SVal Base = UnknownVal();
-
-    // As in case of this expression the sub-expressions are not visited by any
-    // other transfer functions, they are handled by matching their AST.
+  const StackFrame *SF = Pred->getStackFrame();
+  ProgramStateRef state = Pred->getState();
 
-    // Case of implicit copy or move ctor of object with array member
-    //
-    // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the
-    // environment.
-    //
-    //    struct S {
-    //      int arr[2];
-    //    };
-    //
-    //
-    //    S a;
-    //    S b = a;
-    //
-    // The AST in case of a *copy constructor* looks like this:
-    //    ArrayInitLoopExpr
-    //    |-OpaqueValueExpr
-    //    | `-MemberExpr              <-- match this
-    //    |   `-DeclRefExpr
-    //    ` ...
-    //
-    //
-    //    S c;
-    //    S d = std::move(d);
-    //
-    // In case of a *move constructor* the resulting AST looks like:
-    //    ArrayInitLoopExpr
-    //    |-OpaqueValueExpr
-    //    | `-MemberExpr              <-- match this first
-    //    |   `-CXXStaticCastExpr     <-- match this after
-    //    |     `-DeclRefExpr
-    //    ` ...
-    if (const auto *ME = dyn_cast<MemberExpr>(Arr)) {
-      Expr *MEBase = ME->getBase();
-
-      // Move ctor
-      if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) {
-        MEBase = CXXSCE->getSubExpr();
-      }
+  SVal Base = UnknownVal();
 
-      auto ObjDeclExpr = cast<DeclRefExpr>(MEBase);
-      SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), SF);
+  // As in case of this expression the sub-expressions are not visited by any
+  // other transfer functions, they are handled by matching their AST.
 
-      Base = state->getLValue(cast<FieldDecl>(ME->getMemberDecl()), Obj);
+  // Case of implicit copy or move ctor of object with array member
+  //
+  // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the
+  // environment.
+  //
+  //    struct S {
+  //      int arr[2];
+  //    };
+  //
+  //
+  //    S a;
+  //    S b = a;
+  //
+  // The AST in case of a *copy constructor* looks like this:
+  //    ArrayInitLoopExpr
+  //    |-OpaqueValueExpr
+  //    | `-MemberExpr              <-- match this
+  //    |   `-DeclRefExpr
+  //    ` ...
+  //
+  //
+  //    S c;
+  //    S d = std::move(d);
+  //
+  // In case of a *move constructor* the resulting AST looks like:
+  //    ArrayInitLoopExpr
+  //    |-OpaqueValueExpr
+  //    | `-MemberExpr              <-- match this first
+  //    |   `-CXXStaticCastExpr     <-- match this after
+  //    |     `-DeclRefExpr
+  //    ` ...
+  if (const auto *ME = dyn_cast<MemberExpr>(Arr)) {
+    Expr *MEBase = ME->getBase();
+
+    // Move ctor
+    if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) {
+      MEBase = CXXSCE->getSubExpr();
     }
 
-    // Case of lambda capture and decomposition declaration
-    //
-    //    int arr[2];
-    //
-    //    [arr]{ int a = arr[0]; }();
-    //    auto[a, b] = arr;
-    //
-    // In both of these cases the AST looks like the following:
-    //    ArrayInitLoopExpr
-    //    |-OpaqueValueExpr
-    //    | `-DeclRefExpr             <-- match this
-    //    ` ...
-    if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arr))
-      Base = state->getLValue(cast<VarDecl>(DRE->getDecl()), SF);
-
-    // Create a lazy compound value to the original array
-    if (const MemRegion *R = Base.getAsRegion())
-      Base = state->getSVal(R);
-    else
-      Base = UnknownVal();
+    auto ObjDeclExpr = cast<DeclRefExpr>(MEBase);
+    SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), SF);
 
-    EvalSet.insert(Engine.makeNodeWithBinding(Node, Ex, Base));
+    Base = state->getLValue(cast<FieldDecl>(ME->getMemberDecl()), Obj);
   }
 
-  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
+  // Case of lambda capture and decomposition declaration
+  //
+  //    int arr[2];
+  //
+  //    [arr]{ int a = arr[0]; }();
+  //    auto[a, b] = arr;
+  //
+  // In both of these cases the AST looks like the following:
+  //    ArrayInitLoopExpr
+  //    |-OpaqueValueExpr
+  //    | `-DeclRefExpr             <-- match this
+  //    ` ...
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arr))
+    Base = state->getLValue(cast<VarDecl>(DRE->getDecl()), SF);
+
+  // Create a lazy compound value to the original array
+  if (const MemRegion *R = Base.getAsRegion())
+    Base = state->getSVal(R);
+  else
+    Base = UnknownVal();
+
+  Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, Base));
 }
 
 /// VisitArraySubscriptExpr - Transfer function for array accesses

>From 75032f19460fd21154a9395b29724148ff5daf44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 15:03:10 +0200
Subject: [PATCH 22/39] Refactor cases that are unsupported or that do not have
 to be explicitly evaluated

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 110 +++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index fa53f51d8f82e..8dcd4046b4564 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1104,6 +1104,61 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+
+  // Cases not handled yet; but will handle some day.
+  case Stmt::DesignatedInitExprClass:
+  case Stmt::DesignatedInitUpdateExprClass:
+  case Stmt::ArrayInitIndexExprClass:
+  case Stmt::ExtVectorElementExprClass:
+  case Stmt::MatrixElementExprClass:
+  case Stmt::ImaginaryLiteralClass:
+  case Stmt::ObjCAtCatchStmtClass:
+  case Stmt::ObjCAtFinallyStmtClass:
+  case Stmt::ObjCAtTryStmtClass:
+  case Stmt::ObjCAutoreleasePoolStmtClass:
+  case Stmt::ObjCEncodeExprClass:
+  case Stmt::ObjCIsaExprClass:
+  case Stmt::ObjCProtocolExprClass:
+  case Stmt::ObjCSelectorExprClass:
+  case Stmt::ParenListExprClass:
+  case Stmt::ShuffleVectorExprClass:
+  case Stmt::ConvertVectorExprClass:
+  case Stmt::VAArgExprClass:
+  case Stmt::CUDAKernelCallExprClass:
+  case Stmt::OpaqueValueExprClass:
+  case Stmt::AsTypeExprClass:
+  case Stmt::ConceptSpecializationExprClass:
+  case Stmt::CXXRewrittenBinaryOperatorClass:
+  case Stmt::RequiresExprClass:
+  case Stmt::EmbedExprClass:
+
+  // Cases we intentionally don't evaluate, since they don't need
+  // to be explicitly evaluated.
+  case Stmt::PredefinedExprClass:
+  case Stmt::AddrLabelExprClass:
+  case Stmt::IntegerLiteralClass:
+  case Stmt::FixedPointLiteralClass:
+  case Stmt::CharacterLiteralClass:
+  case Stmt::CXXScalarValueInitExprClass:
+  case Stmt::CXXBoolLiteralExprClass:
+  case Stmt::ObjCBoolLiteralExprClass:
+  case Stmt::ObjCAvailabilityCheckExprClass:
+  case Stmt::FloatingLiteralClass:
+  case Stmt::NoInitExprClass:
+  case Stmt::SizeOfPackExprClass:
+  case Stmt::StringLiteralClass:
+  case Stmt::SourceLocExprClass:
+  case Stmt::ObjCStringLiteralClass:
+  case Stmt::CXXPseudoDestructorExprClass:
+  case Stmt::SubstNonTypeTemplateParmExprClass:
+  case Stmt::CXXNullPtrLiteralExprClass:
+  case Stmt::ArraySectionExprClass:
+  case Stmt::OMPArrayShapingExprClass:
+  case Stmt::OMPIteratorExprClass:
+  case Stmt::SYCLUniqueStableNameExprClass:
+  case Stmt::OpenACCAsteriskSizeExprClass:
+  case Stmt::TypeTraitExprClass:
+
   case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
@@ -1135,6 +1190,61 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   switch (S->getStmtClass()) {
   default:
     return false;
+
+  // Cases not handled yet; but will handle some day.
+  case Stmt::DesignatedInitExprClass:
+  case Stmt::DesignatedInitUpdateExprClass:
+  case Stmt::ArrayInitIndexExprClass:
+  case Stmt::ExtVectorElementExprClass:
+  case Stmt::MatrixElementExprClass:
+  case Stmt::ImaginaryLiteralClass:
+  case Stmt::ObjCAtCatchStmtClass:
+  case Stmt::ObjCAtFinallyStmtClass:
+  case Stmt::ObjCAtTryStmtClass:
+  case Stmt::ObjCAutoreleasePoolStmtClass:
+  case Stmt::ObjCEncodeExprClass:
+  case Stmt::ObjCIsaExprClass:
+  case Stmt::ObjCProtocolExprClass:
+  case Stmt::ObjCSelectorExprClass:
+  case Stmt::ParenListExprClass:
+  case Stmt::ShuffleVectorExprClass:
+  case Stmt::ConvertVectorExprClass:
+  case Stmt::VAArgExprClass:
+  case Stmt::CUDAKernelCallExprClass:
+  case Stmt::OpaqueValueExprClass:
+  case Stmt::AsTypeExprClass:
+  case Stmt::ConceptSpecializationExprClass:
+  case Stmt::CXXRewrittenBinaryOperatorClass:
+  case Stmt::RequiresExprClass:
+  case Stmt::EmbedExprClass:
+
+  // Cases we intentionally don't evaluate, since they don't need
+  // to be explicitly evaluated.
+  case Stmt::PredefinedExprClass:
+  case Stmt::AddrLabelExprClass:
+  case Stmt::IntegerLiteralClass:
+  case Stmt::FixedPointLiteralClass:
+  case Stmt::CharacterLiteralClass:
+  case Stmt::CXXScalarValueInitExprClass:
+  case Stmt::CXXBoolLiteralExprClass:
+  case Stmt::ObjCBoolLiteralExprClass:
+  case Stmt::ObjCAvailabilityCheckExprClass:
+  case Stmt::FloatingLiteralClass:
+  case Stmt::NoInitExprClass:
+  case Stmt::SizeOfPackExprClass:
+  case Stmt::StringLiteralClass:
+  case Stmt::SourceLocExprClass:
+  case Stmt::ObjCStringLiteralClass:
+  case Stmt::CXXPseudoDestructorExprClass:
+  case Stmt::SubstNonTypeTemplateParmExprClass:
+  case Stmt::CXXNullPtrLiteralExprClass:
+  case Stmt::ArraySectionExprClass:
+  case Stmt::OMPArrayShapingExprClass:
+  case Stmt::OMPIteratorExprClass:
+  case Stmt::SYCLUniqueStableNameExprClass:
+  case Stmt::OpenACCAsteriskSizeExprClass:
+  case Stmt::TypeTraitExprClass:
+
   case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:

>From 9ebb3b4d273ee84542649439383588f0a8818d62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:00:04 +0200
Subject: [PATCH 23/39] `Stmt::CXXDefaultArgExprClass`,
 `Stmt::CXXDefaultInitExprClass`

These are handled by the same code and currently implemented inline
in the switch, i.e. have no dedicated visit method.
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 27 ++++++++------------
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 8dcd4046b4564..1a191527f2c66 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1162,6 +1162,8 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
+  case Stmt::CXXDefaultArgExprClass:
+  case Stmt::CXXDefaultInitExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
@@ -1248,6 +1250,8 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::ArrayInitLoopExprClass:
   case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
+  case Stmt::CXXDefaultArgExprClass:
+  case Stmt::CXXDefaultInitExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
@@ -2125,9 +2129,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SYCLUniqueStableNameExprClass:
     case Stmt::OpenACCAsteriskSizeExprClass:
     case Stmt::TypeTraitExprClass: {
-      ExplodedNodeSet preVisit;
-      getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
-      getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this);
+      Dst.insert(Pred);
       break;
     }
 
@@ -2137,10 +2139,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
 
     case Stmt::CXXDefaultArgExprClass:
     case Stmt::CXXDefaultInitExprClass: {
-      ExplodedNodeSet PreVisit;
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-
-      ExplodedNodeSet Tmp;
 
       const Expr *ArgE;
       if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
@@ -2161,16 +2159,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
         ConstantVal = UnknownVal();
 
       const StackFrame *SF = Pred->getStackFrame();
-      for (const auto I : PreVisit) {
-        ProgramStateRef State = I->getState();
-        State = State->BindExpr(cast<Expr>(S), SF, *ConstantVal);
-        if (IsTemporary)
-          State = createTemporaryRegionIfNeeded(State, SF, cast<Expr>(S),
-                                                cast<Expr>(S));
-        Tmp.insert(Engine.makePostStmtNode(S, State, I));
-      }
+      ProgramStateRef State = Pred->getState();
+      State = State->BindExpr(cast<Expr>(S), SF, *ConstantVal);
+      if (IsTemporary)
+        State = createTemporaryRegionIfNeeded(State, SF, cast<Expr>(S),
+                                              cast<Expr>(S));
+      Dst.insert(Engine.makePostStmtNode(S, State, Pred));
 
-      getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
       break;
     }
 

>From a6d17293ebd60d6ba3faa231569f541b5ae3a838 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:15:42 +0200
Subject: [PATCH 24/39] `Stmt::CXXStdInitializerListExprClass`,
 `Expr::ObjCArrayLiteralClass`, `Expr::ObjCDictionaryLiteralClass`,
 `Expr::ObjCBoxedExprClass`,

These are handled by the same code and currently implemented inline
in the switch, i.e. have no dedicated visit method.
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 53 ++++++++++----------
 1 file changed, 26 insertions(+), 27 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1a191527f2c66..1453bc35b62c3 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1164,6 +1164,10 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::AttributedStmtClass:
   case Stmt::CXXDefaultArgExprClass:
   case Stmt::CXXDefaultInitExprClass:
+  case Stmt::CXXStdInitializerListExprClass:
+  case Expr::ObjCArrayLiteralClass:
+  case Expr::ObjCDictionaryLiteralClass:
+  case Expr::ObjCBoxedExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
@@ -1252,6 +1256,10 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::AttributedStmtClass:
   case Stmt::CXXDefaultArgExprClass:
   case Stmt::CXXDefaultInitExprClass:
+  case Stmt::CXXStdInitializerListExprClass:
+  case Expr::ObjCArrayLiteralClass:
+  case Expr::ObjCDictionaryLiteralClass:
+  case Expr::ObjCBoxedExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
@@ -2174,37 +2182,28 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Expr::ObjCArrayLiteralClass:
     case Expr::ObjCDictionaryLiteralClass:
     case Expr::ObjCBoxedExprClass: {
-      ExplodedNodeSet preVisit;
-      getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
-
-      ExplodedNodeSet Tmp;
-
       const auto *Ex = cast<Expr>(S);
       QualType resultType = Ex->getType();
 
-      for (const auto N : preVisit) {
-        const StackFrame *SF = N->getStackFrame();
-        SVal result = svalBuilder.conjureSymbolVal(
-            /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
-            getNumVisitedCurrent());
-        ProgramStateRef State = N->getState()->BindExpr(Ex, SF, result);
-
-        // Escape pointers passed into the list, unless it's an ObjC boxed
-        // expression which is not a boxable C structure.
-        if (!(isa<ObjCBoxedExpr>(Ex) &&
-              !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
-                                      ->getType()->isRecordType()))
-          for (auto Child : Ex->children()) {
-            assert(Child);
-            const auto *ChildExpr = dyn_cast<Expr>(Child);
-            SVal Val = ChildExpr ? State->getSVal(ChildExpr, SF) : UnknownVal();
-            State = escapeValues(State, Val, PSK_EscapeOther);
-          }
-
-        Tmp.insert(Engine.makePostStmtNode(S, State, N));
-      }
+      const StackFrame *SF = Pred->getStackFrame();
+      SVal result = svalBuilder.conjureSymbolVal(
+          /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
+          getNumVisitedCurrent());
+      ProgramStateRef State = Pred->getState()->BindExpr(Ex, SF, result);
+
+      // Escape pointers passed into the list, unless it's an ObjC boxed
+      // expression which is not a boxable C structure.
+      if (!(isa<ObjCBoxedExpr>(Ex) &&
+            !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
+                                    ->getType()->isRecordType()))
+        for (auto Child : Ex->children()) {
+          assert(Child);
+          const auto *ChildExpr = dyn_cast<Expr>(Child);
+          SVal Val = ChildExpr ? State->getSVal(ChildExpr, SF) : UnknownVal();
+          State = escapeValues(State, Val, PSK_EscapeOther);
+        }
 
-      getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+      Dst.insert(Engine.makePostStmtNode(S, State, Pred));
       break;
     }
 

>From 4542665d2c15111e8973b60ccf040dddab4774af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:45:53 +0200
Subject: [PATCH 25/39] `ExprEngine::VisitLvalObjCIvarRefExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp     | 1 +
 clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 7 +------
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1453bc35b62c3..79957c0943e85 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1261,6 +1261,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Expr::ObjCDictionaryLiteralClass:
   case Expr::ObjCBoxedExprClass:
   case Stmt::AtomicExprClass:
+  case Stmt::ObjCIvarRefExprClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index bc214f4aa7054..99b6d5f5e8ea2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -25,12 +25,7 @@ void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
   const StackFrame *SF = Pred->getStackFrame();
   SVal baseVal = state->getSVal(Ex->getBase(), SF);
   SVal location = state->getLValue(Ex->getDecl(), baseVal);
-
-  ExplodedNode *N = Engine.makeNodeWithBinding(Pred, Ex, location);
-
-  // Perform the post-condition check of the ObjCIvarRefExpr and store
-  // the created nodes in 'Dst'.
-  getCheckerManager().runCheckersForPostStmt(Dst, N, Ex, *this);
+  Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, location));
 }
 
 void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,

>From 40570db16afe6ba3e81648d5e90daa6b5c8dfccf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:54:10 +0200
Subject: [PATCH 26/39] `ExprEngine::VisitObjCForCollectionStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp     | 1 +
 clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 4 +---
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 79957c0943e85..b09b088e3b374 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1262,6 +1262,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Expr::ObjCBoxedExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::ObjCIvarRefExprClass:
+  case Stmt::ObjCForCollectionStmtClass:
   case Stmt::BlockExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 99b6d5f5e8ea2..88cb1ef625674 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -123,9 +123,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
 
     populateObjCForDestinationSet(S, N, Tmp, elementV, /*hasElements=*/false);
 
-    // Finally, run any custom checkers.
-    // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
-    getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+    Dst.insert(Tmp);
   }
 }
 

>From db5a00955538660b3d606e3dd56671c8696ce705 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Fri, 14 Aug 2026 14:35:24 +0200
Subject: [PATCH 27/39] `ExprEngine::VisitObjCAtSynchronizedStmt`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp     | 1 +
 clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index b09b088e3b374..edfc7c26a6adb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1166,6 +1166,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) {
   case Stmt::CXXDefaultInitExprClass:
   case Stmt::CXXStdInitializerListExprClass:
   case Expr::ObjCArrayLiteralClass:
+  case Stmt::ObjCAtSynchronizedStmtClass:
   case Expr::ObjCDictionaryLiteralClass:
   case Expr::ObjCBoxedExprClass:
   case Stmt::AtomicExprClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 88cb1ef625674..10ebdfeff0ab9 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -31,7 +31,7 @@ void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
 void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
                                              ExplodedNode *Pred,
                                              ExplodedNodeSet &Dst) {
-  getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
+  Dst.insert(Pred);
 }
 
 void ExprEngine::populateObjCForDestinationSet(const ObjCForCollectionStmt *S,

>From c602cc03464b355be70ed54ce7ec497c30d30223 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Fri, 14 Aug 2026 18:10:03 +0200
Subject: [PATCH 28/39] Merge functions `justRunCheckersAsPreVisit` and
 `justRunCheckersAsPostVisit` into the function `shouldJustCallCheckers` and
 also document status of each statement kind.

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 454 +++++++++++++++----
 1 file changed, 355 insertions(+), 99 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index edfc7c26a6adb..d43bff3c27507 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1100,103 +1100,206 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() {
   return &cleanupTag;
 }
 
-static bool justRunCheckersAsPreVisit(const Stmt *S) {
+enum VisitKind {
+  PreVisitKind,
+  PostVisitKind,
+};
+
+static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
+
   switch (S->getStmtClass()) {
-  default:
+
+  // C++, OpenMP and ARC stuff we don't support yet.
+  case Stmt::CXXDependentScopeMemberExprClass:
+  case Stmt::CXXReflectExprClass:
+  case Stmt::CXXTryStmtClass:
+  case Stmt::CXXTypeidExprClass:
+  case Stmt::CXXUuidofExprClass:
+  case Stmt::CXXFoldExprClass:
+  case Stmt::MSPropertyRefExprClass:
+  case Stmt::MSPropertySubscriptExprClass:
+  case Stmt::CXXUnresolvedConstructExprClass:
+  case Stmt::DependentScopeDeclRefExprClass:
+  case Stmt::ArrayTypeTraitExprClass:
+  case Stmt::ExpressionTraitExprClass:
+  case Stmt::UnresolvedLookupExprClass:
+  case Stmt::UnresolvedMemberExprClass:
+  case Stmt::RecoveryExprClass:
+  case Stmt::CXXNoexceptExprClass:
+  case Stmt::PackExpansionExprClass:
+  case Stmt::PackIndexingExprClass:
+  case Stmt::SubstNonTypeTemplateParmPackExprClass:
+  case Stmt::FunctionParmPackExprClass:
+  case Stmt::CoroutineBodyStmtClass:
+  case Stmt::CoawaitExprClass:
+  case Stmt::DependentCoawaitExprClass:
+  case Stmt::CoreturnStmtClass:
+  case Stmt::CoyieldExprClass:
+  case Stmt::SEHTryStmtClass:
+  case Stmt::SEHExceptStmtClass:
+  case Stmt::SEHLeaveStmtClass:
+  case Stmt::SEHFinallyStmtClass:
+  case Stmt::CXXExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtInstantiationClass:
+  case Stmt::CXXExpansionSelectExprClass:
+  case Stmt::OMPCanonicalLoopClass:
+  case Stmt::OMPParallelDirectiveClass:
+  case Stmt::OMPSimdDirectiveClass:
+  case Stmt::OMPForDirectiveClass:
+  case Stmt::OMPForSimdDirectiveClass:
+  case Stmt::OMPSectionsDirectiveClass:
+  case Stmt::OMPSectionDirectiveClass:
+  case Stmt::OMPScopeDirectiveClass:
+  case Stmt::OMPSingleDirectiveClass:
+  case Stmt::OMPMasterDirectiveClass:
+  case Stmt::OMPCriticalDirectiveClass:
+  case Stmt::OMPParallelForDirectiveClass:
+  case Stmt::OMPParallelForSimdDirectiveClass:
+  case Stmt::OMPParallelSectionsDirectiveClass:
+  case Stmt::OMPParallelMasterDirectiveClass:
+  case Stmt::OMPParallelMaskedDirectiveClass:
+  case Stmt::OMPTaskDirectiveClass:
+  case Stmt::OMPTaskyieldDirectiveClass:
+  case Stmt::OMPBarrierDirectiveClass:
+  case Stmt::OMPTaskwaitDirectiveClass:
+  case Stmt::OMPErrorDirectiveClass:
+  case Stmt::OMPTaskgroupDirectiveClass:
+  case Stmt::OMPFlushDirectiveClass:
+  case Stmt::OMPDepobjDirectiveClass:
+  case Stmt::OMPScanDirectiveClass:
+  case Stmt::OMPOrderedDirectiveClass:
+  case Stmt::OMPAtomicDirectiveClass:
+  case Stmt::OMPAssumeDirectiveClass:
+  case Stmt::OMPTargetDirectiveClass:
+  case Stmt::OMPTargetDataDirectiveClass:
+  case Stmt::OMPTargetEnterDataDirectiveClass:
+  case Stmt::OMPTargetExitDataDirectiveClass:
+  case Stmt::OMPTargetParallelDirectiveClass:
+  case Stmt::OMPTargetParallelForDirectiveClass:
+  case Stmt::OMPTargetUpdateDirectiveClass:
+  case Stmt::OMPTeamsDirectiveClass:
+  case Stmt::OMPCancellationPointDirectiveClass:
+  case Stmt::OMPCancelDirectiveClass:
+  case Stmt::OMPTaskLoopDirectiveClass:
+  case Stmt::OMPTaskLoopSimdDirectiveClass:
+  case Stmt::OMPMasterTaskLoopDirectiveClass:
+  case Stmt::OMPMaskedTaskLoopDirectiveClass:
+  case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
+  case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
+  case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
+  case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
+  case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
+  case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
+  case Stmt::OMPDistributeDirectiveClass:
+  case Stmt::OMPDistributeParallelForDirectiveClass:
+  case Stmt::OMPDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPDistributeSimdDirectiveClass:
+  case Stmt::OMPTargetParallelForSimdDirectiveClass:
+  case Stmt::OMPTargetSimdDirectiveClass:
+  case Stmt::OMPTeamsDistributeDirectiveClass:
+  case Stmt::OMPTeamsDistributeSimdDirectiveClass:
+  case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
+  case Stmt::OMPTargetTeamsDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+  case Stmt::OMPReverseDirectiveClass:
+  case Stmt::OMPStripeDirectiveClass:
+  case Stmt::OMPTileDirectiveClass:
+  case Stmt::OMPInterchangeDirectiveClass:
+  case Stmt::OMPSplitDirectiveClass:
+  case Stmt::OMPFuseDirectiveClass:
+  case Stmt::OMPInteropDirectiveClass:
+  case Stmt::OMPDispatchDirectiveClass:
+  case Stmt::OMPMaskedDirectiveClass:
+  case Stmt::OMPGenericLoopDirectiveClass:
+  case Stmt::OMPTeamsGenericLoopDirectiveClass:
+  case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
+  case Stmt::OMPParallelGenericLoopDirectiveClass:
+  case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
+  case Stmt::CapturedStmtClass:
+  case Stmt::SYCLKernelCallStmtClass:
+  case Stmt::UnresolvedSYCLKernelCallStmtClass:
+  case Stmt::OpenACCComputeConstructClass:
+  case Stmt::OpenACCLoopConstructClass:
+  case Stmt::OpenACCCombinedConstructClass:
+  case Stmt::OpenACCDataConstructClass:
+  case Stmt::OpenACCEnterDataConstructClass:
+  case Stmt::OpenACCExitDataConstructClass:
+  case Stmt::OpenACCHostDataConstructClass:
+  case Stmt::OpenACCWaitConstructClass:
+  case Stmt::OpenACCCacheConstructClass:
+  case Stmt::OpenACCInitConstructClass:
+  case Stmt::OpenACCShutdownConstructClass:
+  case Stmt::OpenACCSetConstructClass:
+  case Stmt::OpenACCUpdateConstructClass:
+  case Stmt::OpenACCAtomicConstructClass:
+  case Stmt::OMPUnrollDirectiveClass:
+  case Stmt::OMPMetaDirectiveClass:
+  case Stmt::HLSLOutArgExprClass:
     return false;
 
-  // Cases not handled yet; but will handle some day.
-  case Stmt::DesignatedInitExprClass:
-  case Stmt::DesignatedInitUpdateExprClass:
-  case Stmt::ArrayInitIndexExprClass:
-  case Stmt::ExtVectorElementExprClass:
-  case Stmt::MatrixElementExprClass:
-  case Stmt::ImaginaryLiteralClass:
-  case Stmt::ObjCAtCatchStmtClass:
-  case Stmt::ObjCAtFinallyStmtClass:
-  case Stmt::ObjCAtTryStmtClass:
-  case Stmt::ObjCAutoreleasePoolStmtClass:
-  case Stmt::ObjCEncodeExprClass:
-  case Stmt::ObjCIsaExprClass:
-  case Stmt::ObjCProtocolExprClass:
-  case Stmt::ObjCSelectorExprClass:
-  case Stmt::ParenListExprClass:
-  case Stmt::ShuffleVectorExprClass:
-  case Stmt::ConvertVectorExprClass:
-  case Stmt::VAArgExprClass:
-  case Stmt::CUDAKernelCallExprClass:
-  case Stmt::OpaqueValueExprClass:
-  case Stmt::AsTypeExprClass:
-  case Stmt::ConceptSpecializationExprClass:
-  case Stmt::CXXRewrittenBinaryOperatorClass:
-  case Stmt::RequiresExprClass:
-  case Stmt::EmbedExprClass:
+  // ParenExprs already handled.
+  case Stmt::ParenExprClass:
+    return false;
 
-  // Cases we intentionally don't evaluate, since they don't need
-  // to be explicitly evaluated.
-  case Stmt::PredefinedExprClass:
-  case Stmt::AddrLabelExprClass:
-  case Stmt::IntegerLiteralClass:
-  case Stmt::FixedPointLiteralClass:
-  case Stmt::CharacterLiteralClass:
-  case Stmt::CXXScalarValueInitExprClass:
-  case Stmt::CXXBoolLiteralExprClass:
-  case Stmt::ObjCBoolLiteralExprClass:
-  case Stmt::ObjCAvailabilityCheckExprClass:
-  case Stmt::FloatingLiteralClass:
-  case Stmt::NoInitExprClass:
-  case Stmt::SizeOfPackExprClass:
-  case Stmt::StringLiteralClass:
-  case Stmt::SourceLocExprClass:
-  case Stmt::ObjCStringLiteralClass:
-  case Stmt::CXXPseudoDestructorExprClass:
-  case Stmt::SubstNonTypeTemplateParmExprClass:
-  case Stmt::CXXNullPtrLiteralExprClass:
-  case Stmt::ArraySectionExprClass:
-  case Stmt::OMPArrayShapingExprClass:
-  case Stmt::OMPIteratorExprClass:
-  case Stmt::SYCLUniqueStableNameExprClass:
-  case Stmt::OpenACCAsteriskSizeExprClass:
-  case Stmt::TypeTraitExprClass:
+  // GenericSelectionExprs already handled.
+  case Stmt::GenericSelectionExprClass:
+    return false;
 
-  case Stmt::ArrayInitLoopExprClass:
-  case Stmt::ArraySubscriptExprClass:
-  case Stmt::AttributedStmtClass:
-  case Stmt::CXXDefaultArgExprClass:
-  case Stmt::CXXDefaultInitExprClass:
-  case Stmt::CXXStdInitializerListExprClass:
-  case Expr::ObjCArrayLiteralClass:
+  // Cases that should never be evaluated simply because they shouldn't
+  // appear in the CFG.
+  case Stmt::BreakStmtClass:
+  case Stmt::CaseStmtClass:
+  case Stmt::CompoundStmtClass:
+  case Stmt::ContinueStmtClass:
+  case Stmt::CXXForRangeStmtClass:
+  case Stmt::DefaultStmtClass:
+  case Stmt::DoStmtClass:
+  case Stmt::ForStmtClass:
+  case Stmt::GotoStmtClass:
+  case Stmt::IfStmtClass:
+  case Stmt::IndirectGotoStmtClass:
+  case Stmt::LabelStmtClass:
+  case Stmt::NoStmtClass:
+  case Stmt::NullStmtClass:
+  case Stmt::SwitchStmtClass:
+  case Stmt::WhileStmtClass:
+  case Stmt::DeferStmtClass:
+  case Expr::MSDependentExistsStmtClass:
+    return false;
+
+  // These nodes are shared in the CFG and would case caching out.
+  // Moreover, no additional evaluation required for them, the
+  // analyzer can reconstruct these values from the AST.
+  case Stmt::ImplicitValueInitExprClass:
+    return false;
+
+  // These are handled by PseudoObjectExpr
+  case Stmt::ObjCSubscriptRefExprClass:
+  case Stmt::ObjCPropertyRefExprClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::GNUNullExprClass:
+    return false;
+
+  // FIXME: Does not call PostVisit checkers
   case Stmt::ObjCAtSynchronizedStmtClass:
-  case Expr::ObjCDictionaryLiteralClass:
-  case Expr::ObjCBoxedExprClass:
-  case Stmt::AtomicExprClass:
-  case Stmt::ImplicitCastExprClass:
-  case Stmt::CStyleCastExprClass:
-  case Stmt::CXXStaticCastExprClass:
-  case Stmt::CXXDynamicCastExprClass:
-  case Stmt::CXXReinterpretCastExprClass:
-  case Stmt::CXXConstCastExprClass:
-  case Stmt::CXXFunctionalCastExprClass:
-  case Stmt::BuiltinBitCastExprClass:
-  case Stmt::ObjCBridgedCastExprClass:
-  case Stmt::CXXAddrspaceCastExprClass:
+    return K == PreVisitKind;
+
+  // FIXME: They do not call checkers
+  case Expr::ConstantExprClass:
+  case Stmt::ExprWithCleanupsClass:
+    return false;
+
   case Stmt::CXXBindTemporaryExprClass:
-  case Stmt::GCCAsmStmtClass:
-  case Stmt::CXXNewExprClass:
-  case Stmt::MaterializeTemporaryExprClass:
-  case Stmt::MemberExprClass:
-  case Stmt::ReturnStmtClass:
-  case Stmt::OffsetOfExprClass:
-  case Stmt::UnaryExprOrTypeTraitExprClass:
-  case Stmt::UnaryOperatorClass:
     return true;
-  }
-}
 
-static bool justRunCheckersAsPostVisit(const Stmt *S) {
-  switch (S->getStmtClass()) {
-  default:
-    return false;
+  case Stmt::ArrayInitLoopExprClass:
+    return true;
 
   // Cases not handled yet; but will handle some day.
   case Stmt::DesignatedInitExprClass:
@@ -1224,6 +1327,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::CXXRewrittenBinaryOperatorClass:
   case Stmt::RequiresExprClass:
   case Stmt::EmbedExprClass:
+    return true;
 
   // Cases we intentionally don't evaluate, since they don't need
   // to be explicitly evaluated.
@@ -1251,20 +1355,121 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::SYCLUniqueStableNameExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
   case Stmt::TypeTraitExprClass:
+    return true;
 
-  case Stmt::ArrayInitLoopExprClass:
-  case Stmt::ArraySubscriptExprClass:
   case Stmt::AttributedStmtClass:
+    return true;
+
   case Stmt::CXXDefaultArgExprClass:
   case Stmt::CXXDefaultInitExprClass:
+    return true;
+
   case Stmt::CXXStdInitializerListExprClass:
   case Expr::ObjCArrayLiteralClass:
   case Expr::ObjCDictionaryLiteralClass:
   case Expr::ObjCBoxedExprClass:
-  case Stmt::AtomicExprClass:
-  case Stmt::ObjCIvarRefExprClass:
-  case Stmt::ObjCForCollectionStmtClass:
+    return true;
+
+  case Stmt::ArraySubscriptExprClass:
+    return true;
+
+  // Support for MatrixSingleSubscriptExprClass is not implemented.
+  case Stmt::MatrixSingleSubscriptExprClass:
+    return false;
+
+  // Support for MatrixSubscriptExpr is not implemented.
+  case Stmt::MatrixSubscriptExprClass:
+    return false;
+
+  case Stmt::GCCAsmStmtClass:
+    return true;
+
+  // FIXME: Does not call checkers
+  case Stmt::MSAsmStmtClass:
+    return false;
+
+  // FIXME: Does not call PreVisit checkers
   case Stmt::BlockExprClass:
+    return K == PostVisitKind;
+
+  // FIXME: Does not call PreVisit checkers
+  // Currently the engine does not call PostVisit checkers when
+  // lambda inlining is disabled, so K == PostVisiKind
+  // cannot be returned here.
+  case Stmt::LambdaExprClass:
+    return false;
+
+  // Checkers are called manually with custom logic when this calls
+  // VisitBinaryOperator, but calls no checkers during VisitLogicalExpr
+  case Stmt::BinaryOperatorClass:
+    return false;
+
+  // Checkers are called manually with custom logic in these cases
+  // (VisitCallExpr)
+  case Stmt::CXXOperatorCallExprClass:
+  case Stmt::CallExprClass:
+  case Stmt::CXXMemberCallExprClass:
+  case Stmt::UserDefinedLiteralClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::CXXCatchStmtClass:
+    return false;
+
+  // Checkers are called manually with custom logic in these cases
+  // (handleConstructor)
+  case Stmt::CXXTemporaryObjectExprClass:
+  case Stmt::CXXConstructExprClass:
+    return false;
+
+  // Checkers are called manually with custom logic in this case
+  // (handleConstructor)
+  case Stmt::CXXInheritedCtorInitExprClass:
+    return false;
+
+  case Stmt::CXXNewExprClass:
+    return true;
+
+  // FIXME:
+  // The engine calls both PreStmt and PostStmt checkers before
+  // actually evaluating this kind of expression. In a separate
+  // non NFC patch, the order should be modified to calling PreStmt
+  // checkers, then evaluating and finally calling PostStmt.
+  // Once that patch is merged, this stmt kind can also be refactored
+  // to the new algorithmic scheme.
+  case Stmt::CXXDeleteExprClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::ChooseExprClass:
+    return false;
+
+  // Checkers are called manually with custom logic in this case
+  // (VisitBinaryOperator)
+  case Stmt::CompoundAssignOperatorClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::CompoundLiteralExprClass:
+    return false;
+
+  // FIXME: These do not call checkers
+  case Stmt::BinaryConditionalOperatorClass:
+  case Stmt::ConditionalOperatorClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::CXXThisExprClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Stmt::DeclRefExprClass:
+    return false;
+
+  // Checkers are called manually with custom logic in this case
+  case Stmt::DeclStmtClass:
+    return false;
+
   case Stmt::ImplicitCastExprClass:
   case Stmt::CStyleCastExprClass:
   case Stmt::CXXStaticCastExprClass:
@@ -1275,16 +1480,67 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) {
   case Stmt::BuiltinBitCastExprClass:
   case Stmt::ObjCBridgedCastExprClass:
   case Stmt::CXXAddrspaceCastExprClass:
-  case Stmt::CXXBindTemporaryExprClass:
-  case Stmt::GCCAsmStmtClass:
-  case Stmt::CXXNewExprClass:
-  case Stmt::MaterializeTemporaryExprClass:
+    return true;
+
+  case Expr::MaterializeTemporaryExprClass:
+    return true;
+
+  // FIXME: These do not call checkers
+  // (ConstructInitList)
+  case Stmt::InitListExprClass:
+  case Expr::CXXParenListInitExprClass:
+    return false;
+
   case Stmt::MemberExprClass:
+    return true;
+
+  case Stmt::AtomicExprClass:
+    return true;
+
+  // FIXME: Does not call PreVisit checkers
+  case Stmt::ObjCIvarRefExprClass:
+    return K == PostVisitKind;
+
+  // FIXME: Does not call PreVisit checkers
+  case Stmt::ObjCForCollectionStmtClass:
+    return K == PostVisitKind;
+
+  // FIXME: Does not call checkers
+  case Stmt::ObjCMessageExprClass:
+    return false;
+
+  // FIXME: These do not call checkers
+  case Stmt::ObjCAtThrowStmtClass:
+  case Stmt::CXXThrowExprClass:
+    return false;
+
+  // FIXME: Does not call PostVisit checkers
+  case Stmt::ReturnStmtClass:
+    return K == PreVisitKind;
+
   case Stmt::OffsetOfExprClass:
+    return true;
+
   case Stmt::UnaryExprOrTypeTraitExprClass:
+    return true;
+
+  // FIXME: Does not call checkers
+  case Stmt::StmtExprClass:
+    return false;
+
   case Stmt::UnaryOperatorClass:
     return true;
+
+  // FIXME: Does not call checkers
+  case Stmt::PseudoObjectExprClass:
+    return false;
+
+  // FIXME: Does not call checkers
+  case Expr::ObjCIndirectCopyRestoreExprClass:
+    return false;
   }
+
+  return false;
 }
 
 void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
@@ -1305,7 +1561,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
   ExplodedNodeSet PreVisited;
   for (const auto I : CleanedStates) {
     ExplodedNodeSet Tmp;
-    if (justRunCheckersAsPreVisit(currStmt)) {
+    if (shouldJustCallCheckers(currStmt, PreVisitKind)) {
       getCheckerManager().runCheckersForPreStmt(Tmp, I, currStmt, *this);
       PreVisited.insert(Tmp);
     } else
@@ -1322,7 +1578,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
   ExplodedNodeSet PostVisited;
   for (const auto I : Visited) {
     ExplodedNodeSet Tmp;
-    if (justRunCheckersAsPostVisit(currStmt)) {
+    if (shouldJustCallCheckers(currStmt, PostVisitKind)) {
       getCheckerManager().runCheckersForPostStmt(Tmp, I, currStmt, *this);
       PostVisited.insert(Tmp);
     } else

>From 9da48e90e63017bf172c8833e85d61f61d39709e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 24 Aug 2026 10:32:43 +0200
Subject: [PATCH 29/39] Handle new statement kinds after rebase.

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d43bff3c27507..46075efddb4e7 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1124,6 +1124,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::ExpressionTraitExprClass:
   case Stmt::UnresolvedLookupExprClass:
   case Stmt::UnresolvedMemberExprClass:
+  case Stmt::DependentTemplateIdExprClass:
   case Stmt::RecoveryExprClass:
   case Stmt::CXXNoexceptExprClass:
   case Stmt::PackExpansionExprClass:
@@ -1167,7 +1168,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::OMPFlushDirectiveClass:
   case Stmt::OMPDepobjDirectiveClass:
   case Stmt::OMPScanDirectiveClass:
-  case Stmt::OMPOrderedDirectiveClass:
+  case Stmt::OMPOrderedStandaloneDirectiveClass:
+  case Stmt::OMPOrderedBlockAssocDirectiveClass:
   case Stmt::OMPAtomicDirectiveClass:
   case Stmt::OMPAssumeDirectiveClass:
   case Stmt::OMPTargetDirectiveClass:

>From 915e3623568486332a032962eeea1b939ffd493e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 24 Aug 2026 10:38:47 +0200
Subject: [PATCH 30/39] `ExprEngine::VisitCXXDeleteExpr`

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 26 ++++----------------
 1 file changed, 5 insertions(+), 21 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 46075efddb4e7..41513efaf98b8 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1432,15 +1432,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::CXXNewExprClass:
     return true;
 
-  // FIXME:
-  // The engine calls both PreStmt and PostStmt checkers before
-  // actually evaluating this kind of expression. In a separate
-  // non NFC patch, the order should be modified to calling PreStmt
-  // checkers, then evaluating and finally calling PostStmt.
-  // Once that patch is merged, this stmt kind can also be refactored
-  // to the new algorithmic scheme.
   case Stmt::CXXDeleteExprClass:
-    return false;
+    return true;
 
   // FIXME: Does not call checkers
   case Stmt::ChooseExprClass:
@@ -2546,21 +2539,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, Dst);
       break;
 
-    case Stmt::CXXDeleteExprClass: {
-      ExplodedNodeSet PreVisit;
-      const auto *CDE = cast<CXXDeleteExpr>(S);
-      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-
-      ExplodedNodeSet PostVisit;
-      for (const auto i : PreVisit)
-        VisitCXXDeleteExpr(CDE, i, PostVisit);
-
-      getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+    case Stmt::CXXDeleteExprClass:
+      VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), Pred, Dst);
       break;
-    }
-      // FIXME: ChooseExpr is really a constant.  We need to fix
-      //        the CFG do not model them as explicit control-flow.
 
+    // FIXME: ChooseExpr is really a constant.  We need to fix
+    //        the CFG do not model them as explicit control-flow.
     case Stmt::ChooseExprClass: { // __builtin_choose_expr
       const auto *C = cast<ChooseExpr>(S);
       VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);

>From 89b94721179e18fae14c97c38546d2d1d40dcfa9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 25 Aug 2026 16:08:53 +0200
Subject: [PATCH 31/39] Fix formatting.

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 41513efaf98b8..dc479b00162b6 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2448,8 +2448,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       // Escape pointers passed into the list, unless it's an ObjC boxed
       // expression which is not a boxable C structure.
       if (!(isa<ObjCBoxedExpr>(Ex) &&
-            !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
-                                    ->getType()->isRecordType()))
+            !cast<ObjCBoxedExpr>(Ex)->getSubExpr()->getType()->isRecordType()))
         for (auto Child : Ex->children()) {
           assert(Child);
           const auto *ChildExpr = dyn_cast<Expr>(Child);
@@ -3704,8 +3703,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
       // pointers as soon as they are used.
       if (!M->isGLValue()) {
         assert(M->getType()->isArrayType());
-        const auto *PE =
-          dyn_cast<ImplicitCastExpr>(Pred->getParentMap().getParentIgnoreParens(M));
+        const auto *PE = dyn_cast<ImplicitCastExpr>(
+            Pred->getParentMap().getParentIgnoreParens(M));
         if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) {
           llvm_unreachable("should always be wrapped in ArrayToPointerDecay");
         }
@@ -3718,8 +3717,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
           L = UnknownVal();
       }
 
-      Dst.insert(Engine.makeNodeWithBinding(
-          Pred, M, L, state, ProgramPoint::PostLValueKind));
+      Dst.insert(Engine.makeNodeWithBinding(Pred, M, L, state,
+                                            ProgramPoint::PostLValueKind));
     } else {
       evalLoad(Dst, M, M, Pred, state, L);
     }

>From 492717d67933965a8587da50891a6797e8f64173 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 16:58:31 +0200
Subject: [PATCH 32/39] Simplify switch in `shouldJustCallCheckers`

return true by default and return true for unsupported statement kinds
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 302 +------------------
 1 file changed, 1 insertion(+), 301 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index dc479b00162b6..f596ded35fd13 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1109,180 +1109,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
 
   switch (S->getStmtClass()) {
 
-  // C++, OpenMP and ARC stuff we don't support yet.
-  case Stmt::CXXDependentScopeMemberExprClass:
-  case Stmt::CXXReflectExprClass:
-  case Stmt::CXXTryStmtClass:
-  case Stmt::CXXTypeidExprClass:
-  case Stmt::CXXUuidofExprClass:
-  case Stmt::CXXFoldExprClass:
-  case Stmt::MSPropertyRefExprClass:
-  case Stmt::MSPropertySubscriptExprClass:
-  case Stmt::CXXUnresolvedConstructExprClass:
-  case Stmt::DependentScopeDeclRefExprClass:
-  case Stmt::ArrayTypeTraitExprClass:
-  case Stmt::ExpressionTraitExprClass:
-  case Stmt::UnresolvedLookupExprClass:
-  case Stmt::UnresolvedMemberExprClass:
-  case Stmt::DependentTemplateIdExprClass:
-  case Stmt::RecoveryExprClass:
-  case Stmt::CXXNoexceptExprClass:
-  case Stmt::PackExpansionExprClass:
-  case Stmt::PackIndexingExprClass:
-  case Stmt::SubstNonTypeTemplateParmPackExprClass:
-  case Stmt::FunctionParmPackExprClass:
-  case Stmt::CoroutineBodyStmtClass:
-  case Stmt::CoawaitExprClass:
-  case Stmt::DependentCoawaitExprClass:
-  case Stmt::CoreturnStmtClass:
-  case Stmt::CoyieldExprClass:
-  case Stmt::SEHTryStmtClass:
-  case Stmt::SEHExceptStmtClass:
-  case Stmt::SEHLeaveStmtClass:
-  case Stmt::SEHFinallyStmtClass:
-  case Stmt::CXXExpansionStmtPatternClass:
-  case Stmt::CXXExpansionStmtInstantiationClass:
-  case Stmt::CXXExpansionSelectExprClass:
-  case Stmt::OMPCanonicalLoopClass:
-  case Stmt::OMPParallelDirectiveClass:
-  case Stmt::OMPSimdDirectiveClass:
-  case Stmt::OMPForDirectiveClass:
-  case Stmt::OMPForSimdDirectiveClass:
-  case Stmt::OMPSectionsDirectiveClass:
-  case Stmt::OMPSectionDirectiveClass:
-  case Stmt::OMPScopeDirectiveClass:
-  case Stmt::OMPSingleDirectiveClass:
-  case Stmt::OMPMasterDirectiveClass:
-  case Stmt::OMPCriticalDirectiveClass:
-  case Stmt::OMPParallelForDirectiveClass:
-  case Stmt::OMPParallelForSimdDirectiveClass:
-  case Stmt::OMPParallelSectionsDirectiveClass:
-  case Stmt::OMPParallelMasterDirectiveClass:
-  case Stmt::OMPParallelMaskedDirectiveClass:
-  case Stmt::OMPTaskDirectiveClass:
-  case Stmt::OMPTaskyieldDirectiveClass:
-  case Stmt::OMPBarrierDirectiveClass:
-  case Stmt::OMPTaskwaitDirectiveClass:
-  case Stmt::OMPErrorDirectiveClass:
-  case Stmt::OMPTaskgroupDirectiveClass:
-  case Stmt::OMPFlushDirectiveClass:
-  case Stmt::OMPDepobjDirectiveClass:
-  case Stmt::OMPScanDirectiveClass:
-  case Stmt::OMPOrderedStandaloneDirectiveClass:
-  case Stmt::OMPOrderedBlockAssocDirectiveClass:
-  case Stmt::OMPAtomicDirectiveClass:
-  case Stmt::OMPAssumeDirectiveClass:
-  case Stmt::OMPTargetDirectiveClass:
-  case Stmt::OMPTargetDataDirectiveClass:
-  case Stmt::OMPTargetEnterDataDirectiveClass:
-  case Stmt::OMPTargetExitDataDirectiveClass:
-  case Stmt::OMPTargetParallelDirectiveClass:
-  case Stmt::OMPTargetParallelForDirectiveClass:
-  case Stmt::OMPTargetUpdateDirectiveClass:
-  case Stmt::OMPTeamsDirectiveClass:
-  case Stmt::OMPCancellationPointDirectiveClass:
-  case Stmt::OMPCancelDirectiveClass:
-  case Stmt::OMPTaskLoopDirectiveClass:
-  case Stmt::OMPTaskLoopSimdDirectiveClass:
-  case Stmt::OMPMasterTaskLoopDirectiveClass:
-  case Stmt::OMPMaskedTaskLoopDirectiveClass:
-  case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
-  case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
-  case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
-  case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
-  case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
-  case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
-  case Stmt::OMPDistributeDirectiveClass:
-  case Stmt::OMPDistributeParallelForDirectiveClass:
-  case Stmt::OMPDistributeParallelForSimdDirectiveClass:
-  case Stmt::OMPDistributeSimdDirectiveClass:
-  case Stmt::OMPTargetParallelForSimdDirectiveClass:
-  case Stmt::OMPTargetSimdDirectiveClass:
-  case Stmt::OMPTeamsDistributeDirectiveClass:
-  case Stmt::OMPTeamsDistributeSimdDirectiveClass:
-  case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
-  case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
-  case Stmt::OMPTargetTeamsDirectiveClass:
-  case Stmt::OMPTargetTeamsDistributeDirectiveClass:
-  case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
-  case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
-  case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
-  case Stmt::OMPReverseDirectiveClass:
-  case Stmt::OMPStripeDirectiveClass:
-  case Stmt::OMPTileDirectiveClass:
-  case Stmt::OMPInterchangeDirectiveClass:
-  case Stmt::OMPSplitDirectiveClass:
-  case Stmt::OMPFuseDirectiveClass:
-  case Stmt::OMPInteropDirectiveClass:
-  case Stmt::OMPDispatchDirectiveClass:
-  case Stmt::OMPMaskedDirectiveClass:
-  case Stmt::OMPGenericLoopDirectiveClass:
-  case Stmt::OMPTeamsGenericLoopDirectiveClass:
-  case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
-  case Stmt::OMPParallelGenericLoopDirectiveClass:
-  case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
-  case Stmt::CapturedStmtClass:
-  case Stmt::SYCLKernelCallStmtClass:
-  case Stmt::UnresolvedSYCLKernelCallStmtClass:
-  case Stmt::OpenACCComputeConstructClass:
-  case Stmt::OpenACCLoopConstructClass:
-  case Stmt::OpenACCCombinedConstructClass:
-  case Stmt::OpenACCDataConstructClass:
-  case Stmt::OpenACCEnterDataConstructClass:
-  case Stmt::OpenACCExitDataConstructClass:
-  case Stmt::OpenACCHostDataConstructClass:
-  case Stmt::OpenACCWaitConstructClass:
-  case Stmt::OpenACCCacheConstructClass:
-  case Stmt::OpenACCInitConstructClass:
-  case Stmt::OpenACCShutdownConstructClass:
-  case Stmt::OpenACCSetConstructClass:
-  case Stmt::OpenACCUpdateConstructClass:
-  case Stmt::OpenACCAtomicConstructClass:
-  case Stmt::OMPUnrollDirectiveClass:
-  case Stmt::OMPMetaDirectiveClass:
-  case Stmt::HLSLOutArgExprClass:
-    return false;
-
-  // ParenExprs already handled.
-  case Stmt::ParenExprClass:
-    return false;
-
-  // GenericSelectionExprs already handled.
-  case Stmt::GenericSelectionExprClass:
-    return false;
-
-  // Cases that should never be evaluated simply because they shouldn't
-  // appear in the CFG.
-  case Stmt::BreakStmtClass:
-  case Stmt::CaseStmtClass:
-  case Stmt::CompoundStmtClass:
-  case Stmt::ContinueStmtClass:
-  case Stmt::CXXForRangeStmtClass:
-  case Stmt::DefaultStmtClass:
-  case Stmt::DoStmtClass:
-  case Stmt::ForStmtClass:
-  case Stmt::GotoStmtClass:
-  case Stmt::IfStmtClass:
-  case Stmt::IndirectGotoStmtClass:
-  case Stmt::LabelStmtClass:
-  case Stmt::NoStmtClass:
-  case Stmt::NullStmtClass:
-  case Stmt::SwitchStmtClass:
-  case Stmt::WhileStmtClass:
-  case Stmt::DeferStmtClass:
-  case Expr::MSDependentExistsStmtClass:
-    return false;
-
-  // These nodes are shared in the CFG and would case caching out.
-  // Moreover, no additional evaluation required for them, the
-  // analyzer can reconstruct these values from the AST.
-  case Stmt::ImplicitValueInitExprClass:
-    return false;
-
-  // These are handled by PseudoObjectExpr
-  case Stmt::ObjCSubscriptRefExprClass:
-  case Stmt::ObjCPropertyRefExprClass:
-    return false;
+  default: return true;
 
   // FIXME: Does not call checkers
   case Stmt::GNUNullExprClass:
@@ -1297,95 +1124,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::ExprWithCleanupsClass:
     return false;
 
-  case Stmt::CXXBindTemporaryExprClass:
-    return true;
-
-  case Stmt::ArrayInitLoopExprClass:
-    return true;
-
-  // Cases not handled yet; but will handle some day.
-  case Stmt::DesignatedInitExprClass:
-  case Stmt::DesignatedInitUpdateExprClass:
-  case Stmt::ArrayInitIndexExprClass:
-  case Stmt::ExtVectorElementExprClass:
-  case Stmt::MatrixElementExprClass:
-  case Stmt::ImaginaryLiteralClass:
-  case Stmt::ObjCAtCatchStmtClass:
-  case Stmt::ObjCAtFinallyStmtClass:
-  case Stmt::ObjCAtTryStmtClass:
-  case Stmt::ObjCAutoreleasePoolStmtClass:
-  case Stmt::ObjCEncodeExprClass:
-  case Stmt::ObjCIsaExprClass:
-  case Stmt::ObjCProtocolExprClass:
-  case Stmt::ObjCSelectorExprClass:
-  case Stmt::ParenListExprClass:
-  case Stmt::ShuffleVectorExprClass:
-  case Stmt::ConvertVectorExprClass:
-  case Stmt::VAArgExprClass:
-  case Stmt::CUDAKernelCallExprClass:
-  case Stmt::OpaqueValueExprClass:
-  case Stmt::AsTypeExprClass:
-  case Stmt::ConceptSpecializationExprClass:
-  case Stmt::CXXRewrittenBinaryOperatorClass:
-  case Stmt::RequiresExprClass:
-  case Stmt::EmbedExprClass:
-    return true;
-
-  // Cases we intentionally don't evaluate, since they don't need
-  // to be explicitly evaluated.
-  case Stmt::PredefinedExprClass:
-  case Stmt::AddrLabelExprClass:
-  case Stmt::IntegerLiteralClass:
-  case Stmt::FixedPointLiteralClass:
-  case Stmt::CharacterLiteralClass:
-  case Stmt::CXXScalarValueInitExprClass:
-  case Stmt::CXXBoolLiteralExprClass:
-  case Stmt::ObjCBoolLiteralExprClass:
-  case Stmt::ObjCAvailabilityCheckExprClass:
-  case Stmt::FloatingLiteralClass:
-  case Stmt::NoInitExprClass:
-  case Stmt::SizeOfPackExprClass:
-  case Stmt::StringLiteralClass:
-  case Stmt::SourceLocExprClass:
-  case Stmt::ObjCStringLiteralClass:
-  case Stmt::CXXPseudoDestructorExprClass:
-  case Stmt::SubstNonTypeTemplateParmExprClass:
-  case Stmt::CXXNullPtrLiteralExprClass:
-  case Stmt::ArraySectionExprClass:
-  case Stmt::OMPArrayShapingExprClass:
-  case Stmt::OMPIteratorExprClass:
-  case Stmt::SYCLUniqueStableNameExprClass:
-  case Stmt::OpenACCAsteriskSizeExprClass:
-  case Stmt::TypeTraitExprClass:
-    return true;
-
-  case Stmt::AttributedStmtClass:
-    return true;
-
-  case Stmt::CXXDefaultArgExprClass:
-  case Stmt::CXXDefaultInitExprClass:
-    return true;
-
-  case Stmt::CXXStdInitializerListExprClass:
-  case Expr::ObjCArrayLiteralClass:
-  case Expr::ObjCDictionaryLiteralClass:
-  case Expr::ObjCBoxedExprClass:
-    return true;
-
-  case Stmt::ArraySubscriptExprClass:
-    return true;
-
-  // Support for MatrixSingleSubscriptExprClass is not implemented.
-  case Stmt::MatrixSingleSubscriptExprClass:
-    return false;
-
-  // Support for MatrixSubscriptExpr is not implemented.
-  case Stmt::MatrixSubscriptExprClass:
-    return false;
-
-  case Stmt::GCCAsmStmtClass:
-    return true;
-
   // FIXME: Does not call checkers
   case Stmt::MSAsmStmtClass:
     return false;
@@ -1429,12 +1167,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::CXXInheritedCtorInitExprClass:
     return false;
 
-  case Stmt::CXXNewExprClass:
-    return true;
-
-  case Stmt::CXXDeleteExprClass:
-    return true;
-
   // FIXME: Does not call checkers
   case Stmt::ChooseExprClass:
     return false;
@@ -1465,33 +1197,12 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::DeclStmtClass:
     return false;
 
-  case Stmt::ImplicitCastExprClass:
-  case Stmt::CStyleCastExprClass:
-  case Stmt::CXXStaticCastExprClass:
-  case Stmt::CXXDynamicCastExprClass:
-  case Stmt::CXXReinterpretCastExprClass:
-  case Stmt::CXXConstCastExprClass:
-  case Stmt::CXXFunctionalCastExprClass:
-  case Stmt::BuiltinBitCastExprClass:
-  case Stmt::ObjCBridgedCastExprClass:
-  case Stmt::CXXAddrspaceCastExprClass:
-    return true;
-
-  case Expr::MaterializeTemporaryExprClass:
-    return true;
-
   // FIXME: These do not call checkers
   // (ConstructInitList)
   case Stmt::InitListExprClass:
   case Expr::CXXParenListInitExprClass:
     return false;
 
-  case Stmt::MemberExprClass:
-    return true;
-
-  case Stmt::AtomicExprClass:
-    return true;
-
   // FIXME: Does not call PreVisit checkers
   case Stmt::ObjCIvarRefExprClass:
     return K == PostVisitKind;
@@ -1513,19 +1224,10 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::ReturnStmtClass:
     return K == PreVisitKind;
 
-  case Stmt::OffsetOfExprClass:
-    return true;
-
-  case Stmt::UnaryExprOrTypeTraitExprClass:
-    return true;
-
   // FIXME: Does not call checkers
   case Stmt::StmtExprClass:
     return false;
 
-  case Stmt::UnaryOperatorClass:
-    return true;
-
   // FIXME: Does not call checkers
   case Stmt::PseudoObjectExprClass:
     return false;
@@ -1534,8 +1236,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Expr::ObjCIndirectCopyRestoreExprClass:
     return false;
   }
-
-  return false;
 }
 
 void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {

>From 7cbe3020a561dbd106c58dfebfe5ab230d341ee8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 17:50:41 +0200
Subject: [PATCH 33/39] Revert "`ExprEngine::VisitUnaryOperator`"

This reverts commit 19d7f0cd055ab130057c2ed6d6f378acbab661cf.
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 19 +++-
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 93 ++++++++++---------
 2 files changed, 63 insertions(+), 49 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f596ded35fd13..94d23c74c80fc 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1109,7 +1109,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
 
   switch (S->getStmtClass()) {
 
-  default: return true;
+  default:
+    return true;
 
   // FIXME: Does not call checkers
   case Stmt::GNUNullExprClass:
@@ -1228,6 +1229,10 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
   case Stmt::StmtExprClass:
     return false;
 
+  // Checkers are called manually with custom logic in this case
+  case Stmt::UnaryOperatorClass:
+    return false;
+
   // FIXME: Does not call checkers
   case Stmt::PseudoObjectExprClass:
     return false;
@@ -2351,9 +2356,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       VisitStmtExpr(cast<StmtExpr>(S), Pred, Dst);
       break;
 
-    case Stmt::UnaryOperatorClass:
-      VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst);
+    case Stmt::UnaryOperatorClass: {
+      const auto *U = cast<UnaryOperator>(S);
+      if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
+        ExplodedNodeSet Tmp;
+        VisitUnaryOperator(U, Pred, Tmp);
+        evalEagerlyAssumeBifurcation(Dst, Tmp, U);
+      }
+      else
+        VisitUnaryOperator(U, Pred, Dst);
       break;
+    }
 
     case Stmt::PseudoObjectExprClass:
       VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S), Pred, Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 4e6f93d783fc1..e5ced3806de9b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -849,8 +849,13 @@ void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
   Dst.insert(Pred);
 }
 
-void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
+void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
                                     ExplodedNodeSet &Dst) {
+  // FIXME: Prechecks eventually go in ::Visit().
+  ExplodedNodeSet CheckedSet;
+  getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, U, *this);
+
+  ExplodedNodeSet EvalSet;
 
   // Lambda for handling the case when the operand is returned unchanged.
   auto MakeNodeForIdentityOp = [U, &Engine = Engine](ExplodedNode *N) {
@@ -859,11 +864,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
     return Engine.makeNodeWithBinding(N, U, SV);
   };
 
-  auto VisitUnaryOperatorImpl = [U, Pred, &Engine = Engine, this,
-                                 MakeNodeForIdentityOp](ExplodedNodeSet &Dst) {
+  for (ExplodedNode *N : CheckedSet) {
     switch (U->getOpcode()) {
     default: {
-      VisitIncrementDecrementOperator(U, Pred, Dst);
+      ExplodedNodeSet Tmp;
+      VisitIncrementDecrementOperator(U, N, Tmp);
+      EvalSet.insert(Tmp);
       break;
     }
     case UO_Real: {
@@ -872,13 +878,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
       // FIXME: We don't have complex SValues yet.
       if (Ex->getType()->isAnyComplexType()) {
         // Just report "Unknown."
-        Dst.insert(Pred);
+        EvalSet.insert(N);
         break;
       }
 
       // For all other types, UO_Real is an identity operation.
       assert (U->getType() == Ex->getType());
-      Dst.insert(MakeNodeForIdentityOp(Pred));
+      EvalSet.insert(MakeNodeForIdentityOp(N));
       break;
     }
 
@@ -887,12 +893,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
       // FIXME: We don't have complex SValues yet.
       if (Ex->getType()->isAnyComplexType()) {
         // Just report "Unknown."
-        Dst.insert(Pred);
+        EvalSet.insert(N);
         break;
       }
       // For all other types, UO_Imag returns 0.
       SVal X = svalBuilder.makeZeroVal(Ex->getType());
-      Dst.insert(Engine.makeNodeWithBinding(Pred, U, X));
+      EvalSet.insert(Engine.makeNodeWithBinding(N, U, X));
       break;
     }
 
@@ -904,12 +910,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
 
         if (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
           SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
-          Dst.insert(Engine.makeNodeWithBinding(Pred, U, SV));
+          EvalSet.insert(Engine.makeNodeWithBinding(N, U, SV));
           break;
         }
       }
       // Explicitly proceed with default handler for this case cascade.
-      Dst.insert(MakeNodeForIdentityOp(Pred));
+      EvalSet.insert(MakeNodeForIdentityOp(N));
       break;
     }
     case UO_Plus:
@@ -917,7 +923,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
       [[fallthrough]];
     case UO_Deref:
     case UO_Extension: {
-      Dst.insert(MakeNodeForIdentityOp(Pred));
+      EvalSet.insert(MakeNodeForIdentityOp(N));
       break;
     }
 
@@ -926,62 +932,57 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
     case UO_Not: {
       assert (!U->isGLValue());
       const Expr *Ex = U->getSubExpr()->IgnoreParens();
-      ProgramStateRef state = Pred->getState();
-      const StackFrame *SF = Pred->getStackFrame();
+      ProgramStateRef state = N->getState();
+      const StackFrame *SF = N->getStackFrame();
 
       // Get the value of the subexpression.
       SVal V = state->getSVal(Ex, SF);
 
       if (V.isUnknownOrUndef()) {
-        Dst.insert(Engine.makeNodeWithBinding(Pred, U, V));
+        EvalSet.insert(Engine.makeNodeWithBinding(N, U, V));
         break;
       }
 
       switch (U->getOpcode()) {
-      default:
-        llvm_unreachable("Invalid Opcode.");
-      case UO_Not:
-        // FIXME: Do we need to handle promotions?
-        state = state->BindExpr(U, SF,
-                                svalBuilder.evalComplement(V.castAs<NonLoc>()));
-        break;
-      case UO_Minus:
-        // FIXME: Do we need to handle promotions?
-        state =
-            state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
-        break;
-      case UO_LNot:
-        // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
-        //
-        //  Note: technically we do "E == 0", but this is the same in the
-        //    transfer functions as "0 == E".
-        SVal Result;
-        if (std::optional<Loc> LV = V.getAs<Loc>()) {
+        default:
+          llvm_unreachable("Invalid Opcode.");
+        case UO_Not:
+          // FIXME: Do we need to handle promotions?
+          state = state->BindExpr(
+              U, SF, svalBuilder.evalComplement(V.castAs<NonLoc>()));
+          break;
+        case UO_Minus:
+          // FIXME: Do we need to handle promotions?
+          state =
+              state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
+          break;
+        case UO_LNot:
+          // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
+          //
+          //  Note: technically we do "E == 0", but this is the same in the
+          //    transfer functions as "0 == E".
+          SVal Result;
+          if (std::optional<Loc> LV = V.getAs<Loc>()) {
           Loc X = svalBuilder.makeNullWithType(Ex->getType());
           Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
-        } else if (Ex->getType()->isFloatingType()) {
+          } else if (Ex->getType()->isFloatingType()) {
           // FIXME: handle floating point types.
           Result = UnknownVal();
-        } else {
+          } else {
           nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
           Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType());
-        }
+          }
 
-        state = state->BindExpr(U, SF, Result);
-        break;
+          state = state->BindExpr(U, SF, Result);
+          break;
       }
-      Dst.insert(Engine.makePostStmtNode(U, state, Pred));
+      EvalSet.insert(Engine.makePostStmtNode(U, state, N));
       break;
     }
     }
-  };
+  }
 
-  if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
-    ExplodedNodeSet Tmp;
-    VisitUnaryOperatorImpl(Tmp);
-    evalEagerlyAssumeBifurcation(Dst, Tmp, U);
-  } else
-    VisitUnaryOperatorImpl(Dst);
+  getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this);
 }
 
 void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,

>From bfa1a108584d439b62d96af76abc0329bfd0d800 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 18:17:31 +0200
Subject: [PATCH 34/39] Undo accidental change of `llvm_unreachable` message.

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 94d23c74c80fc..598ce5b12758b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3360,7 +3360,7 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
     Dst.insert(Engine.makePostStmtNode(A, state, Pred));
   } else {
     llvm_unreachable("Array subscript should be an lValue when not \
-a ctor and not a forbidden lvalue type");
+a vector and not a forbidden lvalue type");
   }
 }
 

>From 54d39c4d5d150b4a3ce42c1d9c5c7285915919bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:11:56 +0200
Subject: [PATCH 35/39] Delete redundant cast to `StmtExpr`

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 52d94d25ee096..a6281a54c2b29 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -619,7 +619,7 @@ class ExprEngine {
   void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
                                      ExplodedNode *Pred, ExplodedNodeSet &Dst);
 
-  void VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+  void VisitStmtExpr(const StmtExpr *SE, ExplodedNode *Pred,
                      ExplodedNodeSet &Dst);
 
   /// VisitUnaryOperator - Transfer function logic for unary operators.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index e5ced3806de9b..2dfc551fca398 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -834,9 +834,8 @@ void ExprEngine::VisitUnaryExprOrTypeTraitExpr(
   Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V));
 }
 
-void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+void ExprEngine::VisitStmtExpr(const StmtExpr *SE, ExplodedNode *Pred,
                                ExplodedNodeSet &Dst) {
-  const auto *SE = cast<StmtExpr>(S);
   if (SE->getSubStmt()->body_empty()) {
     // Empty statement expression.
     assert(SE->getType() == getContext().VoidTy &&

>From 7a4fe89964699d4e82e82c9070446db19b24bf70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:26:33 +0200
Subject: [PATCH 36/39] Change `VisitObjCIndirectCopyRestoreClass` suffix to
 `Expr`

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index a6281a54c2b29..13abe41378dac 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -629,7 +629,7 @@ class ExprEngine {
   void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred,
                              ExplodedNodeSet &Dst);
 
-  void VisitObjCIndirectCopyRestoreClass(const ObjCIndirectCopyRestoreExpr *OIE,
+  void VisitObjCIndirectCopyRestoreExpr(const ObjCIndirectCopyRestoreExpr *OIE,
                                          ExplodedNode *Pred,
                                          ExplodedNodeSet &Dst);
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 598ce5b12758b..f895b80db1590 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2373,7 +2373,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       break;
 
     case Expr::ObjCIndirectCopyRestoreExprClass:
-      VisitObjCIndirectCopyRestoreClass(cast<ObjCIndirectCopyRestoreExpr>(S),
+      VisitObjCIndirectCopyRestoreExpr(cast<ObjCIndirectCopyRestoreExpr>(S),
                                         Pred, Dst);
       break;
     }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 2dfc551fca398..c9a0313dd32ba 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -993,7 +993,7 @@ void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,
   Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V));
 }
 
-void ExprEngine::VisitObjCIndirectCopyRestoreClass(
+void ExprEngine::VisitObjCIndirectCopyRestoreExpr(
     const ObjCIndirectCopyRestoreExpr *OIE, ExplodedNode *Pred,
     ExplodedNodeSet &Dst) {
   // ObjCIndirectCopyRestoreExpr implies passing a temporary for

>From 7375eb3d994788295cc5dfd8061e84915e80f553 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:44:21 +0200
Subject: [PATCH 37/39] Hoist redundant declarations of `StackFrame` and
 `ProgramStateRef`

---
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 45 ++++++++-----------
 1 file changed, 19 insertions(+), 26 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c9a0313dd32ba..c60dbeabf74d4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -250,10 +250,10 @@ ExprEngine::handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
 void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
                                ExplodedNodeSet &Dst) {
   const Expr *Ex = CastE->getSubExpr();
+  ProgramStateRef State = Pred->getState();
+  const StackFrame *SF = Pred->getStackFrame();
 
   if (CastE->getCastKind() == CK_LValueToRValue) {
-    ProgramStateRef State = Pred->getState();
-    const StackFrame *SF = Pred->getStackFrame();
     evalLoad(Dst, CastE, CastE, Pred, State, State->getSVal(Ex, SF));
     return;
   }
@@ -262,8 +262,6 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
     ExplodedNodeSet DstEvalLoc;
 
     // Simulate the lvalue-to-rvalue conversion on `Ex`:
-    ProgramStateRef State = Pred->getState();
-    const StackFrame *SF = Pred->getStackFrame();
     evalLocation(DstEvalLoc, CastE, Ex, Pred, State, State->getSVal(Ex, SF),
                  true);
     // Simulate the operation that actually casts the original value to a new
@@ -294,9 +292,6 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
   if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
     T = ExCast->getTypeAsWritten();
 
-  ProgramStateRef state = Pred->getState();
-  const StackFrame *SF = Pred->getStackFrame();
-
   switch (CastE->getCastKind()) {
   case CK_LValueToRValue:
   case CK_LValueToRValueBitCast:
@@ -324,15 +319,13 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
   case CK_BuiltinFnToFnPtr:
   case CK_HLSLArrayRValue: {
     // Copy the SVal of Ex to CastE.
-    ProgramStateRef state = Pred->getState();
-    const StackFrame *SF = Pred->getStackFrame();
-    SVal V = state->getSVal(Ex, SF);
+    SVal V = State->getSVal(Ex, SF);
     Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
     return;
   }
   case CK_MemberPointerToBoolean:
   case CK_PointerToBoolean: {
-    SVal V = state->getSVal(Ex, SF);
+    SVal V = State->getSVal(Ex, SF);
     auto PTMSV = V.getAs<nonloc::PointerToMember>();
     if (PTMSV)
       V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
@@ -341,7 +334,7 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
       return;
     }
     // Explicitly proceed with default handler for this case cascade.
-    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    State = handleLValueBitCast(State, Ex, SF, T, ExTy, CastE, Dst, Pred);
     return;
   }
   case CK_Dependent:
@@ -351,13 +344,13 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
   case CK_BooleanToSignedIntegral:
   case CK_IntegralToPointer:
   case CK_PointerToIntegral: {
-    SVal V = state->getSVal(Ex, SF);
+    SVal V = State->getSVal(Ex, SF);
     if (isa<nonloc::PointerToMember>(V)) {
       Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, UnknownVal()));
       return;
     }
     // Explicitly proceed with default handler for this case cascade.
-    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    State = handleLValueBitCast(State, Ex, SF, T, ExTy, CastE, Dst, Pred);
     return;
   }
   case CK_IntegralToBoolean:
@@ -388,30 +381,30 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
   case CK_FixedPointToBoolean:
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint: {
-    state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+    State = handleLValueBitCast(State, Ex, SF, T, ExTy, CastE, Dst, Pred);
     return;
   }
   case CK_IntegralCast: {
     // Delegate to SValBuilder to process.
-    SVal V = state->getSVal(Ex, SF);
+    SVal V = State->getSVal(Ex, SF);
     if (AMgr.options.analyzerSymbolicIntegerCasts())
       V = svalBuilder.evalCast(V, T, ExTy);
     else
-      V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
+      V = svalBuilder.evalIntegralCast(State, V, T, ExTy);
     Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V));
     return;
   }
   case CK_DerivedToBase:
   case CK_UncheckedDerivedToBase: {
     // For DerivedToBase cast, delegate to the store manager.
-    SVal val = state->getSVal(Ex, SF);
+    SVal val = State->getSVal(Ex, SF);
     val = getStoreManager().evalDerivedToBase(val, CastE);
     Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val));
     return;
   }
   // Handle C++ dyn_cast.
   case CK_Dynamic: {
-    SVal val = state->getSVal(Ex, SF);
+    SVal val = State->getSVal(Ex, SF);
 
     // Compute the type of the result.
     QualType resultType = CastE->getType();
@@ -432,11 +425,11 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
       if (T->isReferenceType()) {
         // A bad_cast exception is thrown if input value is a reference.
         // Currently, we model this, by generating a sink.
-        Engine.makePostStmtNode(CastE, state, Pred, /*MarkAsSink=*/true);
+        Engine.makePostStmtNode(CastE, State, Pred, /*MarkAsSink=*/true);
         return;
       } else {
         // If the cast fails on a pointer, bind to 0.
-        state = state->BindExpr(CastE, SF,
+        State = State->BindExpr(CastE, SF,
                                 svalBuilder.makeNullWithType(resultType));
       }
     } else {
@@ -445,16 +438,16 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
         DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal(
             /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
             getNumVisitedCurrent());
-        state = state->BindExpr(CastE, SF, NewSym);
+        State = State->BindExpr(CastE, SF, NewSym);
       } else
         // Else, bind to the derived region value.
-        state = state->BindExpr(CastE, SF, val);
+        State = State->BindExpr(CastE, SF, val);
     }
-    Dst.insert(Engine.makePostStmtNode(CastE, state, Pred));
+    Dst.insert(Engine.makePostStmtNode(CastE, State, Pred));
     return;
   }
   case CK_BaseToDerived: {
-    SVal val = state->getSVal(Ex, SF);
+    SVal val = State->getSVal(Ex, SF);
     QualType resultType = CastE->getType();
     if (CastE->isGLValue())
       resultType = getContext().getPointerType(resultType);
@@ -486,7 +479,7 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
   case CK_DerivedToBaseMemberPointer:
   case CK_BaseToDerivedMemberPointer:
   case CK_ReinterpretMemberPointer: {
-    SVal V = state->getSVal(Ex, SF);
+    SVal V = State->getSVal(Ex, SF);
     if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
       SVal CastedPTMSV =
           svalBuilder.makePointerToMember(getBasicVals().accumCXXBase(

>From 7edb218b9d4b84ea09982ea2bd10b00aea7eee3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:49:12 +0200
Subject: [PATCH 38/39] Fix PostVisiKind typo

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f895b80db1590..cbbee66b6598f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1135,7 +1135,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
 
   // FIXME: Does not call PreVisit checkers
   // Currently the engine does not call PostVisit checkers when
-  // lambda inlining is disabled, so K == PostVisiKind
+  // lambda inlining is disabled, so K == PostVisitKind
   // cannot be returned here.
   case Stmt::LambdaExprClass:
     return false;

>From 5aab0028cc627e0f412ed1c486d87743020eb899 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
 <gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 16:12:29 +0200
Subject: [PATCH 39/39] Fix formatting.

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 13abe41378dac..7be3c14bc7dfe 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -630,8 +630,8 @@ class ExprEngine {
                              ExplodedNodeSet &Dst);
 
   void VisitObjCIndirectCopyRestoreExpr(const ObjCIndirectCopyRestoreExpr *OIE,
-                                         ExplodedNode *Pred,
-                                         ExplodedNodeSet &Dst);
+                                        ExplodedNode *Pred,
+                                        ExplodedNodeSet &Dst);
 
   /// Handle ++ and -- (both pre- and post-increment).
   void VisitIncrementDecrementOperator(const UnaryOperator* U,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index cbbee66b6598f..48d4e1b39fc9f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2374,7 +2374,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
 
     case Expr::ObjCIndirectCopyRestoreExprClass:
       VisitObjCIndirectCopyRestoreExpr(cast<ObjCIndirectCopyRestoreExpr>(S),
-                                        Pred, Dst);
+                                       Pred, Dst);
       break;
     }
 }



More information about the cfe-commits mailing list