Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suggest alternatives to iterate an array of ranges #11862

Merged
merged 5 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 23 additions & 10 deletions clippy_lints/src/loops/single_element_loop.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, snippet_with_applicability};
use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
use clippy_utils::visitors::contains_break_or_continue;
use rustc_ast::util::parser::PREC_PREFIX;
use rustc_ast::Mutability;
Expand Down Expand Up @@ -87,14 +87,27 @@ pub(super) fn check<'tcx>(
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{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
applicability,
);
if clippy_utils::higher::Range::hir(arg_expression).is_some() {
let sugg = snippet(cx, arg_expression.span, "..");
span_lint_and_sugg(
cx,
SINGLE_ELEMENT_LOOP,
arg.span,
"for loop over a single range inside an array, rather than iterating over the elements in the range directly",
Copy link
Contributor

@llogiq llogiq Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can find a shorter way to phrase this? I like the clarity though. Maybe something like "This loops only once with <loop pattern> being <range expr>", which is admittedly harder to construct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed a lot about the message of this lint, but opted for a longer and more verbose one. I adjusted it to contain your version, which is shorter, but now the text "0..5" is written twice in the lint message, which feels redundant to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it as "item" (the looping variable name) and "0..5", rather than 0..5 twice. Am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the current output of the lint:

error: this loops only once with item being 0..5
  --> $DIR/single_element_loop.rs:24:17
   |
LL |     for item in [0..5] {
   |                 ^^^^^^ help: did you mean to iterate over the range instead?: `0..5`

"0-5" is mentioned in the lint error message and in the suggestion. I thing mentioning it once should be enough, but mentioning it twice will probably not hurt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and with a lowercase "t" the error message actually passes the tests, which were failing with my commit from earlier today. :)

"did you mean to iterate over the range instead?",
sugg.to_string(),
applicability,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
applicability,
Applicability::Unspecified,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

);
} else {
span_lint_and_sugg(
cx,
SINGLE_ELEMENT_LOOP,
expr.span,
"for loop over a single element",
"try",
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
applicability,
);
}
}
}
12 changes: 4 additions & 8 deletions tests/ui/single_element_loop.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,19 @@ fn main() {
dbg!(item);
}

{
let item = &(0..5);
for item in 0..5 {
dbg!(item);
}

{
let item = &mut (0..5);
for item in 0..5 {
dbg!(item);
}

{
let item = 0..5;
for item in 0..5 {
dbg!(item);
}

{
let item = 0..5;
for item in 0..5 {
dbg!(item);
}

Expand Down
72 changes: 16 additions & 56 deletions tests/ui/single_element_loop.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -32,69 +32,29 @@ LL + dbg!(item);
LL + }
|

error: for loop over a single element
--> $DIR/single_element_loop.rs:16: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 range inside an array, rather than iterating over the elements in the range directly
--> $DIR/single_element_loop.rs:16:17
|
LL | for item in &[0..5] {
| ^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`

error: for loop over a single element
--> $DIR/single_element_loop.rs:20: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 range inside an array, rather than iterating over the elements in the range directly
--> $DIR/single_element_loop.rs:20:17
|
LL | for item in [0..5].iter_mut() {
| ^^^^^^^^^^^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`

error: for loop over a single element
--> $DIR/single_element_loop.rs:24: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 range inside an array, rather than iterating over the elements in the range directly
--> $DIR/single_element_loop.rs:24:17
|
LL | for item in [0..5] {
| ^^^^^^ help: did you mean to iterate over the range instead?: `0..5`

error: for loop over a single element
--> $DIR/single_element_loop.rs:28: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: for loop over a single range inside an array, rather than iterating over the elements in the range directly
--> $DIR/single_element_loop.rs:28:17
|
LL | for item in [0..5].into_iter() {
| ^^^^^^^^^^^^^^^^^^ help: did you mean to iterate over the range instead?: `0..5`

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