https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/219726

>From cd800a911d402fafe1f32e4ff2709d5061732e08 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Sat, 29 Aug 2026 22:41:18 +0200
Subject: [PATCH 01/11] [analyzer] Resolve lambda captures for explicit object
 parameters

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 26 +++++++++--
 .../test/Analysis/explicit-lambda-capture.cpp | 43 +++++++++++++++++++
 2 files changed, 65 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Analysis/explicit-lambda-capture.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e6349eb4eba2a..01e05924a2537 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3040,12 +3040,30 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
       // Sema follows a sequence of complex rules to determine whether the
       // variable should be captured.
       if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
-        Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
-        SVal CXXThisVal = state->getSVal(CXXThis);
-        return std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType());
+        if (MD->isImplicitObjectMemberFunction()) {
+          Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
+          SVal CXXThisVal = state->getSVal(CXXThis);
+          return std::make_pair(state->getLValue(FD, CXXThisVal),
+                                FD->getType());
+        }
+        const ParmVarDecl *PVD = MD->getParamDecl(0);
+        if (const Expr *CallSite = SF->getCallSite()) {
+          unsigned Idx = PVD->getFunctionScopeIndex();
+          const ParamVarRegion *PVR =
+              state->getStateManager().getRegionManager().getParamVarRegion(
+                  CallSite, Idx, SF);
+          const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
+          state =
+              state->bindLoc(loc::MemRegionVal(PVR),
+                             state->getSVal(SelfArgExpr, SF->getParent()), SF);
+          SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
+          if (!PVD->getType()->isReferenceType())
+            return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)),
+                                  FD->getType());
+          return std::make_pair(state->getLValue(FD, ParamSVal), 
FD->getType());
+        }
       }
     }
-
     return std::nullopt;
   };
 
diff --git a/clang/test/Analysis/explicit-lambda-capture.cpp 
b/clang/test/Analysis/explicit-lambda-capture.cpp
new file mode 100644
index 0000000000000..2d98b80ce9637
--- /dev/null
+++ b/clang/test/Analysis/explicit-lambda-capture.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -analyze -std=c++23 -analyzer-checker=core.DivideZero 
-verify %s
+
+int implicit_capture_by_value() {
+  int d = 0;
+  auto lam = [d]() { return 1 / d; }; // expected-warning {{Division by zero}}
+  return lam(); 
+}
+
+int explicit_rvalue_self_capture_by_reference() {
+  int d = 0;
+  auto lam = [&d](this auto &&self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
+  return lam(); 
+}
+
+int gh218708_explicit_rvalue_self() {
+  int d = 0;
+  auto lam = [d](this auto &&self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
+  return lam(); 
+}
+
+int gh218708_explicit_lvalue_self() {
+  int d = 0;
+  auto lam = [d](this auto &self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
+  return lam(); 
+}
+
+int gh218708_explicit_by_value_self() {
+  int d = 0;
+  auto lam = [d](this auto self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
+  return lam();
+}
+
+int explicit_rvalue_no_error() {
+  int d = 5;
+  auto lam = [d](this auto &&self) { return 1 / d; }; // 'd' is non-zero so 
there is no division by zero error. 
+  return lam(); 
+}
+
+int explicit_by_value_no_error() {
+  int d = 9;
+  auto lam = [d](this auto self) { return 1 / d; }; // 'd' is non-zero so 
there is no division by zero error.
+  return lam();
+}

>From 14f335aaecfcfe15de5aa0550c210b97e20a5283 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Sat, 29 Aug 2026 22:58:50 +0200
Subject: [PATCH 02/11] Fix unconditional compute for the reference type
 construction.

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

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 01e05924a2537..0ab2d22b87fe2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3053,14 +3053,15 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
               state->getStateManager().getRegionManager().getParamVarRegion(
                   CallSite, Idx, SF);
           const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
-          state =
-              state->bindLoc(loc::MemRegionVal(PVR),
-                             state->getSVal(SelfArgExpr, SF->getParent()), SF);
-          SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
-          if (!PVD->getType()->isReferenceType())
-            return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)),
+          if (PVD->getType()->isReferenceType()) {
+            state = state->bindLoc(loc::MemRegionVal(PVR),
+                                   state->getSVal(SelfArgExpr, 
SF->getParent()),
+                                   SF);
+            SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
+            return std::make_pair(state->getLValue(FD, ParamSVal),
                                   FD->getType());
-          return std::make_pair(state->getLValue(FD, ParamSVal), 
FD->getType());
+          }
+          return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)), 
FD->getType());
         }
       }
     }

>From 19674e6cff8045269ef8655cd4e16816127af6f4 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Sat, 29 Aug 2026 23:05:25 +0200
Subject: [PATCH 03/11] Fix formatting issue

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

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 0ab2d22b87fe2..c503d686fb407 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3061,7 +3061,8 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
             return std::make_pair(state->getLValue(FD, ParamSVal),
                                   FD->getType());
           }
-          return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)), 
FD->getType());
+          return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)),
+                                FD->getType());
         }
       }
     }

>From 57ec566d0cf17f95ae58630740d8c90be7d2877b Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Mon, 31 Aug 2026 11:43:52 +0200
Subject: [PATCH 04/11] Correct comments and RUN line.

---
 clang/test/Analysis/explicit-lambda-capture.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/test/Analysis/explicit-lambda-capture.cpp 
b/clang/test/Analysis/explicit-lambda-capture.cpp
index 2d98b80ce9637..89d249a63da43 100644
--- a/clang/test/Analysis/explicit-lambda-capture.cpp
+++ b/clang/test/Analysis/explicit-lambda-capture.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -std=c++23 -analyzer-checker=core.DivideZero 
-verify %s
+// RUN: %clang_analyze_cc1 -std=c++23 -analyzer-checker=core -verify %s
 
 int implicit_capture_by_value() {
   int d = 0;
@@ -32,12 +32,12 @@ int gh218708_explicit_by_value_self() {
 
 int explicit_rvalue_no_error() {
   int d = 5;
-  auto lam = [d](this auto &&self) { return 1 / d; }; // 'd' is non-zero so 
there is no division by zero error. 
+  auto lam = [d](this auto &&self) { return 1 / d; }; // no-warning 
   return lam(); 
 }
 
 int explicit_by_value_no_error() {
   int d = 9;
-  auto lam = [d](this auto self) { return 1 / d; }; // 'd' is non-zero so 
there is no division by zero error.
+  auto lam = [d](this auto self) { return 1 / d; }; // no-warning 
   return lam();
 }

>From 9c54a9ba6ebd4f50e021855294c40465bae60de2 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Mon, 31 Aug 2026 12:35:51 +0200
Subject: [PATCH 05/11] Created resolveAsLambdaCapturedVar function and
 included the MRMgr direct reference.

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 68d4362aca941..19ceec5cf2df6 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -677,6 +677,10 @@ class ExprEngine {
   static std::pair<const ProgramPointTag *, const ProgramPointTag *>
   getEagerlyAssumeBifurcationTags();
 
+  std::optional<std::pair<SVal, QualType>>
+  resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
+                             ExplodedNode *Pred);
+
   ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
                                       const StackFrame *SF, QualType T,
                                       QualType ExTy, const CastExpr *CastE,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index c503d686fb407..1f047c82c32e9 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3018,63 +3018,62 @@ void ExprEngine::processSwitch(const SwitchStmt 
*Switch, ExplodedNode *Pred,
 // Transfer functions: Loads and stores.
 
//===----------------------------------------------------------------------===//
 
-void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
-                                        ExplodedNode *Pred,
-                                        ExplodedNodeSet &Dst) {
+std::optional<std::pair<SVal, QualType>>
+ExprEngine::resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
+                                       ExplodedNode *Pred) {
   ProgramStateRef state = Pred->getState();
   const StackFrame *SF = Pred->getStackFrame();
 
-  auto resolveAsLambdaCapturedVar =
-      [&](const ValueDecl *VD) -> std::optional<std::pair<SVal, QualType>> {
-    const auto *MD = dyn_cast<CXXMethodDecl>(SF->getDecl());
-    const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
-    if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
-        DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
-        MD->getParent()->isLambda()) {
-      // Lookup the field of the lambda.
-      const CXXRecordDecl *CXXRec = MD->getParent();
-      llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
-      FieldDecl *LambdaThisCaptureField;
-      CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
-
-      // Sema follows a sequence of complex rules to determine whether the
-      // variable should be captured.
-      if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
-        if (MD->isImplicitObjectMemberFunction()) {
-          Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
-          SVal CXXThisVal = state->getSVal(CXXThis);
-          return std::make_pair(state->getLValue(FD, CXXThisVal),
-                                FD->getType());
-        }
-        const ParmVarDecl *PVD = MD->getParamDecl(0);
-        if (const Expr *CallSite = SF->getCallSite()) {
-          unsigned Idx = PVD->getFunctionScopeIndex();
-          const ParamVarRegion *PVR =
-              state->getStateManager().getRegionManager().getParamVarRegion(
-                  CallSite, Idx, SF);
-          const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
-          if (PVD->getType()->isReferenceType()) {
-            state = state->bindLoc(loc::MemRegionVal(PVR),
-                                   state->getSVal(SelfArgExpr, 
SF->getParent()),
-                                   SF);
-            SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
-            return std::make_pair(state->getLValue(FD, ParamSVal),
-                                  FD->getType());
-          }
-          return std::make_pair(state->getLValue(FD, loc::MemRegionVal(PVR)),
-                                FD->getType());
+  const auto *MD = dyn_cast<CXXMethodDecl>(SF->getDecl());
+  const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
+  if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
+      DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
+      MD->getParent()->isLambda()) {
+    // Lookup the field of the lambda.
+    const CXXRecordDecl *CXXRec = MD->getParent();
+    llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
+    FieldDecl *LambdaThisCaptureField;
+    CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
+
+    // Sema follows a sequence of complex rules to determine whether the
+    // variable should be captured.
+    if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
+      if (MD->isImplicitObjectMemberFunction()) {
+        Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
+        SVal CXXThisVal = state->getSVal(CXXThis);
+        return {{state->getLValue(FD, CXXThisVal), FD->getType()}};
+      }
+      const ParmVarDecl *PVD = MD->getParamDecl(0);
+      if (const Expr *CallSite = SF->getCallSite()) {
+        const ParamVarRegion *PVR =
+            MRMgr.getParamVarRegion(CallSite, /*Index=*/0, SF);
+        const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
+        if (PVD->getType()->isReferenceType()) {
+          state =
+              state->bindLoc(loc::MemRegionVal(PVR),
+                             state->getSVal(SelfArgExpr, SF->getParent()), SF);
+          SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
+          return {{state->getLValue(FD, ParamSVal), FD->getType()}};
         }
+        return {{state->getLValue(FD, loc::MemRegionVal(PVR)), FD->getType()}};
       }
     }
-    return std::nullopt;
-  };
+  }
+  return std::nullopt;
+}
+
+void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
+                                        ExplodedNode *Pred,
+                                        ExplodedNodeSet &Dst) {
+  ProgramStateRef state = Pred->getState();
+  const StackFrame *SF = Pred->getStackFrame();
 
   if (const auto *VD = dyn_cast<VarDecl>(D)) {
     // C permits "extern void v", and if you cast the address to a valid type,
     // you can even do things with it. We simply pretend
     assert(Ex->isGLValue() || VD->getType()->isVoidType());
     std::optional<std::pair<SVal, QualType>> VInfo =
-        resolveAsLambdaCapturedVar(VD);
+        resolveAsLambdaCapturedVar(Ex, VD, Pred);
 
     if (!VInfo)
       VInfo = std::make_pair(state->getLValue(VD, SF), VD->getType());
@@ -3116,7 +3115,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
   if (const auto *BD = dyn_cast<BindingDecl>(D)) {
     // Handle structured bindings captured by lambda.
     if (std::optional<std::pair<SVal, QualType>> VInfo =
-            resolveAsLambdaCapturedVar(BD)) {
+            resolveAsLambdaCapturedVar(Ex, BD, Pred)) {
       auto [V, T] = VInfo.value();
 
       if (T->isReferenceType()) {

>From fbb50ddfec1f359fa09da7e85fcc78e2e4c8aabc Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Mon, 31 Aug 2026 22:33:31 +0200
Subject: [PATCH 06/11] Add more test cases.

---
 .../test/Analysis/explicit-lambda-capture.cpp | 37 ++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/clang/test/Analysis/explicit-lambda-capture.cpp 
b/clang/test/Analysis/explicit-lambda-capture.cpp
index 89d249a63da43..5d5100f8eb723 100644
--- a/clang/test/Analysis/explicit-lambda-capture.cpp
+++ b/clang/test/Analysis/explicit-lambda-capture.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_analyze_cc1 -std=c++23 -analyzer-checker=core -verify %s
+// RUN: %clang_analyze_cc1 -std=c++23 -analyzer-checker=core,cplusplus.Move 
-verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
 
 int implicit_capture_by_value() {
   int d = 0;
@@ -41,3 +43,36 @@ int explicit_by_value_no_error() {
   auto lam = [d](this auto self) { return 1 / d; }; // no-warning 
   return lam();
 }
+
+auto by_val() {
+  std::vector<int> v;
+  auto lam = [v](this auto self) {
+    auto res = std::move(v);
+    return res;
+  };
+
+  lam();
+  lam();
+}
+
+auto by_lval() {
+  std::vector<int> v;
+  auto lam = [v](this auto &self) {
+    auto res = std::move(v); // expected-warning {{Moved-from object '' of 
type 'std::vector' is moved}}
+    return res;
+  };
+
+  lam();
+  lam();
+}
+
+auto by_rval() {
+  std::vector<int> v;
+  auto lam = [v](this auto &&self) {
+    auto res = std::move(v); // expected-warning {{Moved-from object '' of 
type 'std::vector' is moved}}
+    return res;
+  };
+
+  std::move(lam)();
+  std::move(lam)();
+}

>From fa1d78f86d8e8377bcd5e93ce61dea425985676b Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 2 Sep 2026 00:00:06 +0200
Subject: [PATCH 07/11] Apply nit changes.

---
 .../Core/PathSensitive/ExprEngine.h           |  2 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 64 +++++++++----------
 .../test/Analysis/explicit-lambda-capture.cpp | 14 ++--
 3 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 19ceec5cf2df6..784881e8270e3 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -679,7 +679,7 @@ class ExprEngine {
 
   std::optional<std::pair<SVal, QualType>>
   resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
-                             ExplodedNode *Pred);
+                             ExplodedNode *Pred) const;
 
   ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
                                       const StackFrame *SF, QualType T,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1f047c82c32e9..212261225820b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3020,43 +3020,43 @@ void ExprEngine::processSwitch(const SwitchStmt 
*Switch, ExplodedNode *Pred,
 
 std::optional<std::pair<SVal, QualType>>
 ExprEngine::resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
-                                       ExplodedNode *Pred) {
-  ProgramStateRef state = Pred->getState();
+                                       ExplodedNode *Pred) const {
+  ProgramStateRef State = Pred->getState();
   const StackFrame *SF = Pred->getStackFrame();
 
   const auto *MD = dyn_cast<CXXMethodDecl>(SF->getDecl());
   const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
-  if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
-      DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
-      MD->getParent()->isLambda()) {
-    // Lookup the field of the lambda.
-    const CXXRecordDecl *CXXRec = MD->getParent();
-    llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
-    FieldDecl *LambdaThisCaptureField;
-    CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
-
-    // Sema follows a sequence of complex rules to determine whether the
-    // variable should be captured.
-    if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
-      if (MD->isImplicitObjectMemberFunction()) {
-        Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
-        SVal CXXThisVal = state->getSVal(CXXThis);
-        return {{state->getLValue(FD, CXXThisVal), FD->getType()}};
-      }
-      const ParmVarDecl *PVD = MD->getParamDecl(0);
-      if (const Expr *CallSite = SF->getCallSite()) {
-        const ParamVarRegion *PVR =
-            MRMgr.getParamVarRegion(CallSite, /*Index=*/0, SF);
-        const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
-        if (PVD->getType()->isReferenceType()) {
-          state =
-              state->bindLoc(loc::MemRegionVal(PVR),
-                             state->getSVal(SelfArgExpr, SF->getParent()), SF);
-          SVal ParamSVal = state->getSVal(loc::MemRegionVal(PVR));
-          return {{state->getLValue(FD, ParamSVal), FD->getType()}};
-        }
-        return {{state->getLValue(FD, loc::MemRegionVal(PVR)), FD->getType()}};
+  if (!(AMgr.options.ShouldInlineLambdas && DeclRefEx &&
+        DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
+        MD->getParent()->isLambda()))
+    return std::nullopt;
+  // Lookup the field of the lambda.
+  const CXXRecordDecl *CXXRec = MD->getParent();
+  llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
+  FieldDecl *LambdaThisCaptureField;
+  CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
+
+  // Sema follows a sequence of complex rules to determine whether the
+  // variable should be captured.
+  if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
+    if (MD->isImplicitObjectMemberFunction()) {
+      Loc CXXThis = svalBuilder.getCXXThis(MD, SF);
+      SVal CXXThisVal = State->getSVal(CXXThis);
+      return {{State->getLValue(FD, CXXThisVal), FD->getType()}};
+    }
+    const ParmVarDecl *PVD = MD->getParamDecl(0);
+    if (const Expr *CallSite = SF->getCallSite()) {
+      const ParamVarRegion *PVR =
+          MRMgr.getParamVarRegion(CallSite, /*Index=*/0, SF);
+      const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
+      if (PVD->getType()->isReferenceType()) {
+        State =
+            State->bindLoc(loc::MemRegionVal(PVR),
+                           State->getSVal(SelfArgExpr, SF->getParent()), SF);
+        SVal ParamSVal = State->getSVal(loc::MemRegionVal(PVR));
+        return {{State->getLValue(FD, ParamSVal), FD->getType()}};
       }
+      return {{State->getLValue(FD, loc::MemRegionVal(PVR)), FD->getType()}};
     }
   }
   return std::nullopt;
diff --git a/clang/test/Analysis/explicit-lambda-capture.cpp 
b/clang/test/Analysis/explicit-lambda-capture.cpp
index 5d5100f8eb723..69b542a4af2cf 100644
--- a/clang/test/Analysis/explicit-lambda-capture.cpp
+++ b/clang/test/Analysis/explicit-lambda-capture.cpp
@@ -5,25 +5,25 @@
 int implicit_capture_by_value() {
   int d = 0;
   auto lam = [d]() { return 1 / d; }; // expected-warning {{Division by zero}}
-  return lam(); 
+  return lam();
 }
 
 int explicit_rvalue_self_capture_by_reference() {
   int d = 0;
   auto lam = [&d](this auto &&self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
-  return lam(); 
+  return lam();
 }
 
 int gh218708_explicit_rvalue_self() {
   int d = 0;
   auto lam = [d](this auto &&self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
-  return lam(); 
+  return lam();
 }
 
 int gh218708_explicit_lvalue_self() {
   int d = 0;
   auto lam = [d](this auto &self) { return 1 / d; }; // expected-warning 
{{Division by zero}}
-  return lam(); 
+  return lam();
 }
 
 int gh218708_explicit_by_value_self() {
@@ -34,13 +34,13 @@ int gh218708_explicit_by_value_self() {
 
 int explicit_rvalue_no_error() {
   int d = 5;
-  auto lam = [d](this auto &&self) { return 1 / d; }; // no-warning 
-  return lam(); 
+  auto lam = [d](this auto &&self) { return 1 / d; }; // no-warning
+  return lam();
 }
 
 int explicit_by_value_no_error() {
   int d = 9;
-  auto lam = [d](this auto self) { return 1 / d; }; // no-warning 
+  auto lam = [d](this auto self) { return 1 / d; }; // no-warning
   return lam();
 }
 

>From 3ce6a48cbbad9131598daaa514775d774f0d7950 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 2 Sep 2026 11:36:10 +0200
Subject: [PATCH 08/11] Move resolveAsLambdacapturedVar function to the private
 section and add doc comments.

---
 .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h    | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 784881e8270e3..7a6a233cb00d8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -677,15 +677,18 @@ class ExprEngine {
   static std::pair<const ProgramPointTag *, const ProgramPointTag *>
   getEagerlyAssumeBifurcationTags();
 
-  std::optional<std::pair<SVal, QualType>>
-  resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
-                             ExplodedNode *Pred) const;
-
   ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
                                       const StackFrame *SF, QualType T,
                                       QualType ExTy, const CastExpr *CastE,
                                       ExplodedNodeSet &Dst, ExplodedNode 
*Pred);
 
+private:
+  /// Resolve a lambda-captured variable's address based on whether the
+  /// enclosing method has an implicit or explicit object paramter.
+  std::optional<std::pair<SVal, QualType>>
+  resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
+                             ExplodedNode *Pred) const;
+
 public:
   SVal evalBinOp(ProgramStateRef ST, BinaryOperator::Opcode Op,
                  SVal LHS, SVal RHS, QualType T) {

>From 126316c721740b01b3729b46b402c301e9bef90c Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 2 Sep 2026 12:15:42 +0200
Subject: [PATCH 09/11] Fix a typo.

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 7a6a233cb00d8..aff56f55253e1 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -684,7 +684,7 @@ class ExprEngine {
 
 private:
   /// Resolve a lambda-captured variable's address based on whether the
-  /// enclosing method has an implicit or explicit object paramter.
+  /// enclosing method has an implicit or explicit object parameter.
   std::optional<std::pair<SVal, QualType>>
   resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
                              ExplodedNode *Pred) const;

>From c84a947f99bc05da849e0bb4ebbf039e8811431e Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 2 Sep 2026 21:24:48 +0200
Subject: [PATCH 10/11] Apply de-morgan and add braces for guarded block and
 TODO comment.

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

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 212261225820b..45830101bd2d7 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3026,10 +3026,11 @@ ExprEngine::resolveAsLambdaCapturedVar(const Expr *Ex, 
const ValueDecl *VD,
 
   const auto *MD = dyn_cast<CXXMethodDecl>(SF->getDecl());
   const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
-  if (!(AMgr.options.ShouldInlineLambdas && DeclRefEx &&
-        DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
-        MD->getParent()->isLambda()))
+  if (!AMgr.options.ShouldInlineLambdas || !DeclRefEx ||
+      !DeclRefEx->refersToEnclosingVariableOrCapture() || !MD ||
+      !MD->getParent()->isLambda()) {
     return std::nullopt;
+  }
   // Lookup the field of the lambda.
   const CXXRecordDecl *CXXRec = MD->getParent();
   llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
@@ -3050,6 +3051,12 @@ ExprEngine::resolveAsLambdaCapturedVar(const Expr *Ex, 
const ValueDecl *VD,
           MRMgr.getParamVarRegion(CallSite, /*Index=*/0, SF);
       const Expr *SelfArgExpr = cast<CallExpr>(CallSite)->getArg(0);
       if (PVD->getType()->isReferenceType()) {
+        // TODO: This binding should happen at call entry instead. The same way
+        // it does for the implicit object parameter (CXXThisRegion, bound in
+        // CXXInstanceCall::getInitialStackFrameContents). The explicit object
+        // parameter's ParamVarRegion is never bound there today, so this
+        // binding is just a workaround. A follow-up PR should properly bind it
+        // at call entry, so it is no longer needed here.
         State =
             State->bindLoc(loc::MemRegionVal(PVR),
                            State->getSVal(SelfArgExpr, SF->getParent()), SF);

>From 6f0018a435b50238c7c57c2a4e9e574c5058cfc3 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Thu, 3 Sep 2026 17:53:08 +0200
Subject: [PATCH 11/11] Make Pred const.

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

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index aff56f55253e1..8db14fa185b77 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -687,7 +687,7 @@ class ExprEngine {
   /// enclosing method has an implicit or explicit object parameter.
   std::optional<std::pair<SVal, QualType>>
   resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
-                             ExplodedNode *Pred) const;
+                             const ExplodedNode *Pred) const;
 
 public:
   SVal evalBinOp(ProgramStateRef ST, BinaryOperator::Opcode Op,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 45830101bd2d7..653e91cbc0d39 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3020,7 +3020,7 @@ void ExprEngine::processSwitch(const SwitchStmt *Switch, 
ExplodedNode *Pred,
 
 std::optional<std::pair<SVal, QualType>>
 ExprEngine::resolveAsLambdaCapturedVar(const Expr *Ex, const ValueDecl *VD,
-                                       ExplodedNode *Pred) const {
+                                       const ExplodedNode *Pred) const {
   ProgramStateRef State = Pred->getState();
   const StackFrame *SF = Pred->getStackFrame();
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to