Skip to content

Commit f6b2992

Browse files
committed
Auto merge of #8616 - pitaj:single_element_loop_arrays, r=llogiq
single_element_loop: handle arrays for Edition2021 changelog: [`single_element_loop`] handle arrays in Edition 2021, handle `.iter_mut()` and `.into_iter()`, and wrap in parens if necessary
2 parents db5739a + 3bbb3e3 commit f6b2992

File tree

4 files changed

+172
-16
lines changed

4 files changed

+172
-16
lines changed

clippy_lints/src/loops/single_element_loop.rs

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::{indent_of, snippet_with_applicability};
44
use if_chain::if_chain;
5+
use rustc_ast::util::parser::PREC_PREFIX;
6+
use rustc_ast::Mutability;
57
use rustc_errors::Applicability;
6-
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
8+
use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
79
use rustc_lint::LateContext;
10+
use rustc_span::edition::Edition;
811

912
pub(super) fn check<'tcx>(
1013
cx: &LateContext<'tcx>,
@@ -13,31 +16,84 @@ pub(super) fn check<'tcx>(
1316
body: &'tcx Expr<'_>,
1417
expr: &'tcx Expr<'_>,
1518
) {
16-
let arg_expr = match arg.kind {
17-
ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
18-
ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
19+
let (arg_expression, prefix) = match arg.kind {
20+
ExprKind::AddrOf(
21+
BorrowKind::Ref,
22+
Mutability::Not,
23+
Expr {
24+
kind: ExprKind::Array([arg]),
25+
..
26+
},
27+
) => (arg, "&"),
28+
ExprKind::AddrOf(
29+
BorrowKind::Ref,
30+
Mutability::Mut,
31+
Expr {
32+
kind: ExprKind::Array([arg]),
33+
..
34+
},
35+
) => (arg, "&mut "),
36+
ExprKind::MethodCall(
37+
method,
38+
[
39+
Expr {
40+
kind: ExprKind::Array([arg]),
41+
..
42+
},
43+
],
44+
_,
45+
) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
46+
ExprKind::MethodCall(
47+
method,
48+
[
49+
Expr {
50+
kind: ExprKind::Array([arg]),
51+
..
52+
},
53+
],
54+
_,
55+
) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
56+
ExprKind::MethodCall(
57+
method,
58+
[
59+
Expr {
60+
kind: ExprKind::Array([arg]),
61+
..
62+
},
63+
],
64+
_,
65+
) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
66+
// Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
67+
ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
1968
_ => return,
2069
};
2170
if_chain! {
22-
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
2371
if let ExprKind::Block(block, _) = body.kind;
2472
if !block.stmts.is_empty();
2573
then {
2674
let mut applicability = Applicability::MachineApplicable;
2775
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
28-
let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
76+
let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
2977
let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
3078
block_str.remove(0);
3179
block_str.pop();
3280
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
3381

82+
// Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
83+
if !prefix.is_empty() && (
84+
// Precedence of internal expression is less than or equal to precedence of `&expr`.
85+
arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
86+
) {
87+
arg_snip = format!("({arg_snip})").into();
88+
}
89+
3490
span_lint_and_sugg(
3591
cx,
3692
SINGLE_ELEMENT_LOOP,
3793
expr.span,
3894
"for loop over a single element",
3995
"try",
40-
format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
96+
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
4197
applicability,
4298
)
4399
}

tests/ui/single_element_loop.fixed

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,31 @@ fn main() {
66
let item1 = 2;
77
{
88
let item = &item1;
9-
println!("{}", item);
9+
dbg!(item);
1010
}
1111

1212
{
1313
let item = &item1;
14-
println!("{:?}", item);
14+
dbg!(item);
15+
}
16+
17+
{
18+
let item = &(0..5);
19+
dbg!(item);
20+
}
21+
22+
{
23+
let item = &mut (0..5);
24+
dbg!(item);
25+
}
26+
27+
{
28+
let item = 0..5;
29+
dbg!(item);
30+
}
31+
32+
{
33+
let item = 0..5;
34+
dbg!(item);
1535
}
1636
}

tests/ui/single_element_loop.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,26 @@
55
fn main() {
66
let item1 = 2;
77
for item in &[item1] {
8-
println!("{}", item);
8+
dbg!(item);
99
}
1010

1111
for item in [item1].iter() {
12-
println!("{:?}", item);
12+
dbg!(item);
13+
}
14+
15+
for item in &[0..5] {
16+
dbg!(item);
17+
}
18+
19+
for item in [0..5].iter_mut() {
20+
dbg!(item);
21+
}
22+
23+
for item in [0..5] {
24+
dbg!(item);
25+
}
26+
27+
for item in [0..5].into_iter() {
28+
dbg!(item);
1329
}
1430
}

tests/ui/single_element_loop.stderr

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: for loop over a single element
22
--> $DIR/single_element_loop.rs:7:5
33
|
44
LL | / for item in &[item1] {
5-
LL | | println!("{}", item);
5+
LL | | dbg!(item);
66
LL | | }
77
| |_____^
88
|
@@ -11,25 +11,89 @@ help: try
1111
|
1212
LL ~ {
1313
LL + let item = &item1;
14-
LL + println!("{}", item);
14+
LL + dbg!(item);
1515
LL + }
1616
|
1717

1818
error: for loop over a single element
1919
--> $DIR/single_element_loop.rs:11:5
2020
|
2121
LL | / for item in [item1].iter() {
22-
LL | | println!("{:?}", item);
22+
LL | | dbg!(item);
2323
LL | | }
2424
| |_____^
2525
|
2626
help: try
2727
|
2828
LL ~ {
2929
LL + let item = &item1;
30-
LL + println!("{:?}", item);
30+
LL + dbg!(item);
3131
LL + }
3232
|
3333

34-
error: aborting due to 2 previous errors
34+
error: for loop over a single element
35+
--> $DIR/single_element_loop.rs:15:5
36+
|
37+
LL | / for item in &[0..5] {
38+
LL | | dbg!(item);
39+
LL | | }
40+
| |_____^
41+
|
42+
help: try
43+
|
44+
LL ~ {
45+
LL + let item = &(0..5);
46+
LL + dbg!(item);
47+
LL + }
48+
|
49+
50+
error: for loop over a single element
51+
--> $DIR/single_element_loop.rs:19:5
52+
|
53+
LL | / for item in [0..5].iter_mut() {
54+
LL | | dbg!(item);
55+
LL | | }
56+
| |_____^
57+
|
58+
help: try
59+
|
60+
LL ~ {
61+
LL + let item = &mut (0..5);
62+
LL + dbg!(item);
63+
LL + }
64+
|
65+
66+
error: for loop over a single element
67+
--> $DIR/single_element_loop.rs:23:5
68+
|
69+
LL | / for item in [0..5] {
70+
LL | | dbg!(item);
71+
LL | | }
72+
| |_____^
73+
|
74+
help: try
75+
|
76+
LL ~ {
77+
LL + let item = 0..5;
78+
LL + dbg!(item);
79+
LL + }
80+
|
81+
82+
error: for loop over a single element
83+
--> $DIR/single_element_loop.rs:27:5
84+
|
85+
LL | / for item in [0..5].into_iter() {
86+
LL | | dbg!(item);
87+
LL | | }
88+
| |_____^
89+
|
90+
help: try
91+
|
92+
LL ~ {
93+
LL + let item = 0..5;
94+
LL + dbg!(item);
95+
LL + }
96+
|
97+
98+
error: aborting due to 6 previous errors
3599

0 commit comments

Comments
 (0)