2021-03-25 18:29:11 +00:00
|
|
|
use clippy_utils::diagnostics::span_lint;
|
2022-10-06 07:44:38 +00:00
|
|
|
use clippy_utils::higher;
|
2021-07-29 10:16:06 +00:00
|
|
|
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
2022-06-30 10:18:51 +00:00
|
|
|
use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
|
2020-01-12 06:08:41 +00:00
|
|
|
use rustc_lint::{LateContext, LateLintPass};
|
2023-11-25 17:45:27 +00:00
|
|
|
use rustc_session::declare_lint_pass;
|
2021-07-29 10:16:06 +00:00
|
|
|
use rustc_span::symbol::{sym, Symbol};
|
2017-08-25 20:20:52 +00:00
|
|
|
|
2018-03-28 13:24:26 +00:00
|
|
|
declare_clippy_lint! {
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### What it does
|
|
|
|
/// Checks for iteration that is guaranteed to be infinite.
|
2019-03-05 16:50:33 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Why is this bad?
|
|
|
|
/// While there may be places where this is acceptable
|
2019-01-31 01:15:29 +00:00
|
|
|
/// (e.g., in event streams), in most cases this is simply an error.
|
2019-03-05 16:50:33 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Example
|
2019-03-05 22:23:50 +00:00
|
|
|
/// ```no_run
|
|
|
|
/// use std::iter;
|
|
|
|
///
|
|
|
|
/// iter::repeat(1_u8).collect::<Vec<_>>();
|
2019-03-05 16:50:33 +00:00
|
|
|
/// ```
|
2021-12-06 11:33:31 +00:00
|
|
|
#[clippy::version = "pre 1.29.0"]
|
2017-08-25 20:20:52 +00:00
|
|
|
pub INFINITE_ITER,
|
2018-03-28 13:24:26 +00:00
|
|
|
correctness,
|
2017-08-25 20:20:52 +00:00
|
|
|
"infinite iteration"
|
|
|
|
}
|
|
|
|
|
2018-03-28 13:24:26 +00:00
|
|
|
declare_clippy_lint! {
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### What it does
|
|
|
|
/// Checks for iteration that may be infinite.
|
2019-03-05 16:50:33 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Why is this bad?
|
|
|
|
/// While there may be places where this is acceptable
|
2019-01-31 01:15:29 +00:00
|
|
|
/// (e.g., in event streams), in most cases this is simply an error.
|
2019-03-05 16:50:33 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Known problems
|
|
|
|
/// The code may have a condition to stop iteration, but
|
2019-03-05 16:50:33 +00:00
|
|
|
/// this lint is not clever enough to analyze it.
|
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Example
|
2023-10-23 13:49:18 +00:00
|
|
|
/// ```no_run
|
2019-08-03 16:42:05 +00:00
|
|
|
/// let infinite_iter = 0..;
|
2022-06-16 15:39:06 +00:00
|
|
|
/// # #[allow(unused)]
|
2019-08-03 16:42:05 +00:00
|
|
|
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
|
2019-03-05 16:50:33 +00:00
|
|
|
/// ```
|
2021-12-06 11:33:31 +00:00
|
|
|
#[clippy::version = "pre 1.29.0"]
|
2017-08-25 20:20:52 +00:00
|
|
|
pub MAYBE_INFINITE_ITER,
|
2018-03-28 13:24:26 +00:00
|
|
|
pedantic,
|
2017-08-25 20:20:52 +00:00
|
|
|
"possible infinite iteration"
|
|
|
|
}
|
|
|
|
|
2019-04-08 20:43:55 +00:00
|
|
|
declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
|
2017-08-25 20:20:52 +00:00
|
|
|
|
2020-06-25 20:41:36 +00:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
|
|
|
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
2017-08-25 20:20:52 +00:00
|
|
|
let (lint, msg) = match complete_infinite_iter(cx, expr) {
|
2017-08-26 16:46:42 +00:00
|
|
|
Infinite => (INFINITE_ITER, "infinite iteration detected"),
|
2017-09-03 21:15:15 +00:00
|
|
|
MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
|
|
|
|
Finite => {
|
|
|
|
return;
|
2022-09-09 11:36:26 +00:00
|
|
|
},
|
2017-08-25 20:20:52 +00:00
|
|
|
};
|
2021-06-03 06:41:37 +00:00
|
|
|
span_lint(cx, lint, expr.span, msg);
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
2017-08-26 16:46:42 +00:00
|
|
|
enum Finiteness {
|
|
|
|
Infinite,
|
|
|
|
MaybeInfinite,
|
2017-09-03 21:15:15 +00:00
|
|
|
Finite,
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 09:33:04 +00:00
|
|
|
use self::Finiteness::{Finite, Infinite, MaybeInfinite};
|
2017-08-25 20:20:52 +00:00
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
impl Finiteness {
|
2019-09-18 06:37:41 +00:00
|
|
|
#[must_use]
|
2017-08-25 20:20:52 +00:00
|
|
|
fn and(self, b: Self) -> Self {
|
|
|
|
match (self, b) {
|
2017-08-26 16:46:42 +00:00
|
|
|
(Finite, _) | (_, Finite) => Finite,
|
2017-09-05 09:33:04 +00:00
|
|
|
(MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
|
2017-09-03 21:15:15 +00:00
|
|
|
_ => Infinite,
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 06:37:41 +00:00
|
|
|
#[must_use]
|
2017-08-25 20:20:52 +00:00
|
|
|
fn or(self, b: Self) -> Self {
|
|
|
|
match (self, b) {
|
2017-08-26 16:46:42 +00:00
|
|
|
(Infinite, _) | (_, Infinite) => Infinite,
|
2017-09-05 09:33:04 +00:00
|
|
|
(MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
|
2017-09-03 21:15:15 +00:00
|
|
|
_ => Finite,
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
impl From<bool> for Finiteness {
|
2019-09-18 06:37:41 +00:00
|
|
|
#[must_use]
|
2017-08-25 20:20:52 +00:00
|
|
|
fn from(b: bool) -> Self {
|
2021-03-12 14:30:50 +00:00
|
|
|
if b { Infinite } else { Finite }
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
/// This tells us what to look for to know if the iterator returned by
|
|
|
|
/// this method is infinite
|
2017-08-25 20:20:52 +00:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
enum Heuristic {
|
2017-08-26 16:46:42 +00:00
|
|
|
/// infinite no matter what
|
2017-08-25 20:20:52 +00:00
|
|
|
Always,
|
2017-08-26 16:46:42 +00:00
|
|
|
/// infinite if the first argument is
|
2017-08-25 20:20:52 +00:00
|
|
|
First,
|
2017-08-26 16:46:42 +00:00
|
|
|
/// infinite if any of the supplied arguments is
|
2017-08-25 20:20:52 +00:00
|
|
|
Any,
|
2017-08-26 16:46:42 +00:00
|
|
|
/// infinite if all of the supplied arguments are
|
2017-09-03 21:15:15 +00:00
|
|
|
All,
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 09:33:04 +00:00
|
|
|
use self::Heuristic::{All, Always, Any, First};
|
2017-08-25 20:20:52 +00:00
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
/// 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
|
2019-01-31 01:15:29 +00:00
|
|
|
/// is an upper bound, e.g., some methods can return a possibly
|
|
|
|
/// infinite iterator at worst, e.g., `take_while`.
|
2019-05-17 21:53:54 +00:00
|
|
|
const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
|
2022-09-01 09:43:35 +00:00
|
|
|
("zip", 1, All, Infinite),
|
|
|
|
("chain", 1, Any, Infinite),
|
|
|
|
("cycle", 0, Always, Infinite),
|
|
|
|
("map", 1, First, Infinite),
|
|
|
|
("by_ref", 0, First, Infinite),
|
|
|
|
("cloned", 0, First, Infinite),
|
|
|
|
("rev", 0, First, Infinite),
|
|
|
|
("inspect", 0, First, Infinite),
|
|
|
|
("enumerate", 0, First, Infinite),
|
|
|
|
("peekable", 1, First, Infinite),
|
|
|
|
("fuse", 0, First, Infinite),
|
|
|
|
("skip", 1, First, Infinite),
|
|
|
|
("skip_while", 0, First, Infinite),
|
|
|
|
("filter", 1, First, Infinite),
|
|
|
|
("filter_map", 1, First, Infinite),
|
|
|
|
("flat_map", 1, First, Infinite),
|
|
|
|
("unzip", 0, First, Infinite),
|
|
|
|
("take_while", 1, First, MaybeInfinite),
|
|
|
|
("scan", 2, First, MaybeInfinite),
|
2017-08-25 20:20:52 +00:00
|
|
|
];
|
|
|
|
|
2020-06-25 20:41:36 +00:00
|
|
|
fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
2019-09-27 15:16:06 +00:00
|
|
|
match expr.kind {
|
2022-09-01 09:43:35 +00:00
|
|
|
ExprKind::MethodCall(method, receiver, args, _) => {
|
2019-05-17 23:34:52 +00:00
|
|
|
for &(name, len, heuristic, cap) in &HEURISTICS {
|
2019-05-17 21:53:54 +00:00
|
|
|
if method.ident.name.as_str() == name && args.len() == len {
|
2017-08-25 20:20:52 +00:00
|
|
|
return (match heuristic {
|
2017-09-05 09:33:04 +00:00
|
|
|
Always => Infinite,
|
2022-09-01 09:43:35 +00:00
|
|
|
First => is_infinite(cx, receiver),
|
|
|
|
Any => is_infinite(cx, receiver).or(is_infinite(cx, &args[0])),
|
|
|
|
All => is_infinite(cx, receiver).and(is_infinite(cx, &args[0])),
|
2018-11-27 20:14:15 +00:00
|
|
|
})
|
|
|
|
.and(cap);
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-01 09:43:35 +00:00
|
|
|
if method.ident.name == sym!(flat_map) && args.len() == 1 {
|
|
|
|
if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
|
2022-06-11 19:25:25 +00:00
|
|
|
let body = cx.tcx.hir().body(body);
|
2022-09-09 11:36:26 +00:00
|
|
|
return is_infinite(cx, body.value);
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-26 16:46:42 +00:00
|
|
|
Finite
|
2017-08-25 20:20:52 +00:00
|
|
|
},
|
2021-04-08 15:50:13 +00:00
|
|
|
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
|
2023-03-14 17:18:26 +00:00
|
|
|
ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
|
2022-10-06 07:44:38 +00:00
|
|
|
ExprKind::Call(path, _) => {
|
|
|
|
if let ExprKind::Path(ref qpath) = path.kind {
|
|
|
|
cx.qpath_res(qpath, path.hir_id)
|
|
|
|
.opt_def_id()
|
|
|
|
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
|
|
|
|
.into()
|
|
|
|
} else {
|
|
|
|
Finite
|
|
|
|
}
|
|
|
|
},
|
2021-08-08 14:49:13 +00:00
|
|
|
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
|
2017-09-03 21:15:15 +00:00
|
|
|
_ => Finite,
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
/// the names and argument lengths of methods that *may* exhaust their
|
|
|
|
/// iterators
|
2019-05-17 21:53:54 +00:00
|
|
|
const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
|
2022-09-01 09:43:35 +00:00
|
|
|
("find", 1),
|
|
|
|
("rfind", 1),
|
|
|
|
("position", 1),
|
|
|
|
("rposition", 1),
|
|
|
|
("any", 1),
|
|
|
|
("all", 1),
|
2017-08-25 20:20:52 +00:00
|
|
|
];
|
|
|
|
|
2017-08-26 16:46:42 +00:00
|
|
|
/// the names and argument lengths of methods that *always* exhaust
|
|
|
|
/// their iterators
|
2019-05-17 21:53:54 +00:00
|
|
|
const COMPLETING_METHODS: [(&str, usize); 12] = [
|
2022-09-01 09:43:35 +00:00
|
|
|
("count", 0),
|
|
|
|
("fold", 2),
|
|
|
|
("for_each", 1),
|
|
|
|
("partition", 1),
|
|
|
|
("max", 0),
|
|
|
|
("max_by", 1),
|
|
|
|
("max_by_key", 1),
|
|
|
|
("min", 0),
|
|
|
|
("min_by", 1),
|
|
|
|
("min_by_key", 1),
|
|
|
|
("sum", 0),
|
|
|
|
("product", 0),
|
2017-08-25 20:20:52 +00:00
|
|
|
];
|
|
|
|
|
2018-12-31 11:06:08 +00:00
|
|
|
/// the paths of types that are known to be infinitely allocating
|
2021-07-29 10:16:06 +00:00
|
|
|
const INFINITE_COLLECTORS: &[Symbol] = &[
|
|
|
|
sym::BinaryHeap,
|
|
|
|
sym::BTreeMap,
|
|
|
|
sym::BTreeSet,
|
2021-10-02 23:51:01 +00:00
|
|
|
sym::HashMap,
|
|
|
|
sym::HashSet,
|
2021-07-29 10:16:06 +00:00
|
|
|
sym::LinkedList,
|
2021-10-02 23:51:01 +00:00
|
|
|
sym::Vec,
|
|
|
|
sym::VecDeque,
|
2018-12-31 11:06:08 +00:00
|
|
|
];
|
|
|
|
|
2020-06-25 20:41:36 +00:00
|
|
|
fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
2019-09-27 15:16:06 +00:00
|
|
|
match expr.kind {
|
2022-09-01 09:43:35 +00:00
|
|
|
ExprKind::MethodCall(method, receiver, args, _) => {
|
2019-05-17 23:34:52 +00:00
|
|
|
for &(name, len) in &COMPLETING_METHODS {
|
2019-05-17 21:53:54 +00:00
|
|
|
if method.ident.name.as_str() == name && args.len() == len {
|
2022-09-01 09:43:35 +00:00
|
|
|
return is_infinite(cx, receiver);
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:34:52 +00:00
|
|
|
for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
|
2019-05-17 21:53:54 +00:00
|
|
|
if method.ident.name.as_str() == name && args.len() == len {
|
2022-09-01 09:43:35 +00:00
|
|
|
return MaybeInfinite.and(is_infinite(cx, receiver));
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-02 13:48:14 +00:00
|
|
|
if method.ident.name == sym!(last) && args.is_empty() {
|
2022-09-09 11:36:26 +00:00
|
|
|
let not_double_ended = cx
|
|
|
|
.tcx
|
|
|
|
.get_diagnostic_item(sym::DoubleEndedIterator)
|
|
|
|
.map_or(false, |id| {
|
2022-09-01 09:43:35 +00:00
|
|
|
!implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
|
2022-02-26 13:26:21 +00:00
|
|
|
});
|
2017-09-03 21:58:27 +00:00
|
|
|
if not_double_ended {
|
2022-09-01 09:43:35 +00:00
|
|
|
return is_infinite(cx, receiver);
|
2017-09-03 21:58:27 +00:00
|
|
|
}
|
2019-05-17 21:53:54 +00:00
|
|
|
} else if method.ident.name == sym!(collect) {
|
2020-07-17 08:47:04 +00:00
|
|
|
let ty = cx.typeck_results().expr_ty(expr);
|
2021-07-29 10:16:06 +00:00
|
|
|
if INFINITE_COLLECTORS
|
|
|
|
.iter()
|
|
|
|
.any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
|
|
|
|
{
|
2022-09-01 09:43:35 +00:00
|
|
|
return is_infinite(cx, receiver);
|
2018-12-31 11:06:08 +00:00
|
|
|
}
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
|
|
|
},
|
2021-04-08 15:50:13 +00:00
|
|
|
ExprKind::Binary(op, l, r) => {
|
2018-11-27 20:14:15 +00:00
|
|
|
if op.node.is_comparison() {
|
|
|
|
return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
|
|
|
|
}
|
2018-07-12 07:30:57 +00:00
|
|
|
}, // TODO: ExprKind::Loop + Match
|
2017-09-03 21:15:15 +00:00
|
|
|
_ => (),
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|
2017-08-26 16:46:42 +00:00
|
|
|
Finite
|
2017-08-25 20:20:52 +00:00
|
|
|
}
|