Skip to content

Commit 2d6ca62

Browse files
committed
Don't suggest using a for loop if the iterator is used in the loop body
Due to rust-lang/rust#8372, we have to use while-let in these cases.
1 parent f3c3e02 commit 2d6ca62

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

src/loops.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,14 @@ impl LateLintPass for LoopsPass {
232232
}
233233
}
234234
if let ExprMatch(ref expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
235+
let body = &arms[0].body;
235236
let pat = &arms[0].pats[0].node;
236-
if let (&PatEnum(ref path, _), &ExprMethodCall(method_name, _, _)) = (pat, &expr.node) {
237+
if let (&PatEnum(ref path, _), &ExprMethodCall(method_name, _, ref args)) = (pat, &expr.node) {
238+
let iterator_def_id = var_def_id(cx, &args[0]);
237239
if method_name.node.as_str() == "next" &&
238240
match_trait_method(cx, expr, &["core", "iter", "Iterator"]) &&
239-
path.segments.last().unwrap().identifier.name.as_str() == "Some" {
241+
path.segments.last().unwrap().identifier.name.as_str() == "Some" &&
242+
!var_used(body, iterator_def_id, cx) {
240243
span_lint(cx, WHILE_LET_ON_ITERATOR, expr.span,
241244
"this loop could be written as a `for` loop");
242245
}
@@ -314,6 +317,32 @@ impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
314317
}
315318
}
316319

320+
fn var_used(expr: &Expr, def_id: Option<NodeId>, cx: &LateContext) -> bool {
321+
match def_id {
322+
None => false,
323+
Some(def_id) => {
324+
let mut visitor = VarUsedVisitor{ def_id: def_id, found: false, cx: cx };
325+
walk_expr(&mut visitor, expr);
326+
visitor.found
327+
}
328+
}
329+
}
330+
331+
struct VarUsedVisitor<'v, 't: 'v> {
332+
cx: &'v LateContext<'v, 't>,
333+
def_id: NodeId,
334+
found: bool
335+
}
336+
337+
impl<'v, 't> Visitor<'v> for VarUsedVisitor<'v, 't> {
338+
fn visit_expr(&mut self, expr: &'v Expr) {
339+
if Some(self.def_id) == var_def_id(self.cx, expr) {
340+
self.found = true;
341+
}
342+
walk_expr(self, expr);
343+
}
344+
}
345+
317346
/// Return true if the type of expr is one that provides IntoIterator impls
318347
/// for &T and &mut T, such as Vec.
319348
fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {

tests/compile-fail/while_loop.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ fn main() {
7070
if let Some(x) = (1..20).next() { // also fine
7171
println!("{}", x)
7272
}
73+
74+
// the following shouldn't warn because it can't be written with a for loop
75+
let mut iter = 1u32..20;
76+
while let Some(x) = iter.next() {
77+
println!("next: {:?}", iter.next())
78+
}
79+
80+
// but this should:
81+
let mut iter2 = 1u32..20;
82+
while let Some(x) = iter2.next() { } //~ERROR this loop could be written as a `for` loop
7383
}
7484

7585
// regression test (#360)

0 commit comments

Comments
 (0)