https://github.com/aviralg created 
https://github.com/llvm/llvm-project/pull/147410

Tuple structured bindings can introduce new variable declarations under 
`BindingDecl` nodes which are currently ignored by the infinite loop checker. 
This PR implements support for extracting variables from these nodes. 
Subsequent logic for checking if these variables have changed has been reused.

>From 782095442544692d3f0e32ed5da4ee1d772c1f9a Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoe...@apple.com>
Date: Mon, 7 Jul 2025 14:38:00 -0700
Subject: [PATCH] [clang-tidy] bugprone-infinite-loop: Add support for tuple
 structured bindings

Tuple structured bindings can introduce new variable declarations under
`BindingDecl` nodes which are currently ignored by the infinite loop checker.
This PR implements support for extracting variables from these nodes.
Subsequent logic for checking if these variables have changed has been reused.
---
 .../clang-tidy/bugprone/InfiniteLoopCheck.cpp | 35 +++++++++++++------
 .../checkers/bugprone/infinite-loop.cpp       | 15 ++++++++
 2 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
index 07116a7ff15f5..2ae0dbc3f53ba 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -65,24 +65,37 @@ static bool isChanged(const Stmt *LoopStmt, const VarDecl 
*Var,
   return ExprMutationAnalyzer(*LoopStmt, *Context).isMutated(Var);
 }
 
+static bool isVarDeclPossiblyChanged(const Decl *Func, const Stmt *LoopStmt,
+                                     const VarDecl *Var, ASTContext *Context) {
+  if (!Var->isLocalVarDeclOrParm())
+    return true;
+
+  if (Var->getType().isVolatileQualified())
+    return true;
+
+  if (!Var->getType().getTypePtr()->isIntegerType())
+    return true;
+
+  return hasPtrOrReferenceInFunc(Func, Var) ||
+         isChanged(LoopStmt, Var, Context);
+}
+
 /// Return whether `Cond` is a variable that is possibly changed in `LoopStmt`.
 static bool isVarThatIsPossiblyChanged(const Decl *Func, const Stmt *LoopStmt,
                                        const Stmt *Cond, ASTContext *Context) {
   if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
-    if (const auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
-      if (!Var->isLocalVarDeclOrParm())
-        return true;
-
-      if (Var->getType().isVolatileQualified())
-        return true;
+    const ValueDecl *VD = DRE->getDecl();
 
-      if (!Var->getType().getTypePtr()->isIntegerType())
-        return true;
+    if (const auto *Var = dyn_cast<VarDecl>(VD)) {
+      return isVarDeclPossiblyChanged(Func, LoopStmt, Var, Context);
+    }
 
-      return hasPtrOrReferenceInFunc(Func, Var) ||
-             isChanged(LoopStmt, Var, Context);
-      // FIXME: Track references.
+    if (const auto *BD = dyn_cast<BindingDecl>(VD)) {
+      return isVarDeclPossiblyChanged(Func, LoopStmt, BD->getHoldingVar(),
+                                      Context);
     }
+
+    // FIXME: Track references.
   } else if (isa<MemberExpr, CallExpr, ObjCIvarRefExpr, ObjCPropertyRefExpr,
                  ObjCMessageExpr>(Cond)) {
     // FIXME: Handle MemberExpr.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
index bc14ece3f332c..e5d3a7da0d763 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/infinite-loop.cpp
@@ -592,6 +592,21 @@ void test_structured_bindings_bad() {
   }
 }
 
+std::tuple<uint8_t *, size_t> get_chunk() {
+  static uint8_t buf[10];
+  return { &buf[0], 2 };
+}
+
+void test_structured_bindings_tuple() {
+  auto [ p, size ] = get_chunk();
+  size_t maxLen = 8;
+
+  while (size < maxLen) {
+    // No warning. The loop is finite because 'size' is being incremented in 
each iteration and compared against 'maxLen' for termination
+    p[size++] = ' ';
+  }
+}
+
 void test_volatile_cast() {
   // This is a no-op cast. Clang ignores the qualifier, we should too.
   for (int i = 0; (volatile int)i < 10;) {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to