about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Jaszkowiak <p.jaszkow@gmail.com>2022-04-01 00:04:19 -0600
committerPeter Jaszkowiak <p.jaszkow@gmail.com>2022-04-01 00:04:19 -0600
commit3bbb3e33295f4de11efb904192df1a3764d89e8d (patch)
tree28c8f031215f56b6094388150220bb058e1db77d
parent8ebe766695e66a97775e4992d3d08f74ce2a7270 (diff)
downloadrust-3bbb3e33295f4de11efb904192df1a3764d89e8d.tar.gz
rust-3bbb3e33295f4de11efb904192df1a3764d89e8d.zip
single_element_loop: handle arrays for Edition2021
also handle `.iter_mut()`, `.into_iter()`,
and wrapping in parens if necessary
-rw-r--r--clippy_lints/src/loops/single_element_loop.rs70
-rw-r--r--tests/ui/single_element_loop.fixed24
-rw-r--r--tests/ui/single_element_loop.rs20
-rw-r--r--tests/ui/single_element_loop.stderr74
4 files changed, 172 insertions, 16 deletions
diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs
index 36ecd83f7d6..a0bd7ad0ac6 100644
--- a/clippy_lints/src/loops/single_element_loop.rs
+++ b/clippy_lints/src/loops/single_element_loop.rs
@@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, snippet_with_applicability};
 use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::Mutability;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
+use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
 use rustc_lint::LateContext;
+use rustc_span::edition::Edition;
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -13,31 +16,84 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
 ) {
-    let arg_expr = match arg.kind {
-        ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
-        ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
+    let (arg_expression, prefix) = match arg.kind {
+        ExprKind::AddrOf(
+            BorrowKind::Ref,
+            Mutability::Not,
+            Expr {
+                kind: ExprKind::Array([arg]),
+                ..
+            },
+        ) => (arg, "&"),
+        ExprKind::AddrOf(
+            BorrowKind::Ref,
+            Mutability::Mut,
+            Expr {
+                kind: ExprKind::Array([arg]),
+                ..
+            },
+        ) => (arg, "&mut "),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
+        // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
+        ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
         _ => return,
     };
     if_chain! {
-        if let ExprKind::Array([arg_expression]) = arg_expr.kind;
         if let ExprKind::Block(block, _) = body.kind;
         if !block.stmts.is_empty();
         then {
             let mut applicability = Applicability::MachineApplicable;
             let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
-            let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
+            let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
             let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
             block_str.remove(0);
             block_str.pop();
             let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
 
+            // Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
+            if !prefix.is_empty() && (
+                // Precedence of internal expression is less than or equal to precedence of `&expr`.
+                arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
+            ) {
+                arg_snip = format!("({arg_snip})").into();
+            }
+
             span_lint_and_sugg(
                 cx,
                 SINGLE_ELEMENT_LOOP,
                 expr.span,
                 "for loop over a single element",
                 "try",
-                format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
+                format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
                 applicability,
             )
         }
diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed
index c307afffcb8..63d31ff83f9 100644
--- a/tests/ui/single_element_loop.fixed
+++ b/tests/ui/single_element_loop.fixed
@@ -6,11 +6,31 @@ fn main() {
     let item1 = 2;
     {
         let item = &item1;
-        println!("{}", item);
+        dbg!(item);
     }
 
     {
         let item = &item1;
-        println!("{:?}", item);
+        dbg!(item);
+    }
+
+    {
+        let item = &(0..5);
+        dbg!(item);
+    }
+
+    {
+        let item = &mut (0..5);
+        dbg!(item);
+    }
+
+    {
+        let item = 0..5;
+        dbg!(item);
+    }
+
+    {
+        let item = 0..5;
+        dbg!(item);
     }
 }
diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs
index 2c0c03b7211..2cda5a329d2 100644
--- a/tests/ui/single_element_loop.rs
+++ b/tests/ui/single_element_loop.rs
@@ -5,10 +5,26 @@
 fn main() {
     let item1 = 2;
     for item in &[item1] {
-        println!("{}", item);
+        dbg!(item);
     }
 
     for item in [item1].iter() {
-        println!("{:?}", item);
+        dbg!(item);
+    }
+
+    for item in &[0..5] {
+        dbg!(item);
+    }
+
+    for item in [0..5].iter_mut() {
+        dbg!(item);
+    }
+
+    for item in [0..5] {
+        dbg!(item);
+    }
+
+    for item in [0..5].into_iter() {
+        dbg!(item);
     }
 }
diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr
index f52ca8c5a9b..0aeb8da1a2e 100644
--- a/tests/ui/single_element_loop.stderr
+++ b/tests/ui/single_element_loop.stderr
@@ -2,7 +2,7 @@ error: for loop over a single element
   --> $DIR/single_element_loop.rs:7:5
    |
 LL | /     for item in &[item1] {
-LL | |         println!("{}", item);
+LL | |         dbg!(item);
 LL | |     }
    | |_____^
    |
@@ -11,7 +11,7 @@ help: try
    |
 LL ~     {
 LL +         let item = &item1;
-LL +         println!("{}", item);
+LL +         dbg!(item);
 LL +     }
    |
 
@@ -19,7 +19,7 @@ error: for loop over a single element
   --> $DIR/single_element_loop.rs:11:5
    |
 LL | /     for item in [item1].iter() {
-LL | |         println!("{:?}", item);
+LL | |         dbg!(item);
 LL | |     }
    | |_____^
    |
@@ -27,9 +27,73 @@ help: try
    |
 LL ~     {
 LL +         let item = &item1;
-LL +         println!("{:?}", item);
+LL +         dbg!(item);
 LL +     }
    |
 
-error: aborting due to 2 previous errors
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:15:5
+   |
+LL | /     for item in &[0..5] {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = &(0..5);
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:19:5
+   |
+LL | /     for item in [0..5].iter_mut() {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = &mut (0..5);
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:23:5
+   |
+LL | /     for item in [0..5] {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = 0..5;
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:27:5
+   |
+LL | /     for item in [0..5].into_iter() {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = 0..5;
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: aborting due to 6 previous errors