]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ranges.rs
Merge branch 'master' into move_links
[rust.git] / clippy_lints / src / ranges.rs
1 use rustc::lint::*;
2 use rustc::hir::*;
3 use utils::{is_integer_literal, paths, snippet, span_lint};
4 use utils::{higher, implements_trait, get_trait_def_id};
5
6 /// **What it does:** Checks for calling `.step_by(0)` on iterators,
7 /// which never terminates.
8 ///
9 /// **Why is this bad?** This very much looks like an oversight, since with
10 /// `loop { .. }` there is an obvious better way to endlessly loop.
11 ///
12 /// **Known problems:** None.
13 ///
14 /// **Example:**
15 /// ```rust
16 /// for x in (5..5).step_by(0) { .. }
17 /// ```
18 declare_lint! {
19     pub ITERATOR_STEP_BY_ZERO,
20     Warn,
21     "using `Iterator::step_by(0)`, which produces an infinite iterator"
22 }
23
24 /// **What it does:** Checks for zipping a collection with the range of
25 /// `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(ref path, _, ref args) = expr.node {
53             let name = path.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(ref iter_path, _, ref iter_args ) = *iter,
75                     iter_path.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(ref len_path, _, ref len_args) = end.node,
81                     len_path.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).map_or(false, |iterator_trait| implements_trait(cx, ty, iterator_trait, &[]))
104 }