]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ranges.rs
Merge pull request #1392 from oli-obk/rustfmt
[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, match_type, paths, snippet, span_lint};
5 use utils::higher;
6
7 /// **What it does:** Checks for iterating over ranges with a `.step_by(0)`,
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 RANGE_STEP_BY_ZERO,
21     Warn,
22     "using `Range::step_by(0)`, which produces an infinite iterator"
23 }
24 /// **What it does:** Checks for zipping a collection with the range of `0.._.len()`.
25 ///
26 /// **Why is this bad?** The code is better expressed with `.enumerate()`.
27 ///
28 /// **Known problems:** None.
29 ///
30 /// **Example:**
31 /// ```rust
32 /// x.iter().zip(0..x.len())
33 /// ```
34 declare_lint! {
35     pub RANGE_ZIP_WITH_LEN,
36     Warn,
37     "zipping iterator with a range when `enumerate()` would do"
38 }
39
40 #[derive(Copy,Clone)]
41 pub struct StepByZero;
42
43 impl LintPass for StepByZero {
44     fn get_lints(&self) -> LintArray {
45         lint_array!(RANGE_STEP_BY_ZERO, RANGE_ZIP_WITH_LEN)
46     }
47 }
48
49 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
50     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
51         if let ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) = expr.node {
52             let name = &*name.as_str();
53
54             // Range with step_by(0).
55             if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) && is_integer_literal(&args[1], 0) {
56                 span_lint(cx,
57                           RANGE_STEP_BY_ZERO,
58                           expr.span,
59                           "Range::step_by(0) produces an infinite iterator. Consider using `std::iter::repeat()` \
60                            instead");
61             } else if name == "zip" && args.len() == 2 {
62                 let iter = &args[0].node;
63                 let zip_arg = &args[1];
64                 if_let_chain! {[
65                     // .iter() call
66                     let ExprMethodCall( Spanned { node: ref iter_name, .. }, _, ref iter_args ) = *iter,
67                     &*iter_name.as_str() == "iter",
68                     // range expression in .zip() call: 0..x.len()
69                     let Some(higher::Range { start: Some(ref start), end: Some(ref end), .. }) = higher::range(zip_arg),
70                     is_integer_literal(start, 0),
71                     // .len() call
72                     let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = end.node,
73                     &*len_name.as_str() == "len" && len_args.len() == 1,
74                     // .iter() and .len() called on same Path
75                     let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node,
76                     let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node,
77                     iter_path.segments == len_path.segments
78                  ], {
79                      span_lint(cx,
80                                RANGE_ZIP_WITH_LEN,
81                                expr.span,
82                                &format!("It is more idiomatic to use {}.iter().enumerate()",
83                                         snippet(cx, iter_args[0].span, "_")));
84                 }}
85             }
86         }
87     }
88 }
89
90 fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
91     // No need for walk_ptrs_ty here because step_by moves self, so it
92     // can't be called on a borrowed range.
93     let ty = cx.tcx.tables().expr_ty(expr);
94
95     // Note: `RangeTo`, `RangeToInclusive` and `RangeFull` don't have step_by
96     match_type(cx, ty, &paths::RANGE) || match_type(cx, ty, &paths::RANGE_FROM) ||
97     match_type(cx, ty, &paths::RANGE_INCLUSIVE)
98 }