]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ranges.rs
Rustup to rustc 1.15.0-nightly (0ed951993 2016-11-14)
[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 LateLintPass for StepByZero {
50     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
51         if let ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) = expr.node {
52             // Range with step_by(0).
53             if name.as_str() == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) &&
54                is_integer_literal(&args[1], 0) {
55                 span_lint(cx,
56                           RANGE_STEP_BY_ZERO,
57                           expr.span,
58                           "Range::step_by(0) produces an infinite iterator. Consider using `std::iter::repeat()` \
59                            instead");
60             } else if name.as_str() == "zip" && args.len() == 2 {
61                 let iter = &args[0].node;
62                 let zip_arg = &args[1];
63                 if_let_chain! {[
64                     // .iter() call
65                     let ExprMethodCall( Spanned { node: ref iter_name, .. }, _, ref iter_args ) = *iter,
66                     iter_name.as_str() == "iter",
67                     // range expression in .zip() call: 0..x.len()
68                     let Some(higher::Range { start: Some(ref start), end: Some(ref end), .. }) = higher::range(zip_arg),
69                     is_integer_literal(start, 0),
70                     // .len() call
71                     let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = end.node,
72                     len_name.as_str() == "len" && len_args.len() == 1,
73                     // .iter() and .len() called on same Path
74                     let ExprPath(_, Path { segments: ref iter_path, .. }) = iter_args[0].node,
75                     let ExprPath(_, Path { segments: ref len_path, .. }) = len_args[0].node,
76                     iter_path == len_path
77                  ], {
78                     span_lint(cx,
79                               RANGE_ZIP_WITH_LEN,
80                               expr.span,
81                               &format!("It is more idiomatic to use {}.iter().enumerate()",
82                                        snippet(cx, iter_args[0].span, "_")));
83                 }}
84             }
85         }
86     }
87 }
88
89 fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
90     // No need for walk_ptrs_ty here because step_by moves self, so it
91     // can't be called on a borrowed range.
92     let ty = cx.tcx.tables().expr_ty(expr);
93
94     // Note: `RangeTo`, `RangeToInclusive` and `RangeFull` don't have step_by
95     match_type(cx, ty, &paths::RANGE)
96         || match_type(cx, ty, &paths::RANGE_FROM)
97         || match_type(cx, ty, &paths::RANGE_INCLUSIVE)
98 }