https://github.com/lbonn updated https://github.com/llvm/llvm-project/pull/173186
>From c7a8ebeab0e2abbf6beca457247ed0080860afef 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 | 41 ++++++++++++++++++- clang/test/Analysis/pack_indexing.cpp | 36 ++++++++++++++++ 3 files changed, 80 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..573580fedaa25 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,13 @@ 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()) { + // FIXME: We should meaningfully implement this. + return; + } + // Handle binding to data members if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) { const auto *Field = cast<FieldDecl>(ME->getMemberDecl()); @@ -3346,6 +3359,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, return; } + if (const auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(D)) { + // FIXME: We should meaningfully implement this. + (void)NTTPD; + return; + } + llvm_unreachable("Support for this Decl not implemented."); } @@ -3447,6 +3466,26 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } +void ExprEngine::VisitPackIndexingExpr(const PackIndexingExpr *E, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert(E->isFullySubstituted() && "unsubstituted pack indexing expression"); + + if (const auto *DE = dyn_cast<DeclRefExpr>(E->getSelectedExpr())) { + VisitCommonDeclRefExpr(E, DE->getDecl(), Pred, Dst); + } else if (const auto *SNTTPE = + dyn_cast<SubstNonTypeTemplateParmExpr>(E->getSelectedExpr())) { + (void)SNTTPE; + // FIXME: handle this case + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + const ExplodedNode *node = Bldr.generateSink(E, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + } else { + llvm_unreachable( + "Unexpected selected expression in pack indexing expression"); + } +} + /// 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..6217248f48771 --- /dev/null +++ b/clang/test/Analysis/pack_indexing.cpp @@ -0,0 +1,36 @@ +// 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}} +} + +template <int I, auto...Ts> +int index_template_pack() +{ + return Ts...[I]; // no-crash +} + +void template_pack_no_crash() +{ + int r = index_template_pack<2, 0, 1, 42>(); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
