]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops.rs
42b31f8eaaaf5eafcee0a7d5a16d7f58ad77fa00
[rust.git] / clippy_lints / src / loops.rs
1 use itertools::Itertools;
2 use reexport::*;
3 use rustc::hir::*;
4 use rustc::hir::def::Def;
5 use rustc::hir::intravisit::{walk_block, walk_decl, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
6 use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt};
7 use rustc::lint::*;
8 use rustc::middle::const_val::ConstVal;
9 use rustc::middle::region;
10 use rustc::ty::{self, Ty};
11 use rustc::ty::subst::{Subst, Substs};
12 use rustc_const_eval::ConstContext;
13 use std::collections::{HashMap, HashSet};
14 use syntax::ast;
15 use utils::sugg;
16 use utils::const_to_u64;
17
18 use utils::{get_enclosing_block, get_parent_expr, higher, in_external_macro, is_integer_literal, is_refutable,
19             last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt,
20             span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then};
21 use utils::paths;
22
23 /// **What it does:** Checks for for-loops that manually copy items between
24 /// slices that could be optimized by having a memcpy.
25 ///
26 /// **Why is this bad?** It is not as fast as a memcpy.
27 ///
28 /// **Known problems:** None.
29 ///
30 /// **Example:**
31 /// ```rust
32 /// for i in 0..src.len() {
33 ///     dst[i + 64] = src[i];
34 /// }
35 /// ```
36 declare_lint! {
37     pub MANUAL_MEMCPY,
38     Warn,
39     "manually copying items between slices"
40 }
41
42 /// **What it does:** Checks for looping over the range of `0..len` of some
43 /// collection just to get the values by index.
44 ///
45 /// **Why is this bad?** Just iterating the collection itself makes the intent
46 /// more clear and is probably faster.
47 ///
48 /// **Known problems:** None.
49 ///
50 /// **Example:**
51 /// ```rust
52 /// for i in 0..vec.len() {
53 ///     println!("{}", vec[i]);
54 /// }
55 /// ```
56 declare_lint! {
57     pub NEEDLESS_RANGE_LOOP,
58     Warn,
59     "for-looping over a range of indices where an iterator over items would do"
60 }
61
62 /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and
63 /// suggests the latter.
64 ///
65 /// **Why is this bad?** Readability.
66 ///
67 /// **Known problems:** False negatives. We currently only warn on some known
68 /// types.
69 ///
70 /// **Example:**
71 /// ```rust
72 /// // with `y` a `Vec` or slice:
73 /// for x in y.iter() { .. }
74 /// ```
75 declare_lint! {
76     pub EXPLICIT_ITER_LOOP,
77     Warn,
78     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
79 }
80
81 /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and
82 /// suggests the latter.
83 ///
84 /// **Why is this bad?** Readability.
85 ///
86 /// **Known problems:** None
87 ///
88 /// **Example:**
89 /// ```rust
90 /// // with `y` a `Vec` or slice:
91 /// for x in y.into_iter() { .. }
92 /// ```
93 declare_lint! {
94     pub EXPLICIT_INTO_ITER_LOOP,
95     Warn,
96     "for-looping over `_.into_iter()` when `_` would do"
97 }
98
99 /// **What it does:** Checks for loops on `x.next()`.
100 ///
101 /// **Why is this bad?** `next()` returns either `Some(value)` if there was a
102 /// value, or `None` otherwise. The insidious thing is that `Option<_>`
103 /// implements `IntoIterator`, so that possibly one value will be iterated,
104 /// leading to some hard to find bugs. No one will want to write such code
105 /// [except to win an Underhanded Rust
106 /// Contest](https://www.reddit.
107 /// com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
108 ///
109 /// **Known problems:** None.
110 ///
111 /// **Example:**
112 /// ```rust
113 /// for x in y.next() { .. }
114 /// ```
115 declare_lint! {
116     pub ITER_NEXT_LOOP,
117     Warn,
118     "for-looping over `_.next()` which is probably not intended"
119 }
120
121 /// **What it does:** Checks for `for` loops over `Option` values.
122 ///
123 /// **Why is this bad?** Readability. This is more clearly expressed as an `if
124 /// let`.
125 ///
126 /// **Known problems:** None.
127 ///
128 /// **Example:**
129 /// ```rust
130 /// for x in option { .. }
131 /// ```
132 ///
133 /// This should be
134 /// ```rust
135 /// if let Some(x) = option { .. }
136 /// ```
137 declare_lint! {
138     pub FOR_LOOP_OVER_OPTION,
139     Warn,
140     "for-looping over an `Option`, which is more clearly expressed as an `if let`"
141 }
142
143 /// **What it does:** Checks for `for` loops over `Result` values.
144 ///
145 /// **Why is this bad?** Readability. This is more clearly expressed as an `if
146 /// let`.
147 ///
148 /// **Known problems:** None.
149 ///
150 /// **Example:**
151 /// ```rust
152 /// for x in result { .. }
153 /// ```
154 ///
155 /// This should be
156 /// ```rust
157 /// if let Ok(x) = result { .. }
158 /// ```
159 declare_lint! {
160     pub FOR_LOOP_OVER_RESULT,
161     Warn,
162     "for-looping over a `Result`, which is more clearly expressed as an `if let`"
163 }
164
165 /// **What it does:** Detects `loop + match` combinations that are easier
166 /// written as a `while let` loop.
167 ///
168 /// **Why is this bad?** The `while let` loop is usually shorter and more
169 /// readable.
170 ///
171 /// **Known problems:** Sometimes the wrong binding is displayed (#383).
172 ///
173 /// **Example:**
174 /// ```rust
175 /// loop {
176 ///     let x = match y {
177 ///         Some(x) => x,
178 ///         None => break,
179 ///     }
180 ///     // .. do something with x
181 /// }
182 /// // is easier written as
183 /// while let Some(x) = y {
184 ///     // .. do something with x
185 /// }
186 /// ```
187 declare_lint! {
188     pub WHILE_LET_LOOP,
189     Warn,
190     "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
191 }
192
193 /// **What it does:** Checks for using `collect()` on an iterator without using
194 /// the result.
195 ///
196 /// **Why is this bad?** It is more idiomatic to use a `for` loop over the
197 /// iterator instead.
198 ///
199 /// **Known problems:** None.
200 ///
201 /// **Example:**
202 /// ```rust
203 /// vec.iter().map(|x| /* some operation returning () */).collect::<Vec<_>>();
204 /// ```
205 declare_lint! {
206     pub UNUSED_COLLECT,
207     Warn,
208     "`collect()`ing an iterator without using the result; this is usually better \
209      written as a for loop"
210 }
211
212 /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y`
213 /// are constant and `x` is greater or equal to `y`, unless the range is
214 /// reversed or has a negative `.step_by(_)`.
215 ///
216 /// **Why is it bad?** Such loops will either be skipped or loop until
217 /// wrap-around (in debug code, this may `panic!()`). Both options are probably
218 /// not intended.
219 ///
220 /// **Known problems:** The lint cannot catch loops over dynamically defined
221 /// ranges. Doing this would require simulating all possible inputs and code
222 /// paths through the program, which would be complex and error-prone.
223 ///
224 /// **Example:**
225 /// ```rust
226 /// for x in 5..10-5 { .. } // oops, stray `-`
227 /// ```
228 declare_lint! {
229     pub REVERSE_RANGE_LOOP,
230     Warn,
231     "iteration over an empty range, such as `10..0` or `5..5`"
232 }
233
234 /// **What it does:** Checks `for` loops over slices with an explicit counter
235 /// and suggests the use of `.enumerate()`.
236 ///
237 /// **Why is it bad?** Not only is the version using `.enumerate()` more
238 /// readable, the compiler is able to remove bounds checks which can lead to
239 /// faster code in some instances.
240 ///
241 /// **Known problems:** None.
242 ///
243 /// **Example:**
244 /// ```rust
245 /// for i in 0..v.len() { foo(v[i]);
246 /// for i in 0..v.len() { bar(i, v[i]); }
247 /// ```
248 declare_lint! {
249     pub EXPLICIT_COUNTER_LOOP,
250     Warn,
251     "for-looping with an explicit counter when `_.enumerate()` would do"
252 }
253
254 /// **What it does:** Checks for empty `loop` expressions.
255 ///
256 /// **Why is this bad?** Those busy loops burn CPU cycles without doing
257 /// anything. Think of the environment and either block on something or at least
258 /// make the thread sleep for some microseconds.
259 ///
260 /// **Known problems:** None.
261 ///
262 /// **Example:**
263 /// ```rust
264 /// loop {}
265 /// ```
266 declare_lint! {
267     pub EMPTY_LOOP,
268     Warn,
269     "empty `loop {}`, which should block or sleep"
270 }
271
272 /// **What it does:** Checks for `while let` expressions on iterators.
273 ///
274 /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys
275 /// the intent better.
276 ///
277 /// **Known problems:** None.
278 ///
279 /// **Example:**
280 /// ```rust
281 /// while let Some(val) = iter() { .. }
282 /// ```
283 declare_lint! {
284     pub WHILE_LET_ON_ITERATOR,
285     Warn,
286     "using a while-let loop instead of a for loop on an iterator"
287 }
288
289 /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and
290 /// ignoring either the keys or values.
291 ///
292 /// **Why is this bad?** Readability. There are `keys` and `values` methods that
293 /// can be used to express that don't need the values or keys.
294 ///
295 /// **Known problems:** None.
296 ///
297 /// **Example:**
298 /// ```rust
299 /// for (k, _) in &map { .. }
300 /// ```
301 ///
302 /// could be replaced by
303 ///
304 /// ```rust
305 /// for k in map.keys() { .. }
306 /// ```
307 declare_lint! {
308     pub FOR_KV_MAP,
309     Warn,
310     "looping on a map using `iter` when `keys` or `values` would do"
311 }
312
313 /// **What it does:** Checks for loops that will always `break`, `return` or
314 /// `continue` an outer loop.
315 ///
316 /// **Why is this bad?** This loop never loops, all it does is obfuscating the
317 /// code.
318 ///
319 /// **Known problems:** None
320 ///
321 /// **Example:**
322 /// ```rust
323 /// loop { ..; break; }
324 /// ```
325 declare_lint! {
326     pub NEVER_LOOP,
327     Warn,
328     "any loop that will always `break` or `return`"
329 }
330
331 #[derive(Copy, Clone)]
332 pub struct Pass;
333
334 impl LintPass for Pass {
335     fn get_lints(&self) -> LintArray {
336         lint_array!(
337             MANUAL_MEMCPY,
338             NEEDLESS_RANGE_LOOP,
339             EXPLICIT_ITER_LOOP,
340             EXPLICIT_INTO_ITER_LOOP,
341             ITER_NEXT_LOOP,
342             FOR_LOOP_OVER_RESULT,
343             FOR_LOOP_OVER_OPTION,
344             WHILE_LET_LOOP,
345             UNUSED_COLLECT,
346             REVERSE_RANGE_LOOP,
347             EXPLICIT_COUNTER_LOOP,
348             EMPTY_LOOP,
349             WHILE_LET_ON_ITERATOR,
350             FOR_KV_MAP,
351             NEVER_LOOP
352         )
353     }
354 }
355
356 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
357     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
358         if let Some((pat, arg, body)) = higher::for_loop(expr) {
359             check_for_loop(cx, pat, arg, body, expr);
360         }
361
362         // check for never_loop
363         match expr.node {
364             ExprWhile(_, ref block, _) |
365             ExprLoop(ref block, _, _) => {
366                 if never_loop(block, &expr.id) {
367                     span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
368                 }
369             },
370             _ => (),
371         }
372
373         // check for `loop { if let {} else break }` that could be `while let`
374         // (also matches an explicit "match" instead of "if let")
375         // (even if the "match" or "if let" is used for declaration)
376         if let ExprLoop(ref block, _, LoopSource::Loop) = expr.node {
377             // also check for empty `loop {}` statements
378             if block.stmts.is_empty() && block.expr.is_none() {
379                 span_lint(
380                     cx,
381                     EMPTY_LOOP,
382                     expr.span,
383                     "empty `loop {}` detected. You may want to either use `panic!()` or add \
384                      `std::thread::sleep(..);` to the loop body.",
385                 );
386             }
387
388             // extract the expression from the first statement (if any) in a block
389             let inner_stmt_expr = extract_expr_from_first_stmt(block);
390             // or extract the first expression (if any) from the block
391             if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
392                 if let ExprMatch(ref matchexpr, ref arms, ref source) = inner.node {
393                     // ensure "if let" compatible match structure
394                     match *source {
395                         MatchSource::Normal |
396                         MatchSource::IfLetDesugar { .. } => {
397                             if arms.len() == 2 && arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
398                                 arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
399                                 is_simple_break_expr(&arms[1].body)
400                             {
401                                 if in_external_macro(cx, expr.span) {
402                                     return;
403                                 }
404
405                                 // NOTE: we used to make build a body here instead of using
406                                 // ellipsis, this was removed because:
407                                 // 1) it was ugly with big bodies;
408                                 // 2) it was not indented properly;
409                                 // 3) it wasn’t very smart (see #675).
410                                 span_lint_and_sugg(
411                                     cx,
412                                     WHILE_LET_LOOP,
413                                     expr.span,
414                                     "this loop could be written as a `while let` loop",
415                                     "try",
416                                     format!(
417                                         "while let {} = {} {{ .. }}",
418                                         snippet(cx, arms[0].pats[0].span, ".."),
419                                         snippet(cx, matchexpr.span, "..")
420                                     ),
421                                 );
422                             }
423                         },
424                         _ => (),
425                     }
426                 }
427             }
428         }
429         if let ExprMatch(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
430             let pat = &arms[0].pats[0].node;
431             if let (&PatKind::TupleStruct(ref qpath, ref pat_args, _),
432                     &ExprMethodCall(ref method_path, _, ref method_args)) = (pat, &match_expr.node)
433             {
434                 let iter_expr = &method_args[0];
435                 let lhs_constructor = last_path_segment(qpath);
436                 if method_path.name == "next" && match_trait_method(cx, match_expr, &paths::ITERATOR) &&
437                     lhs_constructor.name == "Some" && !is_refutable(cx, &pat_args[0]) &&
438                     !is_iterator_used_after_while_let(cx, iter_expr) &&
439                     !is_nested(cx, expr, &method_args[0])
440                 {
441                     let iterator = snippet(cx, method_args[0].span, "_");
442                     let loop_var = snippet(cx, pat_args[0].span, "_");
443                     span_lint_and_sugg(
444                         cx,
445                         WHILE_LET_ON_ITERATOR,
446                         expr.span,
447                         "this loop could be written as a `for` loop",
448                         "try",
449                         format!("for {} in {} {{ .. }}", loop_var, iterator),
450                     );
451                 }
452             }
453         }
454     }
455
456     fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
457         if let StmtSemi(ref expr, _) = stmt.node {
458             if let ExprMethodCall(ref method, _, ref args) = expr.node {
459                 if args.len() == 1 && method.name == "collect" && match_trait_method(cx, expr, &paths::ITERATOR) {
460                     span_lint(
461                         cx,
462                         UNUSED_COLLECT,
463                         expr.span,
464                         "you are collect()ing an iterator and throwing away the result. \
465                          Consider using an explicit for loop to exhaust the iterator",
466                     );
467                 }
468             }
469         }
470     }
471 }
472
473 fn never_loop(block: &Block, id: &NodeId) -> bool {
474     !contains_continue_block(block, id) && loop_exit_block(block)
475 }
476
477 fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
478     block.stmts.iter().any(|e| contains_continue_stmt(e, dest)) ||
479         block.expr.as_ref().map_or(
480             false,
481             |e| contains_continue_expr(e, dest),
482         )
483 }
484
485 fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
486     match stmt.node {
487         StmtSemi(ref e, _) |
488         StmtExpr(ref e, _) => contains_continue_expr(e, dest),
489         StmtDecl(ref d, _) => contains_continue_decl(d, dest),
490     }
491 }
492
493 fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
494     match decl.node {
495         DeclLocal(ref local) => {
496             local.init.as_ref().map_or(
497                 false,
498                 |e| contains_continue_expr(e, dest),
499             )
500         },
501         _ => false,
502     }
503 }
504
505 fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
506     match expr.node {
507         ExprRet(Some(ref e)) |
508         ExprBox(ref e) |
509         ExprUnary(_, ref e) |
510         ExprCast(ref e, _) |
511         ExprType(ref e, _) |
512         ExprField(ref e, _) |
513         ExprTupField(ref e, _) |
514         ExprAddrOf(_, ref e) |
515         ExprRepeat(ref e, _) => contains_continue_expr(e, dest),
516         ExprArray(ref es) |
517         ExprMethodCall(_, _, ref es) |
518         ExprTup(ref es) => es.iter().any(|e| contains_continue_expr(e, dest)),
519         ExprCall(ref e, ref es) => {
520             contains_continue_expr(e, dest) || es.iter().any(|e| contains_continue_expr(e, dest))
521         },
522         ExprBinary(_, ref e1, ref e2) |
523         ExprAssign(ref e1, ref e2) |
524         ExprAssignOp(_, ref e1, ref e2) |
525         ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| contains_continue_expr(e, dest)),
526         ExprIf(ref e, ref e2, ref e3) => {
527             [e, e2].iter().chain(e3.as_ref().iter()).any(|e| {
528                 contains_continue_expr(e, dest)
529             })
530         },
531         ExprWhile(ref e, ref b, _) => contains_continue_expr(e, dest) || contains_continue_block(b, dest),
532         ExprMatch(ref e, ref arms, _) => {
533             contains_continue_expr(e, dest) || arms.iter().any(|a| contains_continue_expr(&a.body, dest))
534         },
535         ExprBlock(ref block) |
536         ExprLoop(ref block, ..) => contains_continue_block(block, dest),
537         ExprStruct(_, _, ref base) => {
538             base.as_ref().map_or(
539                 false,
540                 |e| contains_continue_expr(e, dest),
541             )
542         },
543         ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
544         _ => false,
545     }
546 }
547
548 fn loop_exit_block(block: &Block) -> bool {
549     block.stmts.iter().any(|e| loop_exit_stmt(e)) || block.expr.as_ref().map_or(false, |e| loop_exit_expr(e))
550 }
551
552 fn loop_exit_stmt(stmt: &Stmt) -> bool {
553     match stmt.node {
554         StmtSemi(ref e, _) |
555         StmtExpr(ref e, _) => loop_exit_expr(e),
556         StmtDecl(ref d, _) => loop_exit_decl(d),
557     }
558 }
559
560 fn loop_exit_decl(decl: &Decl) -> bool {
561     match decl.node {
562         DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| loop_exit_expr(e)),
563         _ => false,
564     }
565 }
566
567 fn loop_exit_expr(expr: &Expr) -> bool {
568     match expr.node {
569         ExprBox(ref e) |
570         ExprUnary(_, ref e) |
571         ExprCast(ref e, _) |
572         ExprType(ref e, _) |
573         ExprField(ref e, _) |
574         ExprTupField(ref e, _) |
575         ExprAddrOf(_, ref e) |
576         ExprRepeat(ref e, _) => loop_exit_expr(e),
577         ExprArray(ref es) |
578         ExprMethodCall(_, _, ref es) |
579         ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
580         ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)),
581         ExprBinary(_, ref e1, ref e2) |
582         ExprAssign(ref e1, ref e2) |
583         ExprAssignOp(_, ref e1, ref e2) |
584         ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| loop_exit_expr(e)),
585         ExprIf(ref e, ref e2, ref e3) => {
586             loop_exit_expr(e) || e3.as_ref().map_or(false, |e| loop_exit_expr(e)) && loop_exit_expr(e2)
587         },
588         ExprWhile(ref e, ref b, _) => loop_exit_expr(e) || loop_exit_block(b),
589         ExprMatch(ref e, ref arms, _) => loop_exit_expr(e) || arms.iter().all(|a| loop_exit_expr(&a.body)),
590         ExprBlock(ref b) => loop_exit_block(b),
591         ExprBreak(_, _) | ExprAgain(_) | ExprRet(_) => true,
592         _ => false,
593     }
594 }
595
596 fn check_for_loop<'a, 'tcx>(
597     cx: &LateContext<'a, 'tcx>,
598     pat: &'tcx Pat,
599     arg: &'tcx Expr,
600     body: &'tcx Expr,
601     expr: &'tcx Expr,
602 ) {
603     check_for_loop_range(cx, pat, arg, body, expr);
604     check_for_loop_reverse_range(cx, arg, expr);
605     check_for_loop_arg(cx, pat, arg, expr);
606     check_for_loop_explicit_counter(cx, arg, body, expr);
607     check_for_loop_over_map_kv(cx, pat, arg, body, expr);
608     detect_manual_memcpy(cx, pat, arg, body, expr);
609 }
610
611 fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: ast::NodeId) -> bool {
612     if_let_chain! {[
613         let ExprPath(ref qpath) = expr.node,
614         let QPath::Resolved(None, ref path) = *qpath,
615         path.segments.len() == 1,
616         let Def::Local(local_id) = cx.tables.qpath_def(qpath, expr.hir_id),
617         // our variable!
618         local_id == var
619     ], {
620         return true;
621     }}
622
623     false
624 }
625
626 struct Offset {
627     value: String,
628     negate: bool,
629 }
630
631 impl Offset {
632     fn negative(s: String) -> Self {
633         Self {
634             value: s,
635             negate: true,
636         }
637     }
638
639     fn positive(s: String) -> Self {
640         Self {
641             value: s,
642             negate: false,
643         }
644     }
645 }
646
647 struct FixedOffsetVar {
648     var_name: String,
649     offset: Offset,
650 }
651
652 fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty) -> bool {
653     let is_slice = match ty.sty {
654         ty::TyRef(_, ref subty) => is_slice_like(cx, subty.ty),
655         ty::TySlice(..) | ty::TyArray(..) => true,
656         _ => false,
657     };
658
659     is_slice || match_type(cx, ty, &paths::VEC) || match_type(cx, ty, &paths::VEC_DEQUE)
660 }
661
662 fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: ast::NodeId) -> Option<FixedOffsetVar> {
663     fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr, var: ast::NodeId) -> Option<String> {
664         match e.node {
665             ExprLit(ref l) => {
666                 match l.node {
667                     ast::LitKind::Int(x, _ty) => Some(x.to_string()),
668                     _ => None,
669                 }
670             },
671             ExprPath(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
672             _ => None,
673         }
674     }
675
676     if let ExprIndex(ref seqexpr, ref idx) = expr.node {
677         let ty = cx.tables.expr_ty(seqexpr);
678         if !is_slice_like(cx, ty) {
679             return None;
680         }
681
682         let offset = match idx.node {
683             ExprBinary(op, ref lhs, ref rhs) => {
684                 match op.node {
685                     BinOp_::BiAdd => {
686                         let offset_opt = if same_var(cx, lhs, var) {
687                             extract_offset(cx, rhs, var)
688                         } else if same_var(cx, rhs, var) {
689                             extract_offset(cx, lhs, var)
690                         } else {
691                             None
692                         };
693
694                         offset_opt.map(Offset::positive)
695                     },
696                     BinOp_::BiSub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
697                     _ => None,
698                 }
699             },
700             ExprPath(..) => {
701                 if same_var(cx, idx, var) {
702                     Some(Offset::positive("0".into()))
703                 } else {
704                     None
705                 }
706             },
707             _ => None,
708         };
709
710         offset.map(|o| {
711             FixedOffsetVar {
712                 var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
713                 offset: o,
714             }
715         })
716     } else {
717         None
718     }
719 }
720
721 fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
722     cx: &LateContext<'a, 'tcx>,
723     expr: &Expr,
724     var: ast::NodeId,
725 ) -> Option<FixedOffsetVar> {
726     if_let_chain! {[
727         let ExprMethodCall(ref method, _, ref args) = expr.node,
728         method.name == "clone",
729         args.len() == 1,
730         let Some(arg) = args.get(0),
731     ], {
732         return get_fixed_offset_var(cx, arg, var);
733     }}
734
735     get_fixed_offset_var(cx, expr, var)
736 }
737
738 fn get_indexed_assignments<'a, 'tcx>(
739     cx: &LateContext<'a, 'tcx>,
740     body: &Expr,
741     var: ast::NodeId,
742 ) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
743     fn get_assignment<'a, 'tcx>(
744         cx: &LateContext<'a, 'tcx>,
745         e: &Expr,
746         var: ast::NodeId,
747     ) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
748         if let Expr_::ExprAssign(ref lhs, ref rhs) = e.node {
749             match (get_fixed_offset_var(cx, lhs, var), fetch_cloned_fixed_offset_var(cx, rhs, var)) {
750                 (Some(offset_left), Some(offset_right)) => Some((offset_left, offset_right)),
751                 _ => None,
752             }
753         } else {
754             None
755         }
756     }
757
758     if let Expr_::ExprBlock(ref b) = body.node {
759         let Block {
760             ref stmts,
761             ref expr,
762             ..
763         } = **b;
764
765         stmts
766             .iter()
767             .map(|stmt| match stmt.node {
768                 Stmt_::StmtDecl(..) => None,
769                 Stmt_::StmtExpr(ref e, _node_id) |
770                 Stmt_::StmtSemi(ref e, _node_id) => Some(get_assignment(cx, e, var)),
771             })
772             .chain(expr.as_ref().into_iter().map(|e| {
773                 Some(get_assignment(cx, &*e, var))
774             }))
775             .filter_map(|op| op)
776             .collect::<Option<Vec<_>>>()
777             .unwrap_or_else(|| vec![])
778     } else {
779         get_assignment(cx, body, var).into_iter().collect()
780     }
781 }
782
783 /// Check for for loops that sequentially copy items from one slice-like
784 /// object to another.
785 fn detect_manual_memcpy<'a, 'tcx>(
786     cx: &LateContext<'a, 'tcx>,
787     pat: &'tcx Pat,
788     arg: &'tcx Expr,
789     body: &'tcx Expr,
790     expr: &'tcx Expr,
791 ) {
792     if let Some(higher::Range {
793                     start: Some(start),
794                     ref end,
795                     limits,
796                 }) = higher::range(arg)
797     {
798         // the var must be a single name
799         if let PatKind::Binding(_, canonical_id, _, _) = pat.node {
800             let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
801                 match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
802                     ("0", _, "0", _) => "".into(),
803                     ("0", _, x, false) |
804                     (x, false, "0", false) => x.into(),
805                     ("0", _, x, true) |
806                     (x, false, "0", true) => format!("-{}", x),
807                     (x, false, y, false) => format!("({} + {})", x, y),
808                     (x, false, y, true) => format!("({} - {})", x, y),
809                     (x, true, y, false) => format!("({} - {})", y, x),
810                     (x, true, y, true) => format!("-({} + {})", x, y),
811                 }
812             };
813
814             let print_limit = |end: &Option<&Expr>, offset: Offset, var_name: &str| if let Some(end) = *end {
815                 if_let_chain! {[
816                     let ExprMethodCall(ref method, _, ref len_args) = end.node,
817                     method.name == "len",
818                     len_args.len() == 1,
819                     let Some(arg) = len_args.get(0),
820                     snippet(cx, arg.span, "??") == var_name,
821                 ], {
822                     return if offset.negate {
823                         format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
824                     } else {
825                         "".to_owned()
826                     };
827                 }}
828
829                 let end_str = match limits {
830                     ast::RangeLimits::Closed => {
831                         let end = sugg::Sugg::hir(cx, end, "<count>");
832                         format!("{}", end + sugg::ONE)
833                     },
834                     ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
835                 };
836
837                 print_sum(&Offset::positive(end_str), &offset)
838             } else {
839                 "..".into()
840             };
841
842             // The only statements in the for loops can be indexed assignments from
843             // indexed retrievals.
844             let manual_copies = get_indexed_assignments(cx, body, canonical_id);
845
846             let big_sugg = manual_copies
847                 .into_iter()
848                 .map(|(dst_var, src_var)| {
849                     let start_str = Offset::positive(snippet_opt(cx, start.span).unwrap_or_else(|| "".into()));
850                     let dst_offset = print_sum(&start_str, &dst_var.offset);
851                     let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
852                     let src_offset = print_sum(&start_str, &src_var.offset);
853                     let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
854                     let dst = if dst_offset == "" && dst_limit == "" {
855                         dst_var.var_name
856                     } else {
857                         format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
858                     };
859
860                     format!("{}.clone_from_slice(&{}[{}..{}])", dst, src_var.var_name, src_offset, src_limit)
861                 })
862                 .join("\n    ");
863
864             if !big_sugg.is_empty() {
865                 span_lint_and_sugg(
866                     cx,
867                     MANUAL_MEMCPY,
868                     expr.span,
869                     "it looks like you're manually copying between slices",
870                     "try replacing the loop by",
871                     big_sugg,
872                 );
873             }
874         }
875     }
876 }
877
878 /// Check for looping over a range and then indexing a sequence with it.
879 /// The iteratee must be a range literal.
880 fn check_for_loop_range<'a, 'tcx>(
881     cx: &LateContext<'a, 'tcx>,
882     pat: &'tcx Pat,
883     arg: &'tcx Expr,
884     body: &'tcx Expr,
885     expr: &'tcx Expr,
886 ) {
887     if let Some(higher::Range {
888                     start: Some(start),
889                     ref end,
890                     limits,
891                 }) = higher::range(arg)
892     {
893         // the var must be a single name
894         if let PatKind::Binding(_, canonical_id, ref ident, _) = pat.node {
895             let mut visitor = VarVisitor {
896                 cx: cx,
897                 var: canonical_id,
898                 indexed: HashMap::new(),
899                 referenced: HashSet::new(),
900                 nonindex: false,
901             };
902             walk_expr(&mut visitor, body);
903
904             // linting condition: we only indexed one variable
905             if visitor.indexed.len() == 1 {
906                 let (indexed, indexed_extent) = visitor.indexed.into_iter().next().expect(
907                     "already checked that we have exactly 1 element",
908                 );
909
910                 // ensure that the indexed variable was declared before the loop, see #601
911                 if let Some(indexed_extent) = indexed_extent {
912                     let parent_id = cx.tcx.hir.get_parent(expr.id);
913                     let parent_def_id = cx.tcx.hir.local_def_id(parent_id);
914                     let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
915                     let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
916                     if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
917                         return;
918                     }
919                 }
920
921                 // don't lint if the container that is indexed into is also used without
922                 // indexing
923                 if visitor.referenced.contains(&indexed) {
924                     return;
925                 }
926
927                 let starts_at_zero = is_integer_literal(start, 0);
928
929                 let skip = if starts_at_zero {
930                     "".to_owned()
931                 } else {
932                     format!(".skip({})", snippet(cx, start.span, ".."))
933                 };
934
935                 let take = if let Some(end) = *end {
936                     if is_len_call(end, &indexed) {
937                         "".to_owned()
938                     } else {
939                         match limits {
940                             ast::RangeLimits::Closed => {
941                                 let end = sugg::Sugg::hir(cx, end, "<count>");
942                                 format!(".take({})", end + sugg::ONE)
943                             },
944                             ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, end.span, "..")),
945                         }
946                     }
947                 } else {
948                     "".to_owned()
949                 };
950
951                 if visitor.nonindex {
952                     span_lint_and_then(
953                         cx,
954                         NEEDLESS_RANGE_LOOP,
955                         expr.span,
956                         &format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
957                         |db| {
958                             multispan_sugg(
959                                 db,
960                                 "consider using an iterator".to_string(),
961                                 vec![
962                                     (pat.span, format!("({}, <item>)", ident.node)),
963                                     (arg.span, format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
964                                 ],
965                             );
966                         },
967                     );
968                 } else {
969                     let repl = if starts_at_zero && take.is_empty() {
970                         format!("&{}", indexed)
971                     } else {
972                         format!("{}.iter(){}{}", indexed, take, skip)
973                     };
974
975                     span_lint_and_then(
976                         cx,
977                         NEEDLESS_RANGE_LOOP,
978                         expr.span,
979                         &format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
980                         |db| {
981                             multispan_sugg(
982                                 db,
983                                 "consider using an iterator".to_string(),
984                                 vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
985                             );
986                         },
987                     );
988                 }
989             }
990         }
991     }
992 }
993
994 fn is_len_call(expr: &Expr, var: &Name) -> bool {
995     if_let_chain! {[
996         let ExprMethodCall(ref method, _, ref len_args) = expr.node,
997         len_args.len() == 1,
998         method.name == "len",
999         let ExprPath(QPath::Resolved(_, ref path)) = len_args[0].node,
1000         path.segments.len() == 1,
1001         path.segments[0].name == *var
1002     ], {
1003         return true;
1004     }}
1005
1006     false
1007 }
1008
1009 fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr, expr: &'tcx Expr) {
1010     // if this for loop is iterating over a two-sided range...
1011     if let Some(higher::Range {
1012                     start: Some(start),
1013                     end: Some(end),
1014                     limits,
1015                 }) = higher::range(arg)
1016     {
1017         // ...and both sides are compile-time constant integers...
1018         let parent_item = cx.tcx.hir.get_parent(arg.id);
1019         let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
1020         let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
1021         let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
1022         if let Ok(start_idx) = constcx.eval(start) {
1023             if let Ok(end_idx) = constcx.eval(end) {
1024                 // ...and the start index is greater than the end index,
1025                 // this loop will never run. This is often confusing for developers
1026                 // who think that this will iterate from the larger value to the
1027                 // smaller value.
1028                 let (sup, eq) = match (start_idx, end_idx) {
1029                     (&ty::Const { val: ConstVal::Integral(start_idx), .. },
1030                      &ty::Const { val: ConstVal::Integral(end_idx), .. }) => {
1031                         (start_idx > end_idx, start_idx == end_idx)
1032                     },
1033                     _ => (false, false),
1034                 };
1035
1036                 if sup {
1037                     let start_snippet = snippet(cx, start.span, "_");
1038                     let end_snippet = snippet(cx, end.span, "_");
1039                     let dots = if limits == ast::RangeLimits::Closed {
1040                         "..."
1041                     } else {
1042                         ".."
1043                     };
1044
1045                     span_lint_and_then(
1046                         cx,
1047                         REVERSE_RANGE_LOOP,
1048                         expr.span,
1049                         "this range is empty so this for loop will never run",
1050                         |db| {
1051                             db.span_suggestion(
1052                                 arg.span,
1053                                 "consider using the following if you are attempting to iterate over this \
1054                                  range in reverse",
1055                                 format!(
1056                                     "({end}{dots}{start}).rev()",
1057                                     end = end_snippet,
1058                                     dots = dots,
1059                                     start = start_snippet
1060                                 ),
1061                             );
1062                         },
1063                     );
1064                 } else if eq && limits != ast::RangeLimits::Closed {
1065                     // if they are equal, it's also problematic - this loop
1066                     // will never run.
1067                     span_lint(
1068                         cx,
1069                         REVERSE_RANGE_LOOP,
1070                         expr.span,
1071                         "this range is empty so this for loop will never run",
1072                     );
1073                 }
1074             }
1075         }
1076     }
1077 }
1078
1079 fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, method_name: &str) {
1080     let object = snippet(cx, args[0].span, "_");
1081     let muta = if method_name == "iter_mut" {
1082         "mut "
1083     } else {
1084         ""
1085     };
1086     span_lint_and_sugg(
1087         cx,
1088         EXPLICIT_ITER_LOOP,
1089         arg.span,
1090         "it is more idiomatic to loop over references to containers instead of using explicit \
1091          iteration methods",
1092         "to write this more concisely, try",
1093         format!("&{}{}", muta, object),
1094     )
1095 }
1096
1097 fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
1098     let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
1099     if let ExprMethodCall(ref method, _, ref args) = arg.node {
1100         // just the receiver, no arguments
1101         if args.len() == 1 {
1102             let method_name = &*method.name.as_str();
1103             // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
1104             if method_name == "iter" || method_name == "iter_mut" {
1105                 if is_ref_iterable_type(cx, &args[0]) {
1106                     lint_iter_method(cx, args, arg, method_name);
1107                 }
1108             } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
1109                 let def_id = cx.tables.type_dependent_defs()[arg.hir_id].def_id();
1110                 let substs = cx.tables.node_substs(arg.hir_id);
1111                 let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
1112
1113                 let fn_arg_tys = method_type.fn_sig(cx.tcx).inputs();
1114                 assert_eq!(fn_arg_tys.skip_binder().len(), 1);
1115                 if fn_arg_tys.skip_binder()[0].is_region_ptr() {
1116                     lint_iter_method(cx, args, arg, method_name);
1117                 } else {
1118                     let object = snippet(cx, args[0].span, "_");
1119                     span_lint_and_sugg(
1120                         cx,
1121                         EXPLICIT_INTO_ITER_LOOP,
1122                         arg.span,
1123                         "it is more idiomatic to loop over containers instead of using explicit \
1124                          iteration methods`",
1125                         "to write this more concisely, try",
1126                         object.to_string(),
1127                     );
1128                 }
1129             } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
1130                 span_lint(
1131                     cx,
1132                     ITER_NEXT_LOOP,
1133                     expr.span,
1134                     "you are iterating over `Iterator::next()` which is an Option; this will compile but is \
1135                      probably not what you want",
1136                 );
1137                 next_loop_linted = true;
1138             }
1139         }
1140     }
1141     if !next_loop_linted {
1142         check_arg_type(cx, pat, arg);
1143     }
1144 }
1145
1146 /// Check for `for` loops over `Option`s and `Results`
1147 fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
1148     let ty = cx.tables.expr_ty(arg);
1149     if match_type(cx, ty, &paths::OPTION) {
1150         span_help_and_lint(
1151             cx,
1152             FOR_LOOP_OVER_OPTION,
1153             arg.span,
1154             &format!(
1155                 "for loop over `{0}`, which is an `Option`. This is more readably written as an \
1156                  `if let` statement.",
1157                 snippet(cx, arg.span, "_")
1158             ),
1159             &format!(
1160                 "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
1161                 snippet(cx, pat.span, "_"),
1162                 snippet(cx, arg.span, "_")
1163             ),
1164         );
1165     } else if match_type(cx, ty, &paths::RESULT) {
1166         span_help_and_lint(
1167             cx,
1168             FOR_LOOP_OVER_RESULT,
1169             arg.span,
1170             &format!(
1171                 "for loop over `{0}`, which is a `Result`. This is more readably written as an \
1172                  `if let` statement.",
1173                 snippet(cx, arg.span, "_")
1174             ),
1175             &format!(
1176                 "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
1177                 snippet(cx, pat.span, "_"),
1178                 snippet(cx, arg.span, "_")
1179             ),
1180         );
1181     }
1182 }
1183
1184 fn check_for_loop_explicit_counter<'a, 'tcx>(
1185     cx: &LateContext<'a, 'tcx>,
1186     arg: &'tcx Expr,
1187     body: &'tcx Expr,
1188     expr: &'tcx Expr,
1189 ) {
1190     // Look for variables that are incremented once per loop iteration.
1191     let mut visitor = IncrementVisitor {
1192         cx: cx,
1193         states: HashMap::new(),
1194         depth: 0,
1195         done: false,
1196     };
1197     walk_expr(&mut visitor, body);
1198
1199     // For each candidate, check the parent block to see if
1200     // it's initialized to zero at the start of the loop.
1201     let map = &cx.tcx.hir;
1202     let parent_scope = map.get_enclosing_scope(expr.id).and_then(|id| {
1203         map.get_enclosing_scope(id)
1204     });
1205     if let Some(parent_id) = parent_scope {
1206         if let NodeBlock(block) = map.get(parent_id) {
1207             for (id, _) in visitor.states.iter().filter(
1208                 |&(_, v)| *v == VarState::IncrOnce,
1209             )
1210             {
1211                 let mut visitor2 = InitializeVisitor {
1212                     cx: cx,
1213                     end_expr: expr,
1214                     var_id: *id,
1215                     state: VarState::IncrOnce,
1216                     name: None,
1217                     depth: 0,
1218                     past_loop: false,
1219                 };
1220                 walk_block(&mut visitor2, block);
1221
1222                 if visitor2.state == VarState::Warn {
1223                     if let Some(name) = visitor2.name {
1224                         span_lint(
1225                             cx,
1226                             EXPLICIT_COUNTER_LOOP,
1227                             expr.span,
1228                             &format!(
1229                                 "the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
1230                                  item) in {1}.enumerate()` or similar iterators",
1231                                 name,
1232                                 snippet(cx, arg.span, "_")
1233                             ),
1234                         );
1235                     }
1236                 }
1237             }
1238         }
1239     }
1240 }
1241
1242 /// Check for the `FOR_KV_MAP` lint.
1243 fn check_for_loop_over_map_kv<'a, 'tcx>(
1244     cx: &LateContext<'a, 'tcx>,
1245     pat: &'tcx Pat,
1246     arg: &'tcx Expr,
1247     body: &'tcx Expr,
1248     expr: &'tcx Expr,
1249 ) {
1250     let pat_span = pat.span;
1251
1252     if let PatKind::Tuple(ref pat, _) = pat.node {
1253         if pat.len() == 2 {
1254             let arg_span = arg.span;
1255             let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).sty {
1256                 ty::TyRef(_, ref tam) => {
1257                     match (&pat[0].node, &pat[1].node) {
1258                         (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", tam.ty, tam.mutbl),
1259                         (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", tam.ty, MutImmutable),
1260                         _ => return,
1261                     }
1262                 },
1263                 _ => return,
1264             };
1265             let mutbl = match mutbl {
1266                 MutImmutable => "",
1267                 MutMutable => "_mut",
1268             };
1269             let arg = match arg.node {
1270                 ExprAddrOf(_, ref expr) => &**expr,
1271                 _ => arg,
1272             };
1273
1274             if match_type(cx, ty, &paths::HASHMAP) || match_type(cx, ty, &paths::BTREEMAP) {
1275                 span_lint_and_then(
1276                     cx,
1277                     FOR_KV_MAP,
1278                     expr.span,
1279                     &format!("you seem to want to iterate on a map's {}s", kind),
1280                     |db| {
1281                         let map = sugg::Sugg::hir(cx, arg, "map");
1282                         multispan_sugg(
1283                             db,
1284                             "use the corresponding method".into(),
1285                             vec![
1286                                 (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
1287                                 (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
1288                             ],
1289                         );
1290                     },
1291                 );
1292             }
1293         }
1294     }
1295 }
1296
1297 /// Return true if the pattern is a `PatWild` or an ident prefixed with `'_'`.
1298 fn pat_is_wild<'tcx>(pat: &'tcx PatKind, body: &'tcx Expr) -> bool {
1299     match *pat {
1300         PatKind::Wild => true,
1301         PatKind::Binding(_, _, ident, None) if ident.node.as_str().starts_with('_') => {
1302             let mut visitor = UsedVisitor {
1303                 var: ident.node,
1304                 used: false,
1305             };
1306             walk_expr(&mut visitor, body);
1307             !visitor.used
1308         },
1309         _ => false,
1310     }
1311 }
1312
1313 struct UsedVisitor {
1314     var: ast::Name, // var to look for
1315     used: bool, // has the var been used otherwise?
1316 }
1317
1318 impl<'tcx> Visitor<'tcx> for UsedVisitor {
1319     fn visit_expr(&mut self, expr: &'tcx Expr) {
1320         if match_var(expr, self.var) {
1321             self.used = true;
1322         } else {
1323             walk_expr(self, expr);
1324         }
1325     }
1326
1327     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1328         NestedVisitorMap::None
1329     }
1330 }
1331
1332 struct LocalUsedVisitor<'a, 'tcx: 'a> {
1333     cx: &'a LateContext<'a, 'tcx>,
1334     local: ast::NodeId,
1335     used: bool,
1336 }
1337
1338 impl<'a, 'tcx: 'a> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
1339     fn visit_expr(&mut self, expr: &'tcx Expr) {
1340         if same_var(self.cx, expr, self.local) {
1341             self.used = true;
1342         } else {
1343             walk_expr(self, expr);
1344         }
1345     }
1346
1347     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1348         NestedVisitorMap::None
1349     }
1350 }
1351
1352 struct VarVisitor<'a, 'tcx: 'a> {
1353     /// context reference
1354     cx: &'a LateContext<'a, 'tcx>,
1355     /// var name to look for as index
1356     var: ast::NodeId,
1357     /// indexed variables, the extend is `None` for global
1358     indexed: HashMap<Name, Option<region::Scope>>,
1359     /// Any names that are used outside an index operation.
1360     /// Used to detect things like `&mut vec` used together with `vec[i]`
1361     referenced: HashSet<Name>,
1362     /// has the loop variable been used in expressions other than the index of
1363     /// an index op?
1364     nonindex: bool,
1365 }
1366
1367 impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1368     fn visit_expr(&mut self, expr: &'tcx Expr) {
1369         if_let_chain! {[
1370             // an index op
1371             let ExprIndex(ref seqexpr, ref idx) = expr.node,
1372             // the indexed container is referenced by a name
1373             let ExprPath(ref seqpath) = seqexpr.node,
1374             let QPath::Resolved(None, ref seqvar) = *seqpath,
1375             seqvar.segments.len() == 1,
1376         ], {
1377             let index_used = same_var(self.cx, idx, self.var) || {
1378                 let mut used_visitor = LocalUsedVisitor {
1379                     cx: self.cx,
1380                     local: self.var,
1381                     used: false,
1382                 };
1383                 walk_expr(&mut used_visitor, idx);
1384                 used_visitor.used
1385             };
1386
1387             if index_used {
1388                 let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
1389                 match def {
1390                     Def::Local(node_id) | Def::Upvar(node_id, ..) => {
1391                         let hir_id = self.cx.tcx.hir.node_to_hir_id(node_id);
1392
1393                         let parent_id = self.cx.tcx.hir.get_parent(expr.id);
1394                         let parent_def_id = self.cx.tcx.hir.local_def_id(parent_id);
1395                         let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
1396                         self.indexed.insert(seqvar.segments[0].name, Some(extent));
1397                         return;  // no need to walk further *on the variable*
1398                     }
1399                     Def::Static(..) | Def::Const(..) => {
1400                         self.indexed.insert(seqvar.segments[0].name, None);
1401                         return;  // no need to walk further *on the variable*
1402                     }
1403                     _ => (),
1404                 }
1405             }
1406         }}
1407
1408         if_let_chain! {[
1409             // directly using a variable
1410             let ExprPath(ref qpath) = expr.node,
1411             let QPath::Resolved(None, ref path) = *qpath,
1412             path.segments.len() == 1,
1413             let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id),
1414         ], {
1415             if local_id == self.var {
1416                 // we are not indexing anything, record that
1417                 self.nonindex = true;
1418             } else {
1419                 // not the correct variable, but still a variable
1420                 self.referenced.insert(path.segments[0].name);
1421             }
1422         }}
1423
1424         walk_expr(self, expr);
1425     }
1426     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1427         NestedVisitorMap::None
1428     }
1429 }
1430
1431 fn is_iterator_used_after_while_let<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr) -> bool {
1432     let def_id = match var_def_id(cx, iter_expr) {
1433         Some(id) => id,
1434         None => return false,
1435     };
1436     let mut visitor = VarUsedAfterLoopVisitor {
1437         cx: cx,
1438         def_id: def_id,
1439         iter_expr_id: iter_expr.id,
1440         past_while_let: false,
1441         var_used_after_while_let: false,
1442     };
1443     if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
1444         walk_block(&mut visitor, enclosing_block);
1445     }
1446     visitor.var_used_after_while_let
1447 }
1448
1449 struct VarUsedAfterLoopVisitor<'a, 'tcx: 'a> {
1450     cx: &'a LateContext<'a, 'tcx>,
1451     def_id: NodeId,
1452     iter_expr_id: NodeId,
1453     past_while_let: bool,
1454     var_used_after_while_let: bool,
1455 }
1456
1457 impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
1458     fn visit_expr(&mut self, expr: &'tcx Expr) {
1459         if self.past_while_let {
1460             if Some(self.def_id) == var_def_id(self.cx, expr) {
1461                 self.var_used_after_while_let = true;
1462             }
1463         } else if self.iter_expr_id == expr.id {
1464             self.past_while_let = true;
1465         }
1466         walk_expr(self, expr);
1467     }
1468     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1469         NestedVisitorMap::None
1470     }
1471 }
1472
1473
1474 /// Return true if the type of expr is one that provides `IntoIterator` impls
1475 /// for `&T` and `&mut T`, such as `Vec`.
1476 #[cfg_attr(rustfmt, rustfmt_skip)]
1477 fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {
1478     // no walk_ptrs_ty: calling iter() on a reference can make sense because it
1479     // will allow further borrows afterwards
1480     let ty = cx.tables.expr_ty(e);
1481     is_iterable_array(ty) ||
1482     match_type(cx, ty, &paths::VEC) ||
1483     match_type(cx, ty, &paths::LINKED_LIST) ||
1484     match_type(cx, ty, &paths::HASHMAP) ||
1485     match_type(cx, ty, &paths::HASHSET) ||
1486     match_type(cx, ty, &paths::VEC_DEQUE) ||
1487     match_type(cx, ty, &paths::BINARY_HEAP) ||
1488     match_type(cx, ty, &paths::BTREEMAP) ||
1489     match_type(cx, ty, &paths::BTREESET)
1490 }
1491
1492 fn is_iterable_array(ty: Ty) -> bool {
1493     // IntoIterator is currently only implemented for array sizes <= 32 in rustc
1494     match ty.sty {
1495         ty::TyArray(_, n) => (0...32).contains(const_to_u64(n)),
1496         _ => false,
1497     }
1498 }
1499
1500 /// If a block begins with a statement (possibly a `let` binding) and has an
1501 /// expression, return it.
1502 fn extract_expr_from_first_stmt(block: &Block) -> Option<&Expr> {
1503     if block.stmts.is_empty() {
1504         return None;
1505     }
1506     if let StmtDecl(ref decl, _) = block.stmts[0].node {
1507         if let DeclLocal(ref local) = decl.node {
1508             if let Some(ref expr) = local.init {
1509                 Some(expr)
1510             } else {
1511                 None
1512             }
1513         } else {
1514             None
1515         }
1516     } else {
1517         None
1518     }
1519 }
1520
1521 /// If a block begins with an expression (with or without semicolon), return it.
1522 fn extract_first_expr(block: &Block) -> Option<&Expr> {
1523     match block.expr {
1524         Some(ref expr) if block.stmts.is_empty() => Some(expr),
1525         None if !block.stmts.is_empty() => {
1526             match block.stmts[0].node {
1527                 StmtExpr(ref expr, _) |
1528                 StmtSemi(ref expr, _) => Some(expr),
1529                 StmtDecl(..) => None,
1530             }
1531         },
1532         _ => None,
1533     }
1534 }
1535
1536 /// Return true if expr contains a single break expr without destination label
1537 /// and
1538 /// passed expression. The expression may be within a block.
1539 fn is_simple_break_expr(expr: &Expr) -> bool {
1540     match expr.node {
1541         ExprBreak(dest, ref passed_expr) if dest.ident.is_none() && passed_expr.is_none() => true,
1542         ExprBlock(ref b) => {
1543             match extract_first_expr(b) {
1544                 Some(subexpr) => is_simple_break_expr(subexpr),
1545                 None => false,
1546             }
1547         },
1548         _ => false,
1549     }
1550 }
1551
1552 // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
1553 // incremented exactly once in the loop body, and initialized to zero
1554 // at the start of the loop.
1555 #[derive(PartialEq)]
1556 enum VarState {
1557     Initial, // Not examined yet
1558     IncrOnce, // Incremented exactly once, may be a loop counter
1559     Declared, // Declared but not (yet) initialized to zero
1560     Warn,
1561     DontWarn,
1562 }
1563
1564 /// Scan a for loop for variables that are incremented exactly once.
1565 struct IncrementVisitor<'a, 'tcx: 'a> {
1566     cx: &'a LateContext<'a, 'tcx>, // context reference
1567     states: HashMap<NodeId, VarState>, // incremented variables
1568     depth: u32, // depth of conditional expressions
1569     done: bool,
1570 }
1571
1572 impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
1573     fn visit_expr(&mut self, expr: &'tcx Expr) {
1574         if self.done {
1575             return;
1576         }
1577
1578         // If node is a variable
1579         if let Some(def_id) = var_def_id(self.cx, expr) {
1580             if let Some(parent) = get_parent_expr(self.cx, expr) {
1581                 let state = self.states.entry(def_id).or_insert(VarState::Initial);
1582
1583                 match parent.node {
1584                     ExprAssignOp(op, ref lhs, ref rhs) => {
1585                         if lhs.id == expr.id {
1586                             if op.node == BiAdd && is_integer_literal(rhs, 1) {
1587                                 *state = match *state {
1588                                     VarState::Initial if self.depth == 0 => VarState::IncrOnce,
1589                                     _ => VarState::DontWarn,
1590                                 };
1591                             } else {
1592                                 // Assigned some other value
1593                                 *state = VarState::DontWarn;
1594                             }
1595                         }
1596                     },
1597                     ExprAssign(ref lhs, _) if lhs.id == expr.id => *state = VarState::DontWarn,
1598                     ExprAddrOf(mutability, _) if mutability == MutMutable => *state = VarState::DontWarn,
1599                     _ => (),
1600                 }
1601             }
1602         } else if is_loop(expr) {
1603             self.states.clear();
1604             self.done = true;
1605             return;
1606         } else if is_conditional(expr) {
1607             self.depth += 1;
1608             walk_expr(self, expr);
1609             self.depth -= 1;
1610             return;
1611         }
1612         walk_expr(self, expr);
1613     }
1614     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1615         NestedVisitorMap::None
1616     }
1617 }
1618
1619 /// Check whether a variable is initialized to zero at the start of a loop.
1620 struct InitializeVisitor<'a, 'tcx: 'a> {
1621     cx: &'a LateContext<'a, 'tcx>, // context reference
1622     end_expr: &'tcx Expr, // the for loop. Stop scanning here.
1623     var_id: NodeId,
1624     state: VarState,
1625     name: Option<Name>,
1626     depth: u32, // depth of conditional expressions
1627     past_loop: bool,
1628 }
1629
1630 impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
1631     fn visit_decl(&mut self, decl: &'tcx Decl) {
1632         // Look for declarations of the variable
1633         if let DeclLocal(ref local) = decl.node {
1634             if local.pat.id == self.var_id {
1635                 if let PatKind::Binding(_, _, ref ident, _) = local.pat.node {
1636                     self.name = Some(ident.node);
1637
1638                     self.state = if let Some(ref init) = local.init {
1639                         if is_integer_literal(init, 0) {
1640                             VarState::Warn
1641                         } else {
1642                             VarState::Declared
1643                         }
1644                     } else {
1645                         VarState::Declared
1646                     }
1647                 }
1648             }
1649         }
1650         walk_decl(self, decl);
1651     }
1652
1653     fn visit_expr(&mut self, expr: &'tcx Expr) {
1654         if self.state == VarState::DontWarn {
1655             return;
1656         }
1657         if expr == self.end_expr {
1658             self.past_loop = true;
1659             return;
1660         }
1661         // No need to visit expressions before the variable is
1662         // declared
1663         if self.state == VarState::IncrOnce {
1664             return;
1665         }
1666
1667         // If node is the desired variable, see how it's used
1668         if var_def_id(self.cx, expr) == Some(self.var_id) {
1669             if let Some(parent) = get_parent_expr(self.cx, expr) {
1670                 match parent.node {
1671                     ExprAssignOp(_, ref lhs, _) if lhs.id == expr.id => {
1672                         self.state = VarState::DontWarn;
1673                     },
1674                     ExprAssign(ref lhs, ref rhs) if lhs.id == expr.id => {
1675                         self.state = if is_integer_literal(rhs, 0) && self.depth == 0 {
1676                             VarState::Warn
1677                         } else {
1678                             VarState::DontWarn
1679                         }
1680                     },
1681                     ExprAddrOf(mutability, _) if mutability == MutMutable => self.state = VarState::DontWarn,
1682                     _ => (),
1683                 }
1684             }
1685
1686             if self.past_loop {
1687                 self.state = VarState::DontWarn;
1688                 return;
1689             }
1690         } else if !self.past_loop && is_loop(expr) {
1691             self.state = VarState::DontWarn;
1692             return;
1693         } else if is_conditional(expr) {
1694             self.depth += 1;
1695             walk_expr(self, expr);
1696             self.depth -= 1;
1697             return;
1698         }
1699         walk_expr(self, expr);
1700     }
1701     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1702         NestedVisitorMap::None
1703     }
1704 }
1705
1706 fn var_def_id(cx: &LateContext, expr: &Expr) -> Option<NodeId> {
1707     if let ExprPath(ref qpath) = expr.node {
1708         let path_res = cx.tables.qpath_def(qpath, expr.hir_id);
1709         if let Def::Local(node_id) = path_res {
1710             return Some(node_id);
1711         }
1712     }
1713     None
1714 }
1715
1716 fn is_loop(expr: &Expr) -> bool {
1717     match expr.node {
1718         ExprLoop(..) | ExprWhile(..) => true,
1719         _ => false,
1720     }
1721 }
1722
1723 fn is_conditional(expr: &Expr) -> bool {
1724     match expr.node {
1725         ExprIf(..) | ExprMatch(..) => true,
1726         _ => false,
1727     }
1728 }
1729
1730 fn is_nested(cx: &LateContext, match_expr: &Expr, iter_expr: &Expr) -> bool {
1731     if_let_chain! {[
1732         let Some(loop_block) = get_enclosing_block(cx, match_expr.id),
1733         let Some(map::Node::NodeExpr(loop_expr)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(loop_block.id)),
1734     ], {
1735         return is_loop_nested(cx, loop_expr, iter_expr)
1736     }}
1737     false
1738 }
1739
1740 fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool {
1741     let mut id = loop_expr.id;
1742     let iter_name = if let Some(name) = path_name(iter_expr) {
1743         name
1744     } else {
1745         return true;
1746     };
1747     loop {
1748         let parent = cx.tcx.hir.get_parent_node(id);
1749         if parent == id {
1750             return false;
1751         }
1752         match cx.tcx.hir.find(parent) {
1753             Some(NodeExpr(expr)) => {
1754                 match expr.node {
1755                     ExprLoop(..) | ExprWhile(..) => {
1756                         return true;
1757                     },
1758                     _ => (),
1759                 }
1760             },
1761             Some(NodeBlock(block)) => {
1762                 let mut block_visitor = LoopNestVisitor {
1763                     id: id,
1764                     iterator: iter_name,
1765                     nesting: Unknown,
1766                 };
1767                 walk_block(&mut block_visitor, block);
1768                 if block_visitor.nesting == RuledOut {
1769                     return false;
1770                 }
1771             },
1772             Some(NodeStmt(_)) => (),
1773             _ => {
1774                 return false;
1775             },
1776         }
1777         id = parent;
1778     }
1779 }
1780
1781 #[derive(PartialEq, Eq)]
1782 enum Nesting {
1783     Unknown, // no nesting detected yet
1784     RuledOut, // the iterator is initialized or assigned within scope
1785     LookFurther, // no nesting detected, no further walk required
1786 }
1787
1788 use self::Nesting::{LookFurther, RuledOut, Unknown};
1789
1790 struct LoopNestVisitor {
1791     id: NodeId,
1792     iterator: Name,
1793     nesting: Nesting,
1794 }
1795
1796 impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
1797     fn visit_stmt(&mut self, stmt: &'tcx Stmt) {
1798         if stmt.node.id() == self.id {
1799             self.nesting = LookFurther;
1800         } else if self.nesting == Unknown {
1801             walk_stmt(self, stmt);
1802         }
1803     }
1804
1805     fn visit_expr(&mut self, expr: &'tcx Expr) {
1806         if self.nesting != Unknown {
1807             return;
1808         }
1809         if expr.id == self.id {
1810             self.nesting = LookFurther;
1811             return;
1812         }
1813         match expr.node {
1814             ExprAssign(ref path, _) |
1815             ExprAssignOp(_, ref path, _) => {
1816                 if match_var(path, self.iterator) {
1817                     self.nesting = RuledOut;
1818                 }
1819             },
1820             _ => walk_expr(self, expr),
1821         }
1822     }
1823
1824     fn visit_pat(&mut self, pat: &'tcx Pat) {
1825         if self.nesting != Unknown {
1826             return;
1827         }
1828         if let PatKind::Binding(_, _, span_name, _) = pat.node {
1829             if self.iterator == span_name.node {
1830                 self.nesting = RuledOut;
1831                 return;
1832             }
1833         }
1834         walk_pat(self, pat)
1835     }
1836
1837     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
1838         NestedVisitorMap::None
1839     }
1840 }
1841
1842 fn path_name(e: &Expr) -> Option<Name> {
1843     if let ExprPath(QPath::Resolved(_, ref path)) = e.node {
1844         let segments = &path.segments;
1845         if segments.len() == 1 {
1846             return Some(segments[0].name);
1847         }
1848     };
1849     None
1850 }