3 use utils::{get_trait_def_id, implements_trait, higher, match_qpath, paths, span_lint};
5 /// **What it does:** Checks for iteration that is guaranteed to be infinite.
7 /// **Why is this bad?** While there may be places where this is acceptable
8 /// (e.g. in event streams), in most cases this is simply an error.
10 /// **Known problems:** None.
14 /// repeat(1_u8).iter().collect::<Vec<_>>()
22 /// **What it does:** Checks for iteration that may be infinite.
24 /// **Why is this bad?** While there may be places where this is acceptable
25 /// (e.g. in event streams), in most cases this is simply an error.
27 /// **Known problems:** The code may have a condition to stop iteration, but
28 /// this lint is not clever enough to analyze it.
32 /// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
35 pub MAYBE_INFINITE_ITER,
37 "possible infinite iteration"
40 #[derive(Copy, Clone)]
43 impl LintPass for Pass {
44 fn get_lints(&self) -> LintArray {
45 lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
49 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
50 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
51 let (lint, msg) = match complete_infinite_iter(cx, expr) {
52 Infinite => (INFINITE_ITER, "infinite iteration detected"),
53 MaybeInfinite => (MAYBE_INFINITE_ITER,
54 "possible infinite iteration detected"),
57 span_lint(cx, lint, expr.span, msg)
61 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
68 use self::Finiteness::{Infinite, MaybeInfinite, Finite};
71 fn and(self, b: Self) -> Self {
73 (Finite, _) | (_, Finite) => Finite,
74 (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
79 fn or(self, b: Self) -> Self {
81 (Infinite, _) | (_, Infinite) => Infinite,
82 (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
88 impl From<bool> for Finiteness {
89 fn from(b: bool) -> Self {
90 if b { Infinite } else { Finite }
94 /// This tells us what to look for to know if the iterator returned by
95 /// this method is infinite
96 #[derive(Copy, Clone)]
98 /// infinite no matter what
100 /// infinite if the first argument is
102 /// infinite if any of the supplied arguments is
104 /// infinite if all of the supplied arguments are
108 use self::Heuristic::{Always, First, Any, All};
110 /// a slice of (method name, number of args, heuristic, bounds) tuples
111 /// that will be used to determine whether the method in question
112 /// returns an infinite or possibly infinite iterator. The finiteness
113 /// is an upper bound, e.g. some methods can return a possibly
114 /// infinite iterator at worst, e.g. `take_while`.
115 static HEURISTICS : &[(&str, usize, Heuristic, Finiteness)] = &[
116 ("zip", 2, All, Infinite),
117 ("chain", 2, Any, Infinite),
118 ("cycle", 1, Always, Infinite),
119 ("map", 2, First, Infinite),
120 ("by_ref", 1, First, Infinite),
121 ("cloned", 1, First, Infinite),
122 ("rev", 1, First, Infinite),
123 ("inspect", 1, First, Infinite),
124 ("enumerate", 1, First, Infinite),
125 ("peekable", 2, First, Infinite),
126 ("fuse", 1, First, Infinite),
127 ("skip", 2, First, Infinite),
128 ("skip_while", 1, First, Infinite),
129 ("filter", 2, First, Infinite),
130 ("filter_map", 2, First, Infinite),
131 ("flat_map", 2, First, Infinite),
132 ("unzip", 1, First, Infinite),
133 ("take_while", 2, First, MaybeInfinite),
134 ("scan", 3, First, MaybeInfinite)
137 fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
139 ExprMethodCall(ref method, _, ref args) => {
140 for &(name, len, heuristic, cap) in HEURISTICS.iter() {
141 if method.name == name && args.len() == len {
142 return (match heuristic {
144 First => is_infinite(cx, &args[0]),
145 Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
146 All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
150 if method.name == "flat_map" && args.len() == 2 {
151 if let ExprClosure(_, _, body_id, _) = args[1].node {
152 let body = cx.tcx.hir.body(body_id);
153 return is_infinite(cx, &body.value);
158 ExprBlock(ref block) =>
159 block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
160 ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
161 ExprCall(ref path, _) => {
162 if let ExprPath(ref qpath) = path.node {
163 match_qpath(qpath, &paths::REPEAT).into()
167 higher::range(expr).map_or(false, |r| r.end.is_none()).into()
173 /// the names and argument lengths of methods that *may* exhaust their
175 static POSSIBLY_COMPLETING_METHODS : &[(&str, usize)] = &[
184 /// the names and argument lengths of methods that *always* exhaust
186 static COMPLETING_METHODS : &[(&str, usize)] = &[
202 fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> Finiteness {
204 ExprMethodCall(ref method, _, ref args) => {
205 for &(name, len) in COMPLETING_METHODS.iter() {
206 if method.name == name && args.len() == len {
207 return is_infinite(cx, &args[0]);
210 for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
211 if method.name == name && args.len() == len {
212 return MaybeInfinite.and(is_infinite(cx, &args[0]));
215 if method.name == "last" && args.len() == 1 &&
216 get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(false,
217 |id| !implements_trait(cx,
218 cx.tables.expr_ty(&args[0]),
221 return is_infinite(cx, &args[0]);
224 ExprBinary(op, ref l, ref r) => {
225 if op.node.is_comparison() {
226 return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite)
228 }, //TODO: ExprLoop + Match