Skip to content

Commit 5ca7ebb

Browse files
committed
Fix false positives when iterator variable is used after the loop
1 parent 6641958 commit 5ca7ebb

File tree

3 files changed

+61
-17
lines changed

3 files changed

+61
-17
lines changed

src/loops.rs

+31-17
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use std::collections::{HashSet,HashMap};
1111
use syntax::ast::Lit_::*;
1212

1313
use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type,
14-
in_external_macro, expr_block, span_help_and_lint, is_integer_literal};
14+
in_external_macro, expr_block, span_help_and_lint, is_integer_literal,
15+
get_enclosing_block};
1516
use utils::{VEC_PATH, LL_PATH};
1617

1718
declare_lint!{ pub NEEDLESS_RANGE_LOOP, Warn,
@@ -232,17 +233,16 @@ impl LateLintPass for LoopsPass {
232233
}
233234
}
234235
if let ExprMatch(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
235-
let body = &arms[0].body;
236236
let pat = &arms[0].pats[0].node;
237237
if let (&PatEnum(ref path, Some(ref pat_args)),
238238
&ExprMethodCall(method_name, _, ref method_args)) =
239239
(pat, &match_expr.node) {
240-
let iterator_def_id = var_def_id(cx, &method_args[0]);
240+
let iter_expr = &method_args[0];
241241
if let Some(lhs_constructor) = path.segments.last() {
242242
if method_name.node.as_str() == "next" &&
243243
match_trait_method(cx, match_expr, &["core", "iter", "Iterator"]) &&
244244
lhs_constructor.identifier.name.as_str() == "Some" &&
245-
!var_used(body, iterator_def_id, cx) {
245+
!is_iterator_used_after_while_let(cx, iter_expr) {
246246
let iterator = snippet(cx, method_args[0].span, "_");
247247
let loop_var = snippet(cx, pat_args[0].span, "_");
248248
span_help_and_lint(cx, WHILE_LET_ON_ITERATOR, expr.span,
@@ -326,32 +326,46 @@ impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
326326
}
327327
}
328328

329-
fn var_used(expr: &Expr, def_id: Option<NodeId>, cx: &LateContext) -> bool {
330-
match def_id {
331-
None => false,
332-
Some(def_id) => {
333-
let mut visitor = VarUsedVisitor{ def_id: def_id, found: false, cx: cx };
334-
walk_expr(&mut visitor, expr);
335-
visitor.found
336-
}
329+
fn is_iterator_used_after_while_let(cx: &LateContext, iter_expr: &Expr) -> bool {
330+
let def_id = match var_def_id(cx, iter_expr) {
331+
Some(id) => id,
332+
None => return false
333+
};
334+
let mut visitor = VarUsedAfterLoopVisitor {
335+
cx: cx,
336+
def_id: def_id,
337+
iter_expr_id: iter_expr.id,
338+
past_while_let: false,
339+
var_used_after_while_let: false
340+
};
341+
if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
342+
walk_block(&mut visitor, enclosing_block);
337343
}
344+
visitor.var_used_after_while_let
338345
}
339346

340-
struct VarUsedVisitor<'v, 't: 'v> {
347+
struct VarUsedAfterLoopVisitor<'v, 't: 'v> {
341348
cx: &'v LateContext<'v, 't>,
342349
def_id: NodeId,
343-
found: bool
350+
iter_expr_id: NodeId,
351+
past_while_let: bool,
352+
var_used_after_while_let: bool
344353
}
345354

346-
impl<'v, 't> Visitor<'v> for VarUsedVisitor<'v, 't> {
355+
impl <'v, 't> Visitor<'v> for VarUsedAfterLoopVisitor<'v, 't> {
347356
fn visit_expr(&mut self, expr: &'v Expr) {
348-
if Some(self.def_id) == var_def_id(self.cx, expr) {
349-
self.found = true;
357+
if self.past_while_let {
358+
if Some(self.def_id) == var_def_id(self.cx, expr) {
359+
self.var_used_after_while_let = true;
360+
}
361+
} else if self.iter_expr_id == expr.id {
362+
self.past_while_let = true;
350363
}
351364
walk_expr(self, expr);
352365
}
353366
}
354367

368+
355369
/// Return true if the type of expr is one that provides IntoIterator impls
356370
/// for &T and &mut T, such as Vec.
357371
fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {

src/utils.rs

+13
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,19 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> {
255255
if let NodeExpr(parent) = node { Some(parent) } else { None } )
256256
}
257257

258+
pub fn get_enclosing_block<'c>(cx: &'c LateContext, node: NodeId) -> Option<&'c Block> {
259+
let map = &cx.tcx.map;
260+
let enclosing_node = map.get_enclosing_scope(node)
261+
.and_then(|enclosing_id| map.find(enclosing_id));
262+
if let Some(node) = enclosing_node {
263+
match node {
264+
NodeBlock(ref block) => Some(block),
265+
NodeItem(&Item{ node: ItemFn(_, _, _, _, _, ref block), .. }) => Some(block),
266+
_ => None
267+
}
268+
} else { None }
269+
}
270+
258271
#[cfg(not(feature="structured_logging"))]
259272
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
260273
cx.span_lint(lint, sp, msg);

tests/compile-fail/while_loop.rs

+17
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,23 @@ fn main() {
8080
while let Some(x) = iter.next() {
8181
println!("next: {:?}", iter.next())
8282
}
83+
84+
// neither can this
85+
let mut iter = 1u32..20;
86+
while let Some(x) = iter.next() {
87+
println!("next: {:?}", iter.next());
88+
}
89+
90+
// or this
91+
let mut iter = 1u32..20;
92+
while let Some(x) = iter.next() {break;}
93+
println!("Remaining iter {:?}", iter);
94+
95+
// or this
96+
let mut iter = 1u32..20;
97+
while let Some(x) = iter.next() {
98+
iter = 1..20;
99+
}
83100
}
84101

85102
// regression test (#360)

0 commit comments

Comments
 (0)