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