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