Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3129,16 +3129,10 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();

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());
const LocationContext *LocCtxt = Pred->getLocationContext();
const Decl *D = LocCtxt->getDecl();
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
auto resolveAsLambdaCapturedVar =
[&](const ValueDecl *VD) -> std::optional<std::pair<SVal, QualType>> {
const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
std::optional<std::pair<SVal, QualType>> VInfo;

if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
MD->getParent()->isLambda()) {
Expand All @@ -3151,13 +3145,23 @@ 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, LocCtxt->getStackFrame());
Loc CXXThis = svalBuilder.getCXXThis(MD, LCtx->getStackFrame());
SVal CXXThisVal = state->getSVal(CXXThis);
VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType());
return std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType());
}
}

return std::nullopt;
};

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());
const LocationContext *LocCtxt = Pred->getLocationContext();
std::optional<std::pair<SVal, QualType>> VInfo =
resolveAsLambdaCapturedVar(VD);

if (!VInfo)
VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType());

Expand Down Expand Up @@ -3195,6 +3199,23 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
if (const auto *BD = dyn_cast<BindingDecl>(D)) {
// Handle structured bindings captured by lambda.
if (std::optional<std::pair<SVal, QualType>> VInfo =
resolveAsLambdaCapturedVar(BD)) {
auto [V, T] = VInfo.value();

if (T->isReferenceType()) {
if (const MemRegion *R = V.getAsRegion())
V = state->getSVal(R);
else
V = UnknownVal();
}

Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
return;
}

const auto *DD = cast<DecompositionDecl>(BD->getDecomposedDecl());

SVal Base = state->getLValue(DD, LCtx);
Expand Down
15 changes: 15 additions & 0 deletions clang/test/Analysis/issue-91835.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %clang_analyze_cc1 -std=c++20 %s -analyzer-checker=core.NullDereference -analyzer-output=text -verify

// expected-no-diagnostics

struct S { int x; };

void f(int x) { (void)x; }

int main()
{
S s{42};
auto& [x] = s;
auto g = [x](){ f(x); }; // no warning
g();
}
127 changes: 127 additions & 0 deletions clang/test/Analysis/lambda-capture-structured-binding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// RUN: %clang_analyze_cc1 -std=c++20 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s

#include "Inputs/system-header-simulator-cxx.h"
void clang_analyzer_warnIfReached();
void clang_analyzer_eval(int);

void capture_structured_binding_to_array_byref() {
int arr[] {5};
auto& [i] = arr;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void capture_structured_binding_to_array_byvalue() {
int arr[] {5};
auto [i] = arr;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void capture_structured_binding_to_tuple_like_byref() {
std::pair<int, int> p {5, 6};
auto& [i, _] = p;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void capture_structured_binding_to_tuple_like_byvalue() {
std::pair<int, int> p {5, 6};
auto [i, _] = p;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

struct S { int x; };

void capture_structured_binding_to_data_member_byref() {
S s{5};
auto& [i] = s;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void capture_structured_binding_to_data_member_byvalue() {
S s{5};
auto [i] = s;
[i]() mutable {
if (i != 5)
clang_analyzer_warnIfReached();
++i;
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
}();
[&i] {
if (i != 5)
clang_analyzer_warnIfReached();
i++;
}();
clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}