Skip to content

Commit

Permalink
Auto merge of rust-lang#7262 - Jarcho:while_let_on_iter_closure, r=xF…
Browse files Browse the repository at this point in the history
…rednet,flip1995

fix `while_let_on_iterator` suggestion in a closure

fixes: rust-lang#7249

A future improvement would be to check if the closure is being used as `FnOnce`, in which case the original suggestion would be correct.

changelog: Suggest `&mut iter` inside a closure for `while_let_on_iterator`
  • Loading branch information
bors committed Jun 8, 2021
2 parents c1577ab + 7db0e4f commit 07217e3
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 10 deletions.
11 changes: 7 additions & 4 deletions clippy_lints/src/loops/while_let_on_iterator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
use clippy_utils::{
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
Expand Down Expand Up @@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
}
}

if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration unless it is declared within the outer
// loop.
if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration (for loops), or on the next call (for
// closures) unless it is declared within the enclosing expression. TODO: Check for closures
// used where an `FnOnce` type is expected.
let local_id = match iter_expr.path {
Res::Local(id) => id,
_ => return true,
Expand Down
10 changes: 6 additions & 4 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,14 +861,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
})
}

/// Gets the loop enclosing the given expression, if any.
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
/// Gets the loop or closure enclosing the given expression, if any.
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
let map = tcx.hir();
for (_, node) in map.parent_iter(expr.hir_id) {
match node {
Node::Expr(
e @ Expr {
kind: ExprKind::Loop(..),
e
@
Expr {
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
..
},
) => return Some(e),
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/while_let_on_iterator.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,20 @@ fn issue1924() {
println!("iterator field {}", it.1);
}

fn issue7249() {
let mut it = 0..10;
let mut x = || {
// Needs &mut, the closure can be called multiple times
for x in &mut it {
if x % 2 == 0 {
break;
}
}
};
x();
x();
}

fn main() {
let mut it = 0..20;
for _ in it {
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/while_let_on_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,20 @@ fn issue1924() {
println!("iterator field {}", it.1);
}

fn issue7249() {
let mut it = 0..10;
let mut x = || {
// Needs &mut, the closure can be called multiple times
while let Some(x) = it.next() {
if x % 2 == 0 {
break;
}
}
};
x();
x();
}

fn main() {
let mut it = 0..20;
while let Some(..) = it.next() {
Expand Down
10 changes: 8 additions & 2 deletions tests/ui/while_let_on_iterator.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,16 @@ LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`

error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:325:5
--> $DIR/while_let_on_iterator.rs:327:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`

error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:339:5
|
LL | while let Some(..) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`

error: aborting due to 18 previous errors
error: aborting due to 19 previous errors

0 comments on commit 07217e3

Please sign in to comment.