Skip to content

Commit 6cfbe57

Browse files
committed
Auto merge of #11862 - christophbeberweil:7125-single-element-loop-over-range, r=llogiq
suggest alternatives to iterate an array of ranges works towards #7125 changelog: [`single_element_loop`]: suggest better syntax when iterating over an array of a single range `@thinkerdreamer` and myself worked on this issue during a workshop by `@llogiq` at the RustLab 2023 conference. It is our first contribution to clippy. When iterating over an array of only one element, _which is a range_, our change suggests to replace the array with the contained range itself. Additionally, a hint is printed stating that the user probably intended to iterate over the range and not the array. If the single element in the array is not a range, the previous suggestion in the form of `let {pat_snip} = {prefix}{arg_snip};{block_str}`is used. This change lints the array with the single range directly, so any prefixes or suffixes are covered as well.
2 parents 3e7a63b + f9c6335 commit 6cfbe57

File tree

3 files changed

+45
-74
lines changed

3 files changed

+45
-74
lines changed

clippy_lints/src/loops/single_element_loop.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::SINGLE_ELEMENT_LOOP;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use clippy_utils::source::{indent_of, snippet_with_applicability};
3+
use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
44
use clippy_utils::visitors::contains_break_or_continue;
55
use rustc_ast::util::parser::PREC_PREFIX;
66
use rustc_ast::Mutability;
@@ -87,14 +87,29 @@ pub(super) fn check<'tcx>(
8787
arg_snip = format!("({arg_snip})").into();
8888
}
8989

90-
span_lint_and_sugg(
91-
cx,
92-
SINGLE_ELEMENT_LOOP,
93-
expr.span,
94-
"for loop over a single element",
95-
"try",
96-
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
97-
applicability,
98-
);
90+
if clippy_utils::higher::Range::hir(arg_expression).is_some() {
91+
let range_expr = snippet(cx, arg_expression.span, "?").to_string();
92+
93+
let sugg = snippet(cx, arg_expression.span, "..");
94+
span_lint_and_sugg(
95+
cx,
96+
SINGLE_ELEMENT_LOOP,
97+
arg.span,
98+
format!("this loops only once with `{pat_snip}` being `{range_expr}`").as_str(),
99+
"did you mean to iterate over the range instead?",
100+
sugg.to_string(),
101+
Applicability::Unspecified,
102+
);
103+
} else {
104+
span_lint_and_sugg(
105+
cx,
106+
SINGLE_ELEMENT_LOOP,
107+
expr.span,
108+
"for loop over a single element",
109+
"try",
110+
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
111+
applicability,
112+
);
113+
}
99114
}
100115
}

tests/ui/single_element_loop.fixed

+4-8
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,19 @@ fn main() {
1515
dbg!(item);
1616
}
1717

18-
{
19-
let item = &(0..5);
18+
for item in 0..5 {
2019
dbg!(item);
2120
}
2221

23-
{
24-
let item = &mut (0..5);
22+
for item in 0..5 {
2523
dbg!(item);
2624
}
2725

28-
{
29-
let item = 0..5;
26+
for item in 0..5 {
3027
dbg!(item);
3128
}
3229

33-
{
34-
let item = 0..5;
30+
for item in 0..5 {
3531
dbg!(item);
3632
}
3733

tests/ui/single_element_loop.stderr

+16-56
Original file line numberDiff line numberDiff line change
@@ -32,69 +32,29 @@ LL + dbg!(item);
3232
LL + }
3333
|
3434

35-
error: for loop over a single element
36-
--> $DIR/single_element_loop.rs:16:5
37-
|
38-
LL | / for item in &[0..5] {
39-
LL | | dbg!(item);
40-
LL | | }
41-
| |_____^
42-
|
43-
help: try
44-
|
45-
LL ~ {
46-
LL + let item = &(0..5);
47-
LL + dbg!(item);
48-
LL + }
35+
error: this loops only once with `item` being `0..5`
36+
--> $DIR/single_element_loop.rs:16:17
4937
|
38+
LL | for item in &[0..5] {
39+
| ^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`
5040

51-
error: for loop over a single element
52-
--> $DIR/single_element_loop.rs:20:5
53-
|
54-
LL | / for item in [0..5].iter_mut() {
55-
LL | | dbg!(item);
56-
LL | | }
57-
| |_____^
58-
|
59-
help: try
60-
|
61-
LL ~ {
62-
LL + let item = &mut (0..5);
63-
LL + dbg!(item);
64-
LL + }
41+
error: this loops only once with `item` being `0..5`
42+
--> $DIR/single_element_loop.rs:20:17
6543
|
44+
LL | for item in [0..5].iter_mut() {
45+
| ^^^^^^^^^^^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`
6646

67-
error: for loop over a single element
68-
--> $DIR/single_element_loop.rs:24:5
69-
|
70-
LL | / for item in [0..5] {
71-
LL | | dbg!(item);
72-
LL | | }
73-
| |_____^
74-
|
75-
help: try
76-
|
77-
LL ~ {
78-
LL + let item = 0..5;
79-
LL + dbg!(item);
80-
LL + }
47+
error: this loops only once with `item` being `0..5`
48+
--> $DIR/single_element_loop.rs:24:17
8149
|
50+
LL | for item in [0..5] {
51+
| ^^^^^^ help: did you mean to iterate over the range instead?: `0..5`
8252

83-
error: for loop over a single element
84-
--> $DIR/single_element_loop.rs:28:5
85-
|
86-
LL | / for item in [0..5].into_iter() {
87-
LL | | dbg!(item);
88-
LL | | }
89-
| |_____^
90-
|
91-
help: try
92-
|
93-
LL ~ {
94-
LL + let item = 0..5;
95-
LL + dbg!(item);
96-
LL + }
53+
error: this loops only once with `item` being `0..5`
54+
--> $DIR/single_element_loop.rs:28:17
9755
|
56+
LL | for item in [0..5].into_iter() {
57+
| ^^^^^^^^^^^^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`
9858

9959
error: for loop over a single element
10060
--> $DIR/single_element_loop.rs:47:5

0 commit comments

Comments
 (0)