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
