Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -2893,6 +2893,17 @@ return Block; } + // Visit the binding initializers before the DS initializer, so they + // appear after it in the CFG. The binding initializer will reference the + // new object, created after the the decomposition, so it has to appear + // later in the CFG. + if (const auto *DD = dyn_cast(VD)) { + for (auto BD : llvm::reverse(DD->bindings())) { + if (auto *VD = BD->getHoldingVar()) + Visit(VD->getInit()); + } + } + bool HasTemporaries = false; // Guard static initializers under a branch. Index: clang/lib/Analysis/LiveVariables.cpp =================================================================== --- clang/lib/Analysis/LiveVariables.cpp +++ clang/lib/Analysis/LiveVariables.cpp @@ -72,6 +72,8 @@ bool alive = false; for (const BindingDecl *BD : DD->bindings()) alive |= liveBindings.contains(BD); + + alive |= liveDecls.contains(DD); return alive; } return liveDecls.contains(D); @@ -371,8 +373,12 @@ const Decl* D = DR->getDecl(); bool InAssignment = LV.inAssignment[DR]; if (const auto *BD = dyn_cast(D)) { - if (!InAssignment) + if (!InAssignment) { + if (const auto *HV = BD->getHoldingVar()) + val.liveExprs = LV.ESetFact.add(val.liveExprs, HV->getInit()); + val.liveBindings = LV.BSetFact.add(val.liveBindings, BD); + } } else if (const auto *VD = dyn_cast(D)) { if (!InAssignment && !isAlwaysAlive(VD)) val.liveDecls = LV.DSetFact.add(val.liveDecls, VD); @@ -382,8 +388,14 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { for (const auto *DI : DS->decls()) { if (const auto *DD = dyn_cast(DI)) { - for (const auto *BD : DD->bindings()) + for (const auto *BD : DD->bindings()) { + if (const auto *HV = BD->getHoldingVar()) + val.liveExprs = LV.ESetFact.remove(val.liveExprs, HV->getInit()); + val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD); + } + + val.liveDecls = LV.DSetFact.remove(val.liveDecls, DD); } else if (const auto *VD = dyn_cast(DI)) { if (!isAlwaysAlive(VD)) val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2622,9 +2622,8 @@ V = state->getLValue(BD->getType(), Idx, Base); } // Handle binding to tuple-like strcutures - else if (BD->getHoldingVar()) { - // FIXME: handle tuples - return; + else if (const auto *HV = BD->getHoldingVar()) { + V = state->getSVal(HV->getInit(), LCtx); } else llvm_unreachable("An unknown case of structured binding encountered!"); Index: clang/test/Analysis/live-bindings-test.cpp =================================================================== --- clang/test/Analysis/live-bindings-test.cpp +++ clang/test/Analysis/live-bindings-test.cpp @@ -115,7 +115,10 @@ Mytuple getMytuple(); void deconstruct_tuple_types_warning() { - auto [a, b] = getMytuple(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}} + + // Here the unused warning is not reported, because the call to get<> + // references the decomposed structure and reads a value from it. + auto [a, b] = getMytuple(); } int deconstruct_tuple_types_no_warning() { Index: clang/test/Analysis/uninit-structured-binding-tuple.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/uninit-structured-binding-tuple.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +void clang_analyzer_eval(bool); + +void a(void) { + std::pair p = {1, 2}; + + auto [u, v] = p; + + int x = u; + int y = v; + + clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(y == 2); // expected-warning{{TRUE}} +}