https://github.com/lbonn updated 
https://github.com/llvm/llvm-project/pull/173186

>From 34e789ae79c8532a5504f2e151951eb108198e4a 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  | 39 ++++++++++++++++++-
 clang/test/Analysis/pack_indexing.cpp         | 36 +++++++++++++++++
 3 files changed, 78 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..6c9e46dccf2ff 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,24 @@ 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

Reply via email to