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"
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 {
/// 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),
("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,
}
}
/// their iterators
static COMPLETING_METHODS: &[(&str, usize)] = &[
("count", 1),
- ("collect", 1),
("fold", 3),
("for_each", 2),
("partition", 2),
("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