]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops.rs
0f9d030c2f285b373d8256e823aa40370e2ffb7c
[rust.git] / clippy_lints / src / loops.rs
1 use reexport::*;
2 use rustc::hir::*;
3 use rustc::hir::def::Def;
4 use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
5 use rustc::hir::map::Node::NodeBlock;
6 use rustc::lint::*;
7 use rustc::middle::const_val::ConstVal;
8 use rustc::middle::region::CodeExtent;
9 use rustc::ty;
10 use rustc_const_eval::EvalHint::ExprTypeChecked;
11 use rustc_const_eval::eval_const_expr_partial;
12 use std::borrow::Cow;
13 use std::collections::HashMap;
14 use syntax::ast;
15
16 use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro,
17             span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher,
18             walk_ptrs_ty};
19 use utils::paths;
20
21 /// **What it does:** This lint checks for looping over the range of `0..len` of some collection just to get the values by index.
22 ///
23 /// **Why is this bad?** Just iterating the collection itself makes the intent more clear and is probably faster.
24 ///
25 /// **Known problems:** None
26 ///
27 /// **Example:**
28 /// ```
29 /// for i in 0..vec.len() {
30 ///     println!("{}", vec[i]);
31 /// }
32 /// ```
33 declare_lint! {
34     pub NEEDLESS_RANGE_LOOP,
35     Warn,
36     "for-looping over a range of indices where an iterator over items would do"
37 }
38
39 /// **What it does:** This lint checks for loops on `x.iter()` where `&x` will do, and suggest the latter.
40 ///
41 /// **Why is this bad?** Readability.
42 ///
43 /// **Known problems:** False negatives. We currently only warn on some known types.
44 ///
45 /// **Example:** `for x in y.iter() { .. }` (where y is a `Vec` or slice)
46 declare_lint! {
47     pub EXPLICIT_ITER_LOOP,
48     Warn,
49     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
50 }
51
52 /// **What it does:** This lint checks for loops on `x.next()`.
53 ///
54 /// **Why is this bad?** `next()` returns either `Some(value)` if there was a value, or `None` otherwise. The insidious thing is that `Option<_>` implements `IntoIterator`, so that possibly one value will be iterated, leading to some hard to find bugs. No one will want to write such code [except to win an Underhanded Rust Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
55 ///
56 /// **Known problems:** None
57 ///
58 /// **Example:** `for x in y.next() { .. }`
59 declare_lint! {
60     pub ITER_NEXT_LOOP,
61     Warn,
62     "for-looping over `_.next()` which is probably not intended"
63 }
64
65 /// **What it does:** This lint checks for `for` loops over `Option` values.
66 ///
67 /// **Why is this bad?** Readability. This is more clearly expressed as an `if let`.
68 ///
69 /// **Known problems:** None
70 ///
71 /// **Example:** `for x in option { .. }`. This should be `if let Some(x) = option { .. }`.
72 declare_lint! {
73     pub FOR_LOOP_OVER_OPTION,
74     Warn,
75     "for-looping over an `Option`, which is more clearly expressed as an `if let`"
76 }
77
78 /// **What it does:** This lint checks for `for` loops over `Result` values.
79 ///
80 /// **Why is this bad?** Readability. This is more clearly expressed as an `if let`.
81 ///
82 /// **Known problems:** None
83 ///
84 /// **Example:** `for x in result { .. }`. This should be `if let Ok(x) = result { .. }`.
85 declare_lint! {
86     pub FOR_LOOP_OVER_RESULT,
87     Warn,
88     "for-looping over a `Result`, which is more clearly expressed as an `if let`"
89 }
90
91 /// **What it does:** This lint detects `loop + match` combinations that are easier written as a `while let` loop.
92 ///
93 /// **Why is this bad?** The `while let` loop is usually shorter and more readable
94 ///
95 /// **Known problems:** Sometimes the wrong binding is displayed (#383)
96 ///
97 /// **Example:**
98 ///
99 /// ```
100 /// loop {
101 ///     let x = match y {
102 ///         Some(x) => x,
103 ///         None => break,
104 ///     }
105 ///     // .. do something with x
106 /// }
107 /// // is easier written as
108 /// while let Some(x) = y {
109 ///     // .. do something with x
110 /// }
111 /// ```
112 declare_lint! {
113     pub WHILE_LET_LOOP,
114     Warn,
115     "`loop { if let { ... } else break }` can be written as a `while let` loop"
116 }
117
118 /// **What it does:** This lint checks for using `collect()` on an iterator without using the result.
119 ///
120 /// **Why is this bad?** It is more idiomatic to use a `for` loop over the iterator instead.
121 ///
122 /// **Known problems:** None
123 ///
124 /// **Example:** `vec.iter().map(|x| /* some operation returning () */).collect::<Vec<_>>();`
125 declare_lint! {
126     pub UNUSED_COLLECT,
127     Warn,
128     "`collect()`ing an iterator without using the result; this is usually better \
129      written as a for loop"
130 }
131
132 /// **What it does:** This lint checks for loops over ranges `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`, unless the range is reversed or has a negative `.step_by(_)`.
133 ///
134 /// **Why is it bad?** Such loops will either be skipped or loop until wrap-around (in debug code, this may `panic!()`). Both options are probably not intended.
135 ///
136 /// **Known problems:** The lint cannot catch loops over dynamically defined ranges. Doing this would require simulating all possible inputs and code paths through the program, which would be complex and error-prone.
137 ///
138 /// **Examples**: `for x in 5..10-5 { .. }` (oops, stray `-`)
139 declare_lint! {
140     pub REVERSE_RANGE_LOOP,
141     Warn,
142     "Iterating over an empty range, such as `10..0` or `5..5`"
143 }
144
145 /// **What it does:** This lint checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.
146 ///
147 /// **Why is it bad?** Not only is the version using `.enumerate()` more readable, the compiler is able to remove bounds checks which can lead to faster code in some instances.
148 ///
149 /// **Known problems:** None.
150 ///
151 /// **Example:** `for i in 0..v.len() { foo(v[i]); }` or `for i in 0..v.len() { bar(i, v[i]); }`
152 declare_lint! {
153     pub EXPLICIT_COUNTER_LOOP,
154     Warn,
155     "for-looping with an explicit counter when `_.enumerate()` would do"
156 }
157
158 /// **What it does:** This lint checks for empty `loop` expressions.
159 ///
160 /// **Why is this bad?** Those busy loops burn CPU cycles without doing anything. Think of the environment and either block on something or at least make the thread sleep for some microseconds.
161 ///
162 /// **Known problems:** None
163 ///
164 /// **Example:** `loop {}`
165 declare_lint! {
166     pub EMPTY_LOOP,
167     Warn,
168     "empty `loop {}` detected"
169 }
170
171 /// **What it does:** This lint checks for `while let` expressions on iterators.
172 ///
173 /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys the intent better.
174 ///
175 /// **Known problems:** None
176 ///
177 /// **Example:** `while let Some(val) = iter() { .. }`
178 declare_lint! {
179     pub WHILE_LET_ON_ITERATOR,
180     Warn,
181     "using a while-let loop instead of a for loop on an iterator"
182 }
183
184 /// **What it does:** This warns when you iterate on a map (`HashMap` or `BTreeMap`) and ignore
185 /// either the keys or values.
186 ///
187 /// **Why is this bad?** Readability. There are `keys` and `values` methods that can be used to
188 /// express that don't need the values or keys.
189 ///
190 /// **Known problems:** None
191 ///
192 /// **Example:**
193 /// ```rust
194 /// for (k, _) in &map { .. }
195 /// ```
196 /// could be replaced by
197 /// ```rust
198 /// for k in map.keys() { .. }
199 /// ```
200 declare_lint! {
201     pub FOR_KV_MAP,
202     Warn,
203     "looping on a map using `iter` when `keys` or `values` would do"
204 }
205
206 #[derive(Copy, Clone)]
207 pub struct Pass;
208
209 impl LintPass for Pass {
210     fn get_lints(&self) -> LintArray {
211         lint_array!(NEEDLESS_RANGE_LOOP,
212                     EXPLICIT_ITER_LOOP,
213                     ITER_NEXT_LOOP,
214                     WHILE_LET_LOOP,
215                     UNUSED_COLLECT,
216                     REVERSE_RANGE_LOOP,
217                     EXPLICIT_COUNTER_LOOP,
218                     EMPTY_LOOP,
219                     WHILE_LET_ON_ITERATOR,
220                     FOR_KV_MAP)
221     }
222 }
223
224 impl LateLintPass for Pass {
225     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
226         if let Some((pat, arg, body)) = higher::for_loop(expr) {
227             check_for_loop(cx, pat, arg, body, expr);
228         }
229         // check for `loop { if let {} else break }` that could be `while let`
230         // (also matches an explicit "match" instead of "if let")
231         // (even if the "match" or "if let" is used for declaration)
232         if let ExprLoop(ref block, _) = expr.node {
233             // also check for empty `loop {}` statements
234             if block.stmts.is_empty() && block.expr.is_none() {
235                 span_lint(cx,
236                           EMPTY_LOOP,
237                           expr.span,
238                           "empty `loop {}` detected. You may want to either use `panic!()` or add \
239                            `std::thread::sleep(..);` to the loop body.");
240             }
241
242             // extract the expression from the first statement (if any) in a block
243             let inner_stmt_expr = extract_expr_from_first_stmt(block);
244             // or extract the first expression (if any) from the block
245             if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
246                 if let ExprMatch(ref matchexpr, ref arms, ref source) = inner.node {
247                     // ensure "if let" compatible match structure
248                     match *source {
249                         MatchSource::Normal |
250                         MatchSource::IfLetDesugar { .. } => {
251                             if arms.len() == 2 &&
252                                arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
253                                arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
254                                is_break_expr(&arms[1].body) {
255                                 if in_external_macro(cx, expr.span) {
256                                     return;
257                                 }
258
259                                 // NOTE: we used to make build a body here instead of using
260                                 // ellipsis, this was removed because:
261                                 // 1) it was ugly with big bodies;
262                                 // 2) it was not indented properly;
263                                 // 3) it wasn’t very smart (see #675).
264                                 span_lint_and_then(cx,
265                                                    WHILE_LET_LOOP,
266                                                    expr.span,
267                                                    "this loop could be written as a `while let` loop",
268                                                    |db| {
269                                                        let sug = format!("while let {} = {} {{ .. }}",
270                                                                          snippet(cx, arms[0].pats[0].span, ".."),
271                                                                          snippet(cx, matchexpr.span, ".."));
272                                                        db.span_suggestion(expr.span, "try", sug);
273                                                    });
274                             }
275                         }
276                         _ => (),
277                     }
278                 }
279             }
280         }
281         if let ExprMatch(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
282             let pat = &arms[0].pats[0].node;
283             if let (&PatKind::TupleStruct(ref path, ref pat_args, _),
284                     &ExprMethodCall(method_name, _, ref method_args)) = (pat, &match_expr.node) {
285                 let iter_expr = &method_args[0];
286                 if let Some(lhs_constructor) = path.segments.last() {
287                     if method_name.node.as_str() == "next" &&
288                        match_trait_method(cx, match_expr, &paths::ITERATOR) &&
289                        lhs_constructor.name.as_str() == "Some" &&
290                        !is_iterator_used_after_while_let(cx, iter_expr) {
291                         let iterator = snippet(cx, method_args[0].span, "_");
292                         let loop_var = snippet(cx, pat_args[0].span, "_");
293                         span_lint_and_then(cx,
294                                            WHILE_LET_ON_ITERATOR,
295                                            expr.span,
296                                            "this loop could be written as a `for` loop",
297                                            |db| {
298                         db.span_suggestion(expr.span,
299                                            "try",
300                                            format!("for {} in {} {{ .. }}", loop_var, iterator));
301                         });
302                     }
303                 }
304             }
305         }
306     }
307
308     fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
309         if let StmtSemi(ref expr, _) = stmt.node {
310             if let ExprMethodCall(ref method, _, ref args) = expr.node {
311                 if args.len() == 1 && method.node.as_str() == "collect" &&
312                    match_trait_method(cx, expr, &paths::ITERATOR) {
313                     span_lint(cx,
314                               UNUSED_COLLECT,
315                               expr.span,
316                               "you are collect()ing an iterator and throwing away the result. \
317                                Consider using an explicit for loop to exhaust the iterator");
318                 }
319             }
320         }
321     }
322 }
323
324 fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
325     check_for_loop_range(cx, pat, arg, body, expr);
326     check_for_loop_reverse_range(cx, arg, expr);
327     check_for_loop_arg(cx, pat, arg, expr);
328     check_for_loop_explicit_counter(cx, arg, body, expr);
329     check_for_loop_over_map_kv(cx, pat, arg, body, expr);
330 }
331
332 /// Check for looping over a range and then indexing a sequence with it.
333 /// The iteratee must be a range literal.
334 fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
335     if let Some(higher::Range { start: Some(ref start), ref end, .. }) = higher::range(arg) {
336         // the var must be a single name
337         if let PatKind::Binding(_, ref ident, _) = pat.node {
338             let mut visitor = VarVisitor {
339                 cx: cx,
340                 var: ident.node,
341                 indexed: HashMap::new(),
342                 nonindex: false,
343             };
344             walk_expr(&mut visitor, body);
345
346             // linting condition: we only indexed one variable
347             if visitor.indexed.len() == 1 {
348                 let (indexed, indexed_extent) = visitor.indexed
349                                                        .into_iter()
350                                                        .next()
351                                                        .unwrap_or_else(|| unreachable!() /* len == 1 */);
352
353                 // ensure that the indexed variable was declared before the loop, see #601
354                 if let Some(indexed_extent) = indexed_extent {
355                     let pat_extent = cx.tcx.region_maps.var_scope(pat.id);
356                     if cx.tcx.region_maps.is_subscope_of(indexed_extent, pat_extent) {
357                         return;
358                     }
359                 }
360
361                 let starts_at_zero = is_integer_literal(start, 0);
362
363                 let skip: Cow<_> = if starts_at_zero {
364                     "".into()
365                 } else {
366                     format!(".skip({})", snippet(cx, start.span, "..")).into()
367                 };
368
369                 let take: Cow<_> = if let Some(ref end) = *end {
370                     if is_len_call(end, &indexed) {
371                         "".into()
372                     } else {
373                         format!(".take({})", snippet(cx, end.span, "..")).into()
374                     }
375                 } else {
376                     "".into()
377                 };
378
379                 if visitor.nonindex {
380                     span_lint_and_then(cx,
381                                        NEEDLESS_RANGE_LOOP,
382                                        expr.span,
383                                        &format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
384                                        |db| {
385                         multispan_sugg(db, "consider using an iterator".to_string(), &[
386                             (pat.span, &format!("({}, <item>)", ident.node)),
387                             (arg.span, &format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
388                         ]);
389                     });
390                 } else {
391                     let repl = if starts_at_zero && take.is_empty() {
392                         format!("&{}", indexed)
393                     } else {
394                         format!("{}.iter(){}{}", indexed, take, skip)
395                     };
396
397                     span_lint_and_then(cx,
398                                        NEEDLESS_RANGE_LOOP,
399                                        expr.span,
400                                        &format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
401                                        |db| {
402                         multispan_sugg(db, "consider using an iterator".to_string(), &[
403                             (pat.span, "<item>"),
404                             (arg.span, &repl),
405                         ]);
406                     });
407                 }
408             }
409         }
410     }
411 }
412
413 fn is_len_call(expr: &Expr, var: &Name) -> bool {
414     if_let_chain! {[
415         let ExprMethodCall(method, _, ref len_args) = expr.node,
416         len_args.len() == 1,
417         method.node.as_str() == "len",
418         let ExprPath(_, ref path) = len_args[0].node,
419         path.segments.len() == 1,
420         &path.segments[0].name == var
421     ], {
422         return true;
423     }}
424
425     false
426 }
427
428 fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
429     // if this for loop is iterating over a two-sided range...
430     if let Some(higher::Range { start: Some(ref start), end: Some(ref end), limits }) = higher::range(arg) {
431         // ...and both sides are compile-time constant integers...
432         if let Ok(start_idx) = eval_const_expr_partial(cx.tcx, start, ExprTypeChecked, None) {
433             if let Ok(end_idx) = eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None) {
434                 // ...and the start index is greater than the end index,
435                 // this loop will never run. This is often confusing for developers
436                 // who think that this will iterate from the larger value to the
437                 // smaller value.
438                 let (sup, eq) = match (start_idx, end_idx) {
439                     (ConstVal::Integral(start_idx), ConstVal::Integral(end_idx)) => {
440                         (start_idx > end_idx, start_idx == end_idx)
441                     }
442                     _ => (false, false),
443                 };
444
445                 if sup {
446                     let start_snippet = snippet(cx, start.span, "_");
447                     let end_snippet = snippet(cx, end.span, "_");
448                     let dots = if limits == ast::RangeLimits::Closed {
449                         "..."
450                     } else {
451                         ".."
452                     };
453
454                     span_lint_and_then(cx,
455                                        REVERSE_RANGE_LOOP,
456                                        expr.span,
457                                        "this range is empty so this for loop will never run",
458                                        |db| {
459                                            db.span_suggestion(arg.span,
460                                                               "consider using the following if \
461                                                                you are attempting to iterate \
462                                                                over this range in reverse",
463                                                               format!("({end}{dots}{start}).rev()",
464                                                                       end=end_snippet,
465                                                                       dots=dots,
466                                                                       start=start_snippet));
467                                        });
468                 } else if eq && limits != ast::RangeLimits::Closed {
469                     // if they are equal, it's also problematic - this loop
470                     // will never run.
471                     span_lint(cx,
472                               REVERSE_RANGE_LOOP,
473                               expr.span,
474                               "this range is empty so this for loop will never run");
475                 }
476             }
477         }
478     }
479 }
480
481 fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
482     let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
483     if let ExprMethodCall(ref method, _, ref args) = arg.node {
484         // just the receiver, no arguments
485         if args.len() == 1 {
486             let method_name = method.node;
487             // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
488             if method_name.as_str() == "iter" || method_name.as_str() == "iter_mut" {
489                 if is_ref_iterable_type(cx, &args[0]) {
490                     let object = snippet(cx, args[0].span, "_");
491                     span_lint(cx,
492                               EXPLICIT_ITER_LOOP,
493                               expr.span,
494                               &format!("it is more idiomatic to loop over `&{}{}` instead of `{}.{}()`",
495                                        if method_name.as_str() == "iter_mut" {
496                                            "mut "
497                                        } else {
498                                            ""
499                                        },
500                                        object,
501                                        object,
502                                        method_name));
503                 }
504             } else if method_name.as_str() == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
505                 span_lint(cx,
506                           ITER_NEXT_LOOP,
507                           expr.span,
508                           "you are iterating over `Iterator::next()` which is an Option; this will compile but is \
509                            probably not what you want");
510                 next_loop_linted = true;
511             }
512         }
513     }
514     if !next_loop_linted {
515         check_arg_type(cx, pat, arg);
516     }
517 }
518
519 /// Check for `for` loops over `Option`s and `Results`
520 fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
521     let ty = cx.tcx.expr_ty(arg);
522     if match_type(cx, ty, &paths::OPTION) {
523         span_help_and_lint(cx,
524                            FOR_LOOP_OVER_OPTION,
525                            arg.span,
526                            &format!("for loop over `{0}`, which is an `Option`. This is more readably written as an \
527                                      `if let` statement.",
528                                     snippet(cx, arg.span, "_")),
529                            &format!("consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
530                                     snippet(cx, pat.span, "_"),
531                                     snippet(cx, arg.span, "_")));
532     } else if match_type(cx, ty, &paths::RESULT) {
533         span_help_and_lint(cx,
534                            FOR_LOOP_OVER_RESULT,
535                            arg.span,
536                            &format!("for loop over `{0}`, which is a `Result`. This is more readably written as an \
537                                      `if let` statement.",
538                                     snippet(cx, arg.span, "_")),
539                            &format!("consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
540                                     snippet(cx, pat.span, "_"),
541                                     snippet(cx, arg.span, "_")));
542     }
543 }
544
545 fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, expr: &Expr) {
546     // Look for variables that are incremented once per loop iteration.
547     let mut visitor = IncrementVisitor {
548         cx: cx,
549         states: HashMap::new(),
550         depth: 0,
551         done: false,
552     };
553     walk_expr(&mut visitor, body);
554
555     // For each candidate, check the parent block to see if
556     // it's initialized to zero at the start of the loop.
557     let map = &cx.tcx.map;
558     let parent_scope = map.get_enclosing_scope(expr.id).and_then(|id| map.get_enclosing_scope(id));
559     if let Some(parent_id) = parent_scope {
560         if let NodeBlock(block) = map.get(parent_id) {
561             for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
562                 let mut visitor2 = InitializeVisitor {
563                     cx: cx,
564                     end_expr: expr,
565                     var_id: *id,
566                     state: VarState::IncrOnce,
567                     name: None,
568                     depth: 0,
569                     past_loop: false,
570                 };
571                 walk_block(&mut visitor2, block);
572
573                 if visitor2.state == VarState::Warn {
574                     if let Some(name) = visitor2.name {
575                         span_lint(cx,
576                                   EXPLICIT_COUNTER_LOOP,
577                                   expr.span,
578                                   &format!("the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
579                                             item) in {1}.enumerate()` or similar iterators",
580                                            name,
581                                            snippet(cx, arg.span, "_")));
582                     }
583                 }
584             }
585         }
586     }
587 }
588
589 /// Check for the `FOR_KV_MAP` lint.
590 fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
591     if let PatKind::Tuple(ref pat, _) = pat.node {
592         if pat.len() == 2 {
593             let (pat_span, kind) = match (&pat[0].node, &pat[1].node) {
594                 (key, _) if pat_is_wild(key, body) => (&pat[1].span, "values"),
595                 (_, value) if pat_is_wild(value, body) => (&pat[0].span, "keys"),
596                 _ => return,
597             };
598
599             let arg_span = match arg.node {
600                 ExprAddrOf(MutImmutable, ref expr) => expr.span,
601                 ExprAddrOf(MutMutable, _) => return, // for _ in &mut _, there is no {values,keys}_mut method
602                 _ => arg.span,
603             };
604
605             let ty = walk_ptrs_ty(cx.tcx.expr_ty(arg));
606             if match_type(cx, ty, &paths::HASHMAP) || match_type(cx, ty, &paths::BTREEMAP) {
607                 span_lint_and_then(cx,
608                                    FOR_KV_MAP,
609                                    expr.span,
610                                    &format!("you seem to want to iterate on a map's {}", kind),
611                                    |db| {
612                     db.span_suggestion(expr.span,
613                                        "use the corresponding method",
614                                        format!("for {} in {}.{}() {{ .. }}",
615                                                snippet(cx, *pat_span, ".."),
616                                                snippet(cx, arg_span, ".."),
617                                                kind));
618                 });
619             }
620         }
621     }
622
623 }
624
625 /// Return true if the pattern is a `PatWild` or an ident prefixed with `'_'`.
626 fn pat_is_wild(pat: &PatKind, body: &Expr) -> bool {
627     match *pat {
628         PatKind::Wild => true,
629         PatKind::Binding(_, ident, None) if ident.node.as_str().starts_with('_') => {
630             let mut visitor = UsedVisitor {
631                 var: ident.node,
632                 used: false,
633             };
634             walk_expr(&mut visitor, body);
635             !visitor.used
636         }
637         _ => false,
638     }
639 }
640
641 struct UsedVisitor {
642     var: ast::Name, // var to look for
643     used: bool, // has the var been used otherwise?
644 }
645
646 impl<'a> Visitor<'a> for UsedVisitor {
647     fn visit_expr(&mut self, expr: &Expr) {
648         if let ExprPath(None, ref path) = expr.node {
649             if path.segments.len() == 1 && path.segments[0].name == self.var {
650                 self.used = true;
651                 return;
652             }
653         }
654
655         walk_expr(self, expr);
656     }
657 }
658
659 struct VarVisitor<'v, 't: 'v> {
660     cx: &'v LateContext<'v, 't>, // context reference
661     var: Name, // var name to look for as index
662     indexed: HashMap<Name, Option<CodeExtent>>, // indexed variables, the extent is None for global
663     nonindex: bool, // has the var been used otherwise?
664 }
665
666 impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
667     fn visit_expr(&mut self, expr: &'v Expr) {
668         if let ExprPath(None, ref path) = expr.node {
669             if path.segments.len() == 1 && path.segments[0].name == self.var {
670                 // we are referencing our variable! now check if it's as an index
671                 if_let_chain! {[
672                     let Some(parexpr) = get_parent_expr(self.cx, expr),
673                     let ExprIndex(ref seqexpr, _) = parexpr.node,
674                     let ExprPath(None, ref seqvar) = seqexpr.node,
675                     seqvar.segments.len() == 1
676                 ], {
677                     let def_map = self.cx.tcx.def_map.borrow();
678                     if let Some(def) = def_map.get(&seqexpr.id) {
679                         match def.base_def {
680                             Def::Local(..) | Def::Upvar(..) => {
681                                 let extent = self.cx.tcx.region_maps.var_scope(def.base_def.var_id());
682                                 self.indexed.insert(seqvar.segments[0].name, Some(extent));
683                                 return;  // no need to walk further
684                             }
685                             Def::Static(..) | Def::Const(..) => {
686                                 self.indexed.insert(seqvar.segments[0].name, None);
687                                 return;  // no need to walk further
688                             }
689                             _ => (),
690                         }
691                     }
692                 }}
693                 // we are not indexing anything, record that
694                 self.nonindex = true;
695                 return;
696             }
697         }
698         walk_expr(self, expr);
699     }
700 }
701
702 fn is_iterator_used_after_while_let(cx: &LateContext, iter_expr: &Expr) -> bool {
703     let def_id = match var_def_id(cx, iter_expr) {
704         Some(id) => id,
705         None => return false,
706     };
707     let mut visitor = VarUsedAfterLoopVisitor {
708         cx: cx,
709         def_id: def_id,
710         iter_expr_id: iter_expr.id,
711         past_while_let: false,
712         var_used_after_while_let: false,
713     };
714     if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
715         walk_block(&mut visitor, enclosing_block);
716     }
717     visitor.var_used_after_while_let
718 }
719
720 struct VarUsedAfterLoopVisitor<'v, 't: 'v> {
721     cx: &'v LateContext<'v, 't>,
722     def_id: NodeId,
723     iter_expr_id: NodeId,
724     past_while_let: bool,
725     var_used_after_while_let: bool,
726 }
727
728 impl<'v, 't> Visitor<'v> for VarUsedAfterLoopVisitor<'v, 't> {
729     fn visit_expr(&mut self, expr: &'v Expr) {
730         if self.past_while_let {
731             if Some(self.def_id) == var_def_id(self.cx, expr) {
732                 self.var_used_after_while_let = true;
733             }
734         } else if self.iter_expr_id == expr.id {
735             self.past_while_let = true;
736         }
737         walk_expr(self, expr);
738     }
739 }
740
741
742 /// Return true if the type of expr is one that provides `IntoIterator` impls
743 /// for `&T` and `&mut T`, such as `Vec`.
744 #[cfg_attr(rustfmt, rustfmt_skip)]
745 fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {
746     // no walk_ptrs_ty: calling iter() on a reference can make sense because it
747     // will allow further borrows afterwards
748     let ty = cx.tcx.expr_ty(e);
749     is_iterable_array(ty) ||
750     match_type(cx, ty, &paths::VEC) ||
751     match_type(cx, ty, &paths::LINKED_LIST) ||
752     match_type(cx, ty, &paths::HASHMAP) ||
753     match_type(cx, ty, &paths::HASHSET) ||
754     match_type(cx, ty, &paths::VEC_DEQUE) ||
755     match_type(cx, ty, &paths::BINARY_HEAP) ||
756     match_type(cx, ty, &paths::BTREEMAP) ||
757     match_type(cx, ty, &paths::BTREESET)
758 }
759
760 fn is_iterable_array(ty: ty::Ty) -> bool {
761     // IntoIterator is currently only implemented for array sizes <= 32 in rustc
762     match ty.sty {
763         ty::TyArray(_, 0...32) => true,
764         _ => false,
765     }
766 }
767
768 /// If a block begins with a statement (possibly a `let` binding) and has an expression, return it.
769 fn extract_expr_from_first_stmt(block: &Block) -> Option<&Expr> {
770     if block.stmts.is_empty() {
771         return None;
772     }
773     if let StmtDecl(ref decl, _) = block.stmts[0].node {
774         if let DeclLocal(ref local) = decl.node {
775             if let Some(ref expr) = local.init {
776                 Some(expr)
777             } else {
778                 None
779             }
780         } else {
781             None
782         }
783     } else {
784         None
785     }
786 }
787
788 /// If a block begins with an expression (with or without semicolon), return it.
789 fn extract_first_expr(block: &Block) -> Option<&Expr> {
790     match block.expr {
791         Some(ref expr) if block.stmts.is_empty() => Some(expr),
792         None if !block.stmts.is_empty() => {
793             match block.stmts[0].node {
794                 StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => Some(expr),
795                 StmtDecl(..) => None,
796             }
797         }
798         _ => None,
799     }
800 }
801
802 /// Return true if expr contains a single break expr (maybe within a block).
803 fn is_break_expr(expr: &Expr) -> bool {
804     match expr.node {
805         ExprBreak(None) => true,
806         ExprBlock(ref b) => {
807             match extract_first_expr(b) {
808                 Some(ref subexpr) => is_break_expr(subexpr),
809                 None => false,
810             }
811         }
812         _ => false,
813     }
814 }
815
816 // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
817 // incremented exactly once in the loop body, and initialized to zero
818 // at the start of the loop.
819 #[derive(PartialEq)]
820 enum VarState {
821     Initial, // Not examined yet
822     IncrOnce, // Incremented exactly once, may be a loop counter
823     Declared, // Declared but not (yet) initialized to zero
824     Warn,
825     DontWarn,
826 }
827
828 /// Scan a for loop for variables that are incremented exactly once.
829 struct IncrementVisitor<'v, 't: 'v> {
830     cx: &'v LateContext<'v, 't>, // context reference
831     states: HashMap<NodeId, VarState>, // incremented variables
832     depth: u32, // depth of conditional expressions
833     done: bool,
834 }
835
836 impl<'v, 't> Visitor<'v> for IncrementVisitor<'v, 't> {
837     fn visit_expr(&mut self, expr: &'v Expr) {
838         if self.done {
839             return;
840         }
841
842         // If node is a variable
843         if let Some(def_id) = var_def_id(self.cx, expr) {
844             if let Some(parent) = get_parent_expr(self.cx, expr) {
845                 let state = self.states.entry(def_id).or_insert(VarState::Initial);
846
847                 match parent.node {
848                     ExprAssignOp(op, ref lhs, ref rhs) => {
849                         if lhs.id == expr.id {
850                             if op.node == BiAdd && is_integer_literal(rhs, 1) {
851                                 *state = match *state {
852                                     VarState::Initial if self.depth == 0 => VarState::IncrOnce,
853                                     _ => VarState::DontWarn,
854                                 };
855                             } else {
856                                 // Assigned some other value
857                                 *state = VarState::DontWarn;
858                             }
859                         }
860                     }
861                     ExprAssign(ref lhs, _) if lhs.id == expr.id => *state = VarState::DontWarn,
862                     ExprAddrOf(mutability, _) if mutability == MutMutable => *state = VarState::DontWarn,
863                     _ => (),
864                 }
865             }
866         } else if is_loop(expr) {
867             self.states.clear();
868             self.done = true;
869             return;
870         } else if is_conditional(expr) {
871             self.depth += 1;
872             walk_expr(self, expr);
873             self.depth -= 1;
874             return;
875         }
876         walk_expr(self, expr);
877     }
878 }
879
880 /// Check whether a variable is initialized to zero at the start of a loop.
881 struct InitializeVisitor<'v, 't: 'v> {
882     cx: &'v LateContext<'v, 't>, // context reference
883     end_expr: &'v Expr, // the for loop. Stop scanning here.
884     var_id: NodeId,
885     state: VarState,
886     name: Option<Name>,
887     depth: u32, // depth of conditional expressions
888     past_loop: bool,
889 }
890
891 impl<'v, 't> Visitor<'v> for InitializeVisitor<'v, 't> {
892     fn visit_decl(&mut self, decl: &'v Decl) {
893         // Look for declarations of the variable
894         if let DeclLocal(ref local) = decl.node {
895             if local.pat.id == self.var_id {
896                 if let PatKind::Binding(_, ref ident, _) = local.pat.node {
897                     self.name = Some(ident.node);
898
899                     self.state = if let Some(ref init) = local.init {
900                         if is_integer_literal(init, 0) {
901                             VarState::Warn
902                         } else {
903                             VarState::Declared
904                         }
905                     } else {
906                         VarState::Declared
907                     }
908                 }
909             }
910         }
911         walk_decl(self, decl);
912     }
913
914     fn visit_expr(&mut self, expr: &'v Expr) {
915         if self.state == VarState::DontWarn {
916             return;
917         }
918         if expr == self.end_expr {
919             self.past_loop = true;
920             return;
921         }
922         // No need to visit expressions before the variable is
923         // declared
924         if self.state == VarState::IncrOnce {
925             return;
926         }
927
928         // If node is the desired variable, see how it's used
929         if var_def_id(self.cx, expr) == Some(self.var_id) {
930             if let Some(parent) = get_parent_expr(self.cx, expr) {
931                 match parent.node {
932                     ExprAssignOp(_, ref lhs, _) if lhs.id == expr.id => {
933                         self.state = VarState::DontWarn;
934                     }
935                     ExprAssign(ref lhs, ref rhs) if lhs.id == expr.id => {
936                         self.state = if is_integer_literal(rhs, 0) && self.depth == 0 {
937                             VarState::Warn
938                         } else {
939                             VarState::DontWarn
940                         }
941                     }
942                     ExprAddrOf(mutability, _) if mutability == MutMutable => self.state = VarState::DontWarn,
943                     _ => (),
944                 }
945             }
946
947             if self.past_loop {
948                 self.state = VarState::DontWarn;
949                 return;
950             }
951         } else if !self.past_loop && is_loop(expr) {
952             self.state = VarState::DontWarn;
953             return;
954         } else if is_conditional(expr) {
955             self.depth += 1;
956             walk_expr(self, expr);
957             self.depth -= 1;
958             return;
959         }
960         walk_expr(self, expr);
961     }
962 }
963
964 fn var_def_id(cx: &LateContext, expr: &Expr) -> Option<NodeId> {
965     if let Some(path_res) = cx.tcx.def_map.borrow().get(&expr.id) {
966         if let Def::Local(_, node_id) = path_res.base_def {
967             return Some(node_id);
968         }
969     }
970     None
971 }
972
973 fn is_loop(expr: &Expr) -> bool {
974     match expr.node {
975         ExprLoop(..) | ExprWhile(..) => true,
976         _ => false,
977     }
978 }
979
980 fn is_conditional(expr: &Expr) -> bool {
981     match expr.node {
982         ExprIf(..) | ExprMatch(..) => true,
983         _ => false,
984     }
985 }