Index: lib/Analysis/LiveVariables.cpp =================================================================== --- lib/Analysis/LiveVariables.cpp +++ lib/Analysis/LiveVariables.cpp @@ -81,6 +81,11 @@ llvm::DenseMap blocksBeginToLiveness; llvm::DenseMap stmtsToLiveness; llvm::DenseMap inAssignment; + + // Binding decl does not have a parent pointer to the decomposition decl. + // We are adding those in the pre-processing step. + llvm::DenseMap + bindingParentMap; const bool killAtAssign; LiveVariables::LivenessValues @@ -329,9 +334,16 @@ // Assigning to a variable? Expr *LHS = B->getLHS()->IgnoreParens(); - - if (DeclRefExpr *DR = dyn_cast(LHS)) - if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + + if (DeclRefExpr *DR = dyn_cast(LHS)) { + const Decl* D = DR->getDecl(); + const VarDecl *VD = dyn_cast(D); + + if (const BindingDecl* BD = dyn_cast(D)) { + VD = LV.bindingParentMap[BD]; + } + + if (VD) { // Assignments to references don't kill the ref's address if (VD->getType()->isReferenceType()) return; @@ -344,6 +356,7 @@ if (observer) observer->observerKill(DR); } + } } } @@ -357,17 +370,30 @@ } void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) { - if (const VarDecl *D = dyn_cast(DR->getDecl())) - if (!isAlwaysAlive(D) && LV.inAssignment.find(DR) == LV.inAssignment.end()) - val.liveDecls = LV.DSetFact.add(val.liveDecls, D); + const Decl* D = DR->getDecl(); + const auto *VD = dyn_cast(D); + if (const auto *BD = dyn_cast(D)) { + VD = LV.bindingParentMap[BD]; + } + + if (VD) { + if (!isAlwaysAlive(VD) && LV.inAssignment.find(DR) == LV.inAssignment.end()) + val.liveDecls = LV.DSetFact.add(val.liveDecls, VD); + } } void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { - for (const auto *DI : DS->decls()) - if (const auto *VD = dyn_cast(DI)) { - if (!isAlwaysAlive(VD)) - val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); + for (const auto *DI : DS->decls()) { + const auto *VD = dyn_cast(DI); + if (const auto *DD = dyn_cast(DI)) { + if (DD->bindings().size() >= 1) { + VD = LV.bindingParentMap[DD->bindings()[0]]; + } } + + if (VD && !isAlwaysAlive(VD)) + val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); + } } void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS) { @@ -422,12 +448,14 @@ case UO_PreDec: break; } - - if (DeclRefExpr *DR = dyn_cast(UO->getSubExpr()->IgnoreParens())) - if (isa(DR->getDecl())) { + + if (auto *DR = dyn_cast(UO->getSubExpr()->IgnoreParens())) { + const Decl *D = DR->getDecl(); + if (isa(D) || isa(D)) { // Treat ++/-- as a kill. observer->observerKill(DR); } + } } LiveVariables::LivenessValues @@ -508,14 +536,27 @@ for (CFGBlock::const_iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { if (Optional cs = bi->getAs()) { - if (const BinaryOperator *BO = - dyn_cast(cs->getStmt())) { + const Stmt* stmt = cs->getStmt(); + if (const auto *BO = dyn_cast(stmt)) { if (BO->getOpcode() == BO_Assign) { - if (const DeclRefExpr *DR = + if (const auto *DR = dyn_cast(BO->getLHS()->IgnoreParens())) { LV->inAssignment[DR] = 1; } } + } else if (const auto *DS = dyn_cast(stmt)) { + + // Pre-construct parent pointers for binding declarations. + // Note that all usages of `bindingParentMap` results + // should ideally be checked for nullability, + // in case preconstruction misses cases. + for (const auto *DI : DS->decls()) { + if (const auto *DD = dyn_cast(DI)) { + for (BindingDecl *BD : DD->bindings()) { + LV->bindingParentMap[BD] = DD; + } + } + } } } } Index: test/Analysis/live-bindings-test.cpp =================================================================== --- /dev/null +++ test/Analysis/live-bindings-test.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core,deadcode -verify %s + +struct S { + int a; + double b; +}; + +S GetNumbers(); + +int used_binding() { + const auto [a, b] = GetNumbers(); + return a + b; // no-warning +} + + +int unused_binding_ignored() { + const auto [a, b] = GetNumbers(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}} + return 0; +} + +int unused_binding_liveness_required() { + auto [a, b] = GetNumbers(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}} + a = 10; + b = 20; + return a + b; +}