about summary refs log tree commit diff
path: root/clippy_lints/src/methods
diff options
context:
space:
mode:
authorMichael Wright <mikerite@lavabit.com>2019-12-18 18:59:43 +0200
committerMichael Wright <mikerite@lavabit.com>2019-12-18 18:59:43 +0200
commite097fca4df2ff70e0213d747a408d109db16c5d2 (patch)
treeeb83c0eb013149226fde9b155053d51f77ce9583 /clippy_lints/src/methods
parentc62396dbf442839fc725d353ef85306da1667caf (diff)
downloadrust-e097fca4df2ff70e0213d747a408d109db16c5d2.tar.gz
rust-e097fca4df2ff70e0213d747a408d109db16c5d2.zip
Update iterator_step_by_zero
Move `iterator_step_by_zero` into `methods` since it applies to all
iterators and not just ranges. Simplify the code while doing so.
Diffstat (limited to 'clippy_lints/src/methods')
-rw-r--r--clippy_lints/src/methods/mod.rs36
1 files changed, 36 insertions, 0 deletions
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index ca62e7ea9d2..8e94cc0f002 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -738,6 +738,26 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint! {
+    /// **What it does:** Checks for calling `.step_by(0)` on iterators,
+    /// which never terminates.
+    ///
+    /// **Why is this bad?** This very much looks like an oversight, since with
+    /// `loop { .. }` there is an obvious better way to endlessly loop.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```ignore
+    /// for x in (5..5).step_by(0) {
+    ///     ..
+    /// }
+    /// ```
+    pub ITERATOR_STEP_BY_ZERO,
+    correctness,
+    "using `Iterator::step_by(0)`, which produces an infinite iterator"
+}
+
+declare_clippy_lint! {
     /// **What it does:** Checks for use of `.iter().nth()` (and the related
     /// `.iter_mut().nth()`) on standard library types with O(1) element access.
     ///
@@ -1115,6 +1135,7 @@ declare_lint_pass!(Methods => [
     FLAT_MAP_IDENTITY,
     FIND_MAP,
     MAP_FLATTEN,
+    ITERATOR_STEP_BY_ZERO,
     ITER_NTH,
     ITER_SKIP_NEXT,
     GET_UNWRAP,
@@ -1173,6 +1194,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
             },
             ["nth", "iter"] => lint_iter_nth(cx, expr, arg_lists[1], false),
             ["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true),
+            ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
             ["next", "skip"] => lint_iter_skip_next(cx, expr),
             ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
             ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
@@ -1950,6 +1972,20 @@ fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr, fold_args:
     }
 }
 
+fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, args: &'tcx [hir::Expr]) {
+    if match_trait_method(cx, expr, &paths::ITERATOR) {
+        use crate::consts::{constant, Constant};
+        if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &args[1]) {
+            span_lint(
+                cx,
+                ITERATOR_STEP_BY_ZERO,
+                expr.span,
+                "Iterator::step_by(0) will panic at runtime",
+            );
+        }
+    }
+}
+
 fn lint_iter_nth<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_args: &'tcx [hir::Expr], is_mut: bool) {
     let mut_str = if is_mut { "_mut" } else { "" };
     let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {