https://github.com/lbonn created https://github.com/llvm/llvm-project/pull/173186
Analyzer used to crash when visiting the unsubstituted DeclRefExpr nodes here. Instead we skip them and process them in a dedicated pack indexing transfer function. Fixes #154042 >From d230021a70640842c792b41c14232d2805f72e24 Mon Sep 17 00:00:00 2001 From: Laurent Bonnans <[email protected]> Date: Fri, 19 Dec 2025 14:54:21 +0100 Subject: [PATCH] [analyzer] Support pack indexing expressions Analyzer used to crash when visiting the unsubstituted DeclRefExpr nodes here. Instead we skip them and process them in a dedicated pack indexing transfer function. --- .../Core/PathSensitive/ExprEngine.h | 4 +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 32 ++++++++++++++++++- clang/test/Analysis/pack_indexing.cpp | 25 +++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 clang/test/Analysis/pack_indexing.cpp diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index d184986cda15d..2d96d668d9f7e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -506,6 +506,10 @@ class ExprEngine { void VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst); + /// VisitPackIndexingExpr - Transfer function logic for C++26 pack indexing + void VisitPackIndexingExpr(const PackIndexingExpr *E, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index c8dc5b6e81b16..48e026faaa49f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1740,7 +1740,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::RecoveryExprClass: case Stmt::CXXNoexceptExprClass: case Stmt::PackExpansionExprClass: - case Stmt::PackIndexingExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: case Stmt::CoroutineBodyStmtClass: @@ -2292,6 +2291,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::PackIndexingExprClass: { + Bldr.takeNodes(Pred); + VisitPackIndexingExpr(cast<PackIndexingExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: @@ -3296,6 +3302,14 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, SVal V = UnknownVal(); + // For pack indexing expressions. Binding is not available here but through + // the expanded expressions in the PackIndexingExpr node. + if (BD->isParameterPack()) { + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + return; + } + // Handle binding to data members if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) { const auto *Field = cast<FieldDecl>(ME->getMemberDecl()); @@ -3447,6 +3461,22 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } +void ExprEngine::VisitPackIndexingExpr(const PackIndexingExpr *E, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + if (!E->isFullySubstituted()) { + llvm_unreachable("Unsubstituted pack indexing expression"); + } + + if (const auto *DRE = dyn_cast<DeclRefExpr>(E->getSelectedExpr())) { + VisitCommonDeclRefExpr(E, DRE->getDecl(), Pred, Dst); + return; + } + + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateSink(E, Pred, Pred->getState()); +} + /// VisitArraySubscriptExpr - Transfer function for array accesses void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, ExplodedNode *Pred, diff --git a/clang/test/Analysis/pack_indexing.cpp b/clang/test/Analysis/pack_indexing.cpp new file mode 100644 index 0000000000000..0c3f1280c1529 --- /dev/null +++ b/clang/test/Analysis/pack_indexing.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -std=c++26 -verify %s + +void clang_analyzer_eval(bool); + +template <class T> +constexpr decltype(auto) get0(const T& val) noexcept { + auto& [...members] = val; + auto&& r = members...[0]; // no-crash + return r; +} + +struct A { + int a; +}; + +void no_crash_negative() { + const int& x = get0(A{1}); + clang_analyzer_eval(x == 1); +} + +void uninitialized() { + A a; + const int& x = get0(a); + clang_analyzer_eval(x == 0); // expected-warning{{The left operand of '==' is a garbage value}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
