about summary refs log tree commit diff
diff options
context:
space:
mode:
authorllogiq <bogusandre@gmail.com>2025-07-19 12:34:07 +0000
committerGitHub <noreply@github.com>2025-07-19 12:34:07 +0000
commit17968e7585756c5110ee9944984087034fc46a03 (patch)
tree95a8c3c36e2db238f39111a655ef5c552eac9aac
parentf85cdbbd6fd9d2baa85b69ab8e9f198c3f3eedbf (diff)
parent6cf00e94901057e6b4c98b50ec9866afbb7b6cd4 (diff)
downloadrust-17968e7585756c5110ee9944984087034fc46a03.tar.gz
rust-17968e7585756c5110ee9944984087034fc46a03.zip
Fix `needless_range_loop` FP on array literals (#15314)
Closes rust-lang/rust-clippy#15309

changelog: [`needless_range_loop`] fix FP on array literals
-rw-r--r--clippy_lints/src/loops/needless_range_loop.rs22
-rw-r--r--tests/ui/needless_range_loop.rs25
2 files changed, 45 insertions, 2 deletions
diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs
index 7837b18bcd3..972b0b110e0 100644
--- a/clippy_lints/src/loops/needless_range_loop.rs
+++ b/clippy_lints/src/loops/needless_range_loop.rs
@@ -39,7 +39,9 @@ pub(super) fn check<'tcx>(
             var: canonical_id,
             indexed_mut: FxHashSet::default(),
             indexed_indirectly: FxHashMap::default(),
+            unnamed_indexed_indirectly: false,
             indexed_directly: FxIndexMap::default(),
+            unnamed_indexed_directly: false,
             referenced: FxHashSet::default(),
             nonindex: false,
             prefer_mutable: false,
@@ -47,7 +49,11 @@ pub(super) fn check<'tcx>(
         walk_expr(&mut visitor, body);
 
         // linting condition: we only indexed one variable, and indexed it directly
-        if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
+        if visitor.indexed_indirectly.is_empty()
+            && !visitor.unnamed_indexed_indirectly
+            && !visitor.unnamed_indexed_directly
+            && visitor.indexed_directly.len() == 1
+        {
             let (indexed, (indexed_extent, indexed_ty)) = visitor
                 .indexed_directly
                 .into_iter()
@@ -217,6 +223,7 @@ fn is_end_eq_array_len<'tcx>(
     false
 }
 
+#[expect(clippy::struct_excessive_bools)]
 struct VarVisitor<'a, 'tcx> {
     /// context reference
     cx: &'a LateContext<'tcx>,
@@ -226,9 +233,13 @@ struct VarVisitor<'a, 'tcx> {
     indexed_mut: FxHashSet<Symbol>,
     /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
     indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
+    /// indirectly indexed literals, like `[1, 2, 3][(i + 4) % N]`
+    unnamed_indexed_indirectly: bool,
     /// subset of `indexed` of vars that are indexed directly: `v[i]`
     /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
     indexed_directly: FxIndexMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
+    /// directly indexed literals, like `[1, 2, 3][i]`
+    unnamed_indexed_directly: bool,
     /// Any names that are used outside an index operation.
     /// Used to detect things like `&mut vec` used together with `vec[i]`
     referenced: FxHashSet<Symbol>,
@@ -242,6 +253,7 @@ struct VarVisitor<'a, 'tcx> {
 
 impl<'tcx> VarVisitor<'_, 'tcx> {
     fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
+        let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
         if let ExprKind::Path(ref seqpath) = seqexpr.kind
             // the indexed container is referenced by a name
             && let QPath::Resolved(None, seqvar) = *seqpath
@@ -251,7 +263,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
             if self.prefer_mutable {
                 self.indexed_mut.insert(seqvar.segments[0].ident.name);
             }
-            let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
             let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
             match res {
                 Res::Local(hir_id) => {
@@ -286,6 +297,13 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
                 },
                 _ => (),
             }
+        } else if let ExprKind::Repeat(..) | ExprKind::Array(..) = seqexpr.kind {
+            if index_used_directly {
+                self.unnamed_indexed_directly = true;
+            } else {
+                self.unnamed_indexed_indirectly = true;
+            }
+            return false;
         }
         true
     }
diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs
index 8a1c1be289c..70cf9fa7369 100644
--- a/tests/ui/needless_range_loop.rs
+++ b/tests/ui/needless_range_loop.rs
@@ -185,3 +185,28 @@ mod issue_2496 {
         unimplemented!()
     }
 }
+
+fn needless_loop() {
+    use std::hint::black_box;
+    let x = [0; 64];
+    for i in 0..64 {
+        let y = [0; 64];
+
+        black_box(x[i]);
+        black_box(y[i]);
+    }
+
+    for i in 0..64 {
+        black_box(x[i]);
+        black_box([0; 64][i]);
+    }
+
+    for i in 0..64 {
+        black_box(x[i]);
+        black_box([1, 2, 3, 4, 5, 6, 7, 8][i]);
+    }
+
+    for i in 0..64 {
+        black_box([1, 2, 3, 4, 5, 6, 7, 8][i]);
+    }
+}