-use rustc::hir::*;
-use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
-use rustc::{declare_lint_pass, declare_tool_lint};
+use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::sym;
use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
-use lazy_static::lazy_static;
-use syntax::symbol::Symbol;
declare_clippy_lint! {
/// **What it does:** Checks for iteration that is guaranteed to be infinite.
///
/// **Example:**
/// ```rust
- /// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
+ /// let infinite_iter = 0..;
+ /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
/// ```
pub MAYBE_INFINITE_ITER,
pedantic,
declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InfiniteIter {
- fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
+impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (lint, msg) = match complete_infinite_iter(cx, expr) {
Infinite => (INFINITE_ITER, "infinite iteration detected"),
MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
use self::Finiteness::{Finite, Infinite, MaybeInfinite};
impl Finiteness {
+ #[must_use]
fn and(self, b: Self) -> Self {
match (self, b) {
(Finite, _) | (_, Finite) => Finite,
}
}
+ #[must_use]
fn or(self, b: Self) -> Self {
match (self, b) {
(Infinite, _) | (_, Infinite) => Infinite,
}
impl From<bool> for Finiteness {
+ #[must_use]
fn from(b: bool) -> Self {
- if b {
- Infinite
- } else {
- Finite
- }
+ if b { Infinite } else { Finite }
}
}
use self::Heuristic::{All, Always, Any, First};
-lazy_static! {
/// 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`.
-static ref HEURISTICS: [(Symbol, usize, Heuristic, Finiteness); 19] = [
- (*sym::zip, 2, All, Infinite),
- (*sym::chain, 2, Any, Infinite),
- (*sym::cycle, 1, Always, Infinite),
- (*sym::map, 2, First, Infinite),
- (*sym::by_ref, 1, First, Infinite),
- (*sym::cloned, 1, First, Infinite),
- (*sym::rev, 1, First, Infinite),
- (*sym::inspect, 1, First, Infinite),
- (*sym::enumerate, 1, First, Infinite),
- (*sym::peekable, 2, First, Infinite),
- (*sym::fuse, 1, First, Infinite),
- (*sym::skip, 2, First, Infinite),
- (*sym::skip_while, 1, First, Infinite),
- (*sym::filter, 2, First, Infinite),
- (*sym::filter_map, 2, First, Infinite),
- (*sym::flat_map, 2, First, Infinite),
- (*sym::unzip, 1, First, Infinite),
- (*sym::take_while, 2, First, MaybeInfinite),
- (*sym::scan, 3, First, MaybeInfinite),
+const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
+ ("zip", 2, All, Infinite),
+ ("chain", 2, Any, Infinite),
+ ("cycle", 1, Always, Infinite),
+ ("map", 2, First, Infinite),
+ ("by_ref", 1, First, Infinite),
+ ("cloned", 1, First, Infinite),
+ ("rev", 1, First, Infinite),
+ ("inspect", 1, First, Infinite),
+ ("enumerate", 1, First, Infinite),
+ ("peekable", 2, First, Infinite),
+ ("fuse", 1, First, Infinite),
+ ("skip", 2, First, Infinite),
+ ("skip_while", 1, First, Infinite),
+ ("filter", 2, First, Infinite),
+ ("filter_map", 2, First, Infinite),
+ ("flat_map", 2, First, Infinite),
+ ("unzip", 1, First, Infinite),
+ ("take_while", 2, First, MaybeInfinite),
+ ("scan", 3, First, MaybeInfinite),
];
-}
-fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
- match expr.node {
- ExprKind::MethodCall(ref method, _, ref args) => {
- for &(name, len, heuristic, cap) in HEURISTICS.iter() {
- if method.ident.name == name && args.len() == len {
+fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
+ match expr.kind {
+ ExprKind::MethodCall(ref method, _, ref args, _) => {
+ for &(name, len, heuristic, cap) in &HEURISTICS {
+ if method.ident.name.as_str() == name && args.len() == len {
return (match heuristic {
Always => Infinite,
First => is_infinite(cx, &args[0]),
.and(cap);
}
}
- if method.ident.name == *sym::flat_map && args.len() == 2 {
- if let ExprKind::Closure(_, _, body_id, _, _) = args[1].node {
+ if method.ident.name == sym!(flat_map) && args.len() == 2 {
+ if let ExprKind::Closure(_, _, body_id, _, _) = args[1].kind {
let body = cx.tcx.hir().body(body_id);
return is_infinite(cx, &body.value);
}
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::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e),
ExprKind::Call(ref path, _) => {
- if let ExprKind::Path(ref qpath) = path.node {
- match_qpath(qpath, &*paths::REPEAT).into()
+ if let ExprKind::Path(ref qpath) = path.kind {
+ match_qpath(qpath, &paths::REPEAT).into()
} else {
Finite
}
},
- ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(),
+ ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite,
}
}
-lazy_static! {
/// the names and argument lengths of methods that *may* exhaust their
/// iterators
-static ref POSSIBLY_COMPLETING_METHODS: [(Symbol, usize); 6] = [
- (*sym::find, 2),
- (*sym::rfind, 2),
- (*sym::position, 2),
- (*sym::rposition, 2),
- (*sym::any, 2),
- (*sym::all, 2),
+const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
+ ("find", 2),
+ ("rfind", 2),
+ ("position", 2),
+ ("rposition", 2),
+ ("any", 2),
+ ("all", 2),
];
/// the names and argument lengths of methods that *always* exhaust
/// their iterators
-static ref COMPLETING_METHODS: [(Symbol, usize); 12] = [
- (*sym::count, 1),
- (*sym::fold, 3),
- (*sym::for_each, 2),
- (*sym::partition, 2),
- (*sym::max, 1),
- (*sym::max_by, 2),
- (*sym::max_by_key, 2),
- (*sym::min, 1),
- (*sym::min_by, 2),
- (*sym::min_by_key, 2),
- (*sym::sum, 1),
- (*sym::product, 1),
+const COMPLETING_METHODS: [(&str, usize); 12] = [
+ ("count", 1),
+ ("fold", 3),
+ ("for_each", 2),
+ ("partition", 2),
+ ("max", 1),
+ ("max_by", 2),
+ ("max_by_key", 2),
+ ("min", 1),
+ ("min_by", 2),
+ ("min_by_key", 2),
+ ("sum", 1),
+ ("product", 1),
];
/// the paths of types that are known to be infinitely allocating
-static ref INFINITE_COLLECTORS: [Vec<Symbol>; 8] = [
- paths::BINARY_HEAP.to_vec(),
- paths::BTREEMAP.to_vec(),
- paths::BTREESET.to_vec(),
- paths::HASHMAP.to_vec(),
- paths::HASHSET.to_vec(),
- paths::LINKED_LIST.to_vec(),
- paths::VEC.to_vec(),
- paths::VEC_DEQUE.to_vec(),
+const INFINITE_COLLECTORS: [&[&str]; 8] = [
+ &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 {
- ExprKind::MethodCall(ref method, _, ref args) => {
- for &(name, len) in COMPLETING_METHODS.iter() {
- if method.ident.name == name && args.len() == len {
+fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
+ match expr.kind {
+ ExprKind::MethodCall(ref method, _, ref args, _) => {
+ for &(name, len) in &COMPLETING_METHODS {
+ if method.ident.name.as_str() == name && args.len() == len {
return is_infinite(cx, &args[0]);
}
}
- for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
- if method.ident.name == name && args.len() == len {
+ for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
+ if method.ident.name.as_str() == name && args.len() == len {
return MaybeInfinite.and(is_infinite(cx, &args[0]));
}
}
- if method.ident.name == *sym::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 method.ident.name == sym!(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.typeck_results().expr_ty(&args[0]), id, &[])
+ });
if not_double_ended {
return is_infinite(cx, &args[0]);
}
- } else if method.ident.name == *sym::collect {
- let ty = cx.tables.expr_ty(expr);
+ } else if method.ident.name == sym!(collect) {
+ let ty = cx.typeck_results().expr_ty(expr);
if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) {
return is_infinite(cx, &args[0]);
}