]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ranges.rs
Merge pull request #1837 from Manishearth/step_by
[rust.git] / clippy_lints / src / ranges.rs
1 use rustc::lint::*;
2 use rustc::hir::*;
3 use syntax::codemap::Spanned;
4 use utils::{is_integer_literal, paths, snippet, span_lint};
5 use utils::{higher, implements_trait, get_trait_def_id};
6
7 /// **What it does:** Checks for calling `.step_by(0)` on iterators,
8 /// which never terminates.
9 ///
10 /// **Why is this bad?** This very much looks like an oversight, since with
11 /// `loop { .. }` there is an obvious better way to endlessly loop.
12 ///
13 /// **Known problems:** None.
14 ///
15 /// **Example:**
16 /// ```rust
17 /// for x in (5..5).step_by(0) { .. }
18 /// ```
19 declare_lint! {
20     pub ITERATOR_STEP_BY_ZERO,
21     Warn,
22     "using `Iterator::step_by(0)`, which produces an infinite iterator"
23 }
24
25 /// **What it does:** Checks for zipping a collection with the range of `0.._.len()`.
26 ///
27 /// **Why is this bad?** The code is better expressed with `.enumerate()`.
28 ///
29 /// **Known problems:** None.
30 ///
31 /// **Example:**
32 /// ```rust
33 /// x.iter().zip(0..x.len())
34 /// ```
35 declare_lint! {
36     pub RANGE_ZIP_WITH_LEN,
37     Warn,
38     "zipping iterator with a range when `enumerate()` would do"
39 }
40
41 #[derive(Copy,Clone)]
42 pub struct StepByZero;
43
44 impl LintPass for StepByZero {
45     fn get_lints(&self) -> LintArray {
46         lint_array!(ITERATOR_STEP_BY_ZERO, RANGE_ZIP_WITH_LEN)
47     }
48 }
49
50 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
51     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
52         if let ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) = expr.node {
53             let name = name.as_str();
54
55             // Range with step_by(0).
56             if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) {
57                 use consts::{Constant, constant};
58                 use rustc_const_math::ConstInt::Usize;
59                 if let Some((Constant::Int(Usize(us)), _)) = constant(cx, &args[1]) {
60                     if us.as_u64(cx.sess().target.uint_type) == 0 {
61                         span_lint(
62                             cx,
63                             ITERATOR_STEP_BY_ZERO,
64                             expr.span,
65                             "Iterator::step_by(0) will panic at runtime",
66                         );
67                     }
68                 }
69             } else if name == "zip" && args.len() == 2 {
70                 let iter = &args[0].node;
71                 let zip_arg = &args[1];
72                 if_let_chain! {[
73                     // .iter() call
74                     let ExprMethodCall( Spanned { node: iter_name, .. }, _, ref iter_args ) = *iter,
75                     iter_name == "iter",
76                     // range expression in .zip() call: 0..x.len()
77                     let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg),
78                     is_integer_literal(start, 0),
79                     // .len() call
80                     let ExprMethodCall(Spanned { node: len_name, .. }, _, ref len_args) = end.node,
81                     len_name == "len" && len_args.len() == 1,
82                     // .iter() and .len() called on same Path
83                     let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node,
84                     let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node,
85                     iter_path.segments == len_path.segments
86                  ], {
87                      span_lint(cx,
88                                RANGE_ZIP_WITH_LEN,
89                                expr.span,
90                                &format!("It is more idiomatic to use {}.iter().enumerate()",
91                                         snippet(cx, iter_args[0].span, "_")));
92                 }}
93             }
94         }
95     }
96 }
97
98 fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
99     // No need for walk_ptrs_ty here because step_by moves self, so it
100     // can't be called on a borrowed range.
101     let ty = cx.tables.expr_ty_adjusted(expr);
102
103     get_trait_def_id(cx, &paths::ITERATOR)
104         .map_or(
105             false,
106             |iterator_trait| implements_trait(cx, ty, iterator_trait, &[])
107         )
108 }