]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ranges.rs
Merge pull request #1860 from Vurich/master
[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(cx,
62                                   ITERATOR_STEP_BY_ZERO,
63                                   expr.span,
64                                   "Iterator::step_by(0) will panic at runtime");
65                     }
66                 }
67             } else if name == "zip" && args.len() == 2 {
68                 let iter = &args[0].node;
69                 let zip_arg = &args[1];
70                 if_let_chain! {[
71                     // .iter() call
72                     let ExprMethodCall( Spanned { node: iter_name, .. }, _, ref iter_args ) = *iter,
73                     iter_name == "iter",
74                     // range expression in .zip() call: 0..x.len()
75                     let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg),
76                     is_integer_literal(start, 0),
77                     // .len() call
78                     let ExprMethodCall(Spanned { node: len_name, .. }, _, ref len_args) = end.node,
79                     len_name == "len" && len_args.len() == 1,
80                     // .iter() and .len() called on same Path
81                     let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node,
82                     let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node,
83                     iter_path.segments == len_path.segments
84                  ], {
85                      span_lint(cx,
86                                RANGE_ZIP_WITH_LEN,
87                                expr.span,
88                                &format!("It is more idiomatic to use {}.iter().enumerate()",
89                                         snippet(cx, iter_args[0].span, "_")));
90                 }}
91             }
92         }
93     }
94 }
95
96 fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
97     // No need for walk_ptrs_ty here because step_by moves self, so it
98     // can't be called on a borrowed range.
99     let ty = cx.tables.expr_ty_adjusted(expr);
100
101     get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |iterator_trait| implements_trait(cx, ty, iterator_trait, &[]))
102 }