]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/infinite_iter.rs
Auto merge of #3946 - rchaser53:issue-3920, r=flip1995
[rust.git] / clippy_lints / src / infinite_iter.rs
index cb31c1cd044a0862ae12af6dfc0cc45a812cf8e0..689cd8fd3b05eefd2783af72428cc6523b392e02 100644 (file)
@@ -1,37 +1,41 @@
 use rustc::hir::*;
-use rustc::lint::*;
-use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, paths, span_lint};
-
-/// **What it does:** Checks for iteration that is guaranteed to be infinite.
-///
-/// **Why is this bad?** While there may be places where this is acceptable
-/// (e.g. in event streams), in most cases this is simply an error.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-/// ```rust
-/// repeat(1_u8).iter().collect::<Vec<_>>()
-/// ```
+use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
+use rustc::{declare_tool_lint, lint_array};
+
+use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
+
 declare_clippy_lint! {
+    /// **What it does:** Checks for iteration that is guaranteed to be infinite.
+    ///
+    /// **Why is this bad?** While there may be places where this is acceptable
+    /// (e.g., in event streams), in most cases this is simply an error.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```no_run
+    /// use std::iter;
+    ///
+    /// iter::repeat(1_u8).collect::<Vec<_>>();
+    /// ```
     pub INFINITE_ITER,
     correctness,
     "infinite iteration"
 }
 
-/// **What it does:** Checks for iteration that may be infinite.
-///
-/// **Why is this bad?** While there may be places where this is acceptable
-/// (e.g. in event streams), in most cases this is simply an error.
-///
-/// **Known problems:** The code may have a condition to stop iteration, but
-/// this lint is not clever enough to analyze it.
-///
-/// **Example:**
-/// ```rust
-/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
-/// ```
 declare_clippy_lint! {
+    /// **What it does:** Checks for iteration that may be infinite.
+    ///
+    /// **Why is this bad?** While there may be places where this is acceptable
+    /// (e.g., in event streams), in most cases this is simply an error.
+    ///
+    /// **Known problems:** The code may have a condition to stop iteration, but
+    /// this lint is not clever enough to analyze it.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
+    /// ```
     pub MAYBE_INFINITE_ITER,
     pedantic,
     "possible infinite iteration"
@@ -44,6 +48,10 @@ impl LintPass for Pass {
     fn get_lints(&self) -> LintArray {
         lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
     }
+
+    fn name(&self) -> &'static str {
+        "InfiniteIter"
+    }
 }
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
@@ -115,8 +123,8 @@ enum Heuristic {
 /// a slice of (method name, number of args, heuristic, bounds) tuples
 /// that will be used to determine whether the method in question
 /// returns an infinite or possibly infinite iterator. The finiteness
-/// is an upper bound, e.g. some methods can return a possibly
-/// infinite iterator at worst, e.g. `take_while`.
+/// is an upper bound, e.g., some methods can return a possibly
+/// infinite iterator at worst, e.g., `take_while`.
 static HEURISTICS: &[(&str, usize, Heuristic, Finiteness)] = &[
     ("zip", 2, All, Infinite),
     ("chain", 2, Any, Infinite),
@@ -139,37 +147,38 @@ enum Heuristic {
     ("scan", 3, First, MaybeInfinite),
 ];
 
-fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
+fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
     match expr.node {
-        ExprMethodCall(ref method, _, ref args) => {
+        ExprKind::MethodCall(ref method, _, ref args) => {
             for &(name, len, heuristic, cap) in HEURISTICS.iter() {
-                if method.name == name && args.len() == len {
+                if method.ident.name == name && args.len() == len {
                     return (match heuristic {
                         Always => Infinite,
                         First => is_infinite(cx, &args[0]),
                         Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
                         All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
-                    }).and(cap);
+                    })
+                    .and(cap);
                 }
             }
-            if method.name == "flat_map" && args.len() == 2 {
-                if let ExprClosure(_, _, body_id, _, _) = args[1].node {
-                    let body = cx.tcx.hir.body(body_id);
+            if method.ident.name == "flat_map" && args.len() == 2 {
+                if let ExprKind::Closure(_, _, body_id, _, _) = args[1].node {
+                    let body = cx.tcx.hir().body(body_id);
                     return is_infinite(cx, &body.value);
                 }
             }
             Finite
         },
-        ExprBlock(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
-        ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
-        ExprCall(ref path, _) => if let ExprPath(ref qpath) = path.node {
-            match_qpath(qpath, &paths::REPEAT).into()
-        } else {
-            Finite
+        ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
+        ExprKind::Box(ref e) | ExprKind::AddrOf(_, ref e) => is_infinite(cx, e),
+        ExprKind::Call(ref path, _) => {
+            if let ExprKind::Path(ref qpath) = path.node {
+                match_qpath(qpath, &paths::REPEAT).into()
+            } else {
+                Finite
+            }
         },
-        ExprStruct(..) => higher::range(cx, expr)
-            .map_or(false, |r| r.end.is_none())
-            .into(),
+        ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(),
         _ => Finite,
     }
 }
@@ -189,7 +198,6 @@ fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
 /// their iterators
 static COMPLETING_METHODS: &[(&str, usize)] = &[
     ("count", 1),
-    ("collect", 1),
     ("fold", 3),
     ("for_each", 2),
     ("partition", 2),
@@ -203,32 +211,49 @@ fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
     ("product", 1),
 ];
 
-fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> Finiteness {
+/// the paths of types that are known to be infinitely allocating
+static INFINITE_COLLECTORS: &[&[&str]] = &[
+    &paths::BINARY_HEAP,
+    &paths::BTREEMAP,
+    &paths::BTREESET,
+    &paths::HASHMAP,
+    &paths::HASHSET,
+    &paths::LINKED_LIST,
+    &paths::VEC,
+    &paths::VEC_DEQUE,
+];
+
+fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
     match expr.node {
-        ExprMethodCall(ref method, _, ref args) => {
+        ExprKind::MethodCall(ref method, _, ref args) => {
             for &(name, len) in COMPLETING_METHODS.iter() {
-                if method.name == name && args.len() == len {
+                if method.ident.name == name && args.len() == len {
                     return is_infinite(cx, &args[0]);
                 }
             }
             for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
-                if method.name == name && args.len() == len {
+                if method.ident.name == name && args.len() == len {
                     return MaybeInfinite.and(is_infinite(cx, &args[0]));
                 }
             }
-            if method.name == "last" && args.len() == 1 {
+            if method.ident.name == "last" && args.len() == 1 {
                 let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR)
                     .map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[]));
                 if not_double_ended {
                     return is_infinite(cx, &args[0]);
                 }
+            } else if method.ident.name == "collect" {
+                let ty = cx.tables.expr_ty(expr);
+                if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) {
+                    return is_infinite(cx, &args[0]);
+                }
             }
         },
-        ExprBinary(op, ref l, ref r) => if op.node.is_comparison() {
-            return is_infinite(cx, l)
-                .and(is_infinite(cx, r))
-                .and(MaybeInfinite);
-        }, // TODO: ExprLoop + Match
+        ExprKind::Binary(op, ref l, ref r) => {
+            if op.node.is_comparison() {
+                return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
+            }
+        }, // TODO: ExprKind::Loop + Match
         _ => (),
     }
     Finite