Skip to content

Commit b740020

Browse files
committed
Auto merge of #7262 - Jarcho:while_let_on_iter_closure, r=flip1995
fix `while_let_on_iterator` suggestion in a closure fixes: #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`
2 parents c1577ab + 7db0e4f commit b740020

File tree

5 files changed

+49
-10
lines changed

5 files changed

+49
-10
lines changed

clippy_lints/src/loops/while_let_on_iterator.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use super::WHILE_LET_ON_ITERATOR;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::snippet_with_applicability;
4-
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
4+
use clippy_utils::{
5+
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
6+
};
57
use if_chain::if_chain;
68
use rustc_errors::Applicability;
79
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
@@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
315317
}
316318
}
317319

318-
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
319-
// The iterator expression will be used on the next iteration unless it is declared within the outer
320-
// loop.
320+
if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
321+
// The iterator expression will be used on the next iteration (for loops), or on the next call (for
322+
// closures) unless it is declared within the enclosing expression. TODO: Check for closures
323+
// used where an `FnOnce` type is expected.
321324
let local_id = match iter_expr.path {
322325
Res::Local(id) => id,
323326
_ => return true,

clippy_utils/src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -861,14 +861,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
861861
})
862862
}
863863

864-
/// Gets the loop enclosing the given expression, if any.
865-
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
864+
/// Gets the loop or closure enclosing the given expression, if any.
865+
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
866866
let map = tcx.hir();
867867
for (_, node) in map.parent_iter(expr.hir_id) {
868868
match node {
869869
Node::Expr(
870-
e @ Expr {
871-
kind: ExprKind::Loop(..),
870+
e
871+
@
872+
Expr {
873+
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
872874
..
873875
},
874876
) => return Some(e),

tests/ui/while_let_on_iterator.fixed

+14
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ fn issue1924() {
320320
println!("iterator field {}", it.1);
321321
}
322322

323+
fn issue7249() {
324+
let mut it = 0..10;
325+
let mut x = || {
326+
// Needs &mut, the closure can be called multiple times
327+
for x in &mut it {
328+
if x % 2 == 0 {
329+
break;
330+
}
331+
}
332+
};
333+
x();
334+
x();
335+
}
336+
323337
fn main() {
324338
let mut it = 0..20;
325339
for _ in it {

tests/ui/while_let_on_iterator.rs

+14
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ fn issue1924() {
320320
println!("iterator field {}", it.1);
321321
}
322322

323+
fn issue7249() {
324+
let mut it = 0..10;
325+
let mut x = || {
326+
// Needs &mut, the closure can be called multiple times
327+
while let Some(x) = it.next() {
328+
if x % 2 == 0 {
329+
break;
330+
}
331+
}
332+
};
333+
x();
334+
x();
335+
}
336+
323337
fn main() {
324338
let mut it = 0..20;
325339
while let Some(..) = it.next() {

tests/ui/while_let_on_iterator.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,16 @@ LL | while let Some(n) = it.next() {
105105
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
106106

107107
error: this loop could be written as a `for` loop
108-
--> $DIR/while_let_on_iterator.rs:325:5
108+
--> $DIR/while_let_on_iterator.rs:327:9
109+
|
110+
LL | while let Some(x) = it.next() {
111+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
112+
113+
error: this loop could be written as a `for` loop
114+
--> $DIR/while_let_on_iterator.rs:339:5
109115
|
110116
LL | while let Some(..) = it.next() {
111117
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
112118

113-
error: aborting due to 18 previous errors
119+
error: aborting due to 19 previous errors
114120

0 commit comments

Comments
 (0)