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, "possible infinite iteration detected"),
58 span_lint(cx, lint, expr.span, msg)
62 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
69 use self::Finiteness::{Infinite, MaybeInfinite, Finite};
72 fn and(self, b: Self) -> Self {
74 (Finite, _) | (_, Finite) => Finite,
76 (_, MaybeInfinite) => MaybeInfinite,
81 fn or(self, b: Self) -> Self {
83 (Infinite, _) | (_, Infinite) => Infinite,
85 (_, MaybeInfinite) => MaybeInfinite,
91 impl From<bool> for Finiteness {
92 fn from(b: bool) -> Self {
93 if b { Infinite } else { Finite }
97 /// This tells us what to look for to know if the iterator returned by
98 /// this method is infinite
99 #[derive(Copy, Clone)]
101 /// infinite no matter what
103 /// infinite if the first argument is
105 /// infinite if any of the supplied arguments is
107 /// infinite if all of the supplied arguments are
111 use self::Heuristic::{Always, First, Any, All};
113 /// a slice of (method name, number of args, heuristic, bounds) tuples
114 /// that will be used to determine whether the method in question
115 /// returns an infinite or possibly infinite iterator. The finiteness
116 /// is an upper bound, e.g. some methods can return a possibly
117 /// infinite iterator at worst, e.g. `take_while`.
118 static HEURISTICS: &[(&str, usize, Heuristic, Finiteness)] = &[
119 ("zip", 2, All, Infinite),
120 ("chain", 2, Any, Infinite),
121 ("cycle", 1, Always, Infinite),
122 ("map", 2, First, Infinite),
123 ("by_ref", 1, First, Infinite),
124 ("cloned", 1, First, Infinite),
125 ("rev", 1, First, Infinite),
126 ("inspect", 1, First, Infinite),
127 ("enumerate", 1, First, Infinite),
128 ("peekable", 2, First, Infinite),
129 ("fuse", 1, First, Infinite),
130 ("skip", 2, First, Infinite),
131 ("skip_while", 1, First, Infinite),
132 ("filter", 2, First, Infinite),
133 ("filter_map", 2, First, Infinite),
134 ("flat_map", 2, First, Infinite),
135 ("unzip", 1, First, Infinite),
136 ("take_while", 2, First, MaybeInfinite),
137 ("scan", 3, First, MaybeInfinite),
140 fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
142 ExprMethodCall(ref method, _, ref args) => {
143 for &(name, len, heuristic, cap) in HEURISTICS.iter() {
144 if method.name == name && args.len() == len {
145 return (match heuristic {
147 First => is_infinite(cx, &args[0]),
148 Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
149 All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
153 if method.name == "flat_map" && args.len() == 2 {
154 if let ExprClosure(_, _, body_id, _, _) = args[1].node {
155 let body = cx.tcx.hir.body(body_id);
156 return is_infinite(cx, &body.value);
161 ExprBlock(ref block) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
163 ExprAddrOf(_, ref e) => is_infinite(cx, e),
164 ExprCall(ref path, _) => {
165 if let ExprPath(ref qpath) = path.node {
166 match_qpath(qpath, &paths::REPEAT).into()
173 .map_or(false, |r| r.end.is_none())
180 /// the names and argument lengths of methods that *may* exhaust their
182 static POSSIBLY_COMPLETING_METHODS: &[(&str, usize)] = &[
191 /// the names and argument lengths of methods that *always* exhaust
193 static COMPLETING_METHODS: &[(&str, usize)] = &[
209 fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> Finiteness {
211 ExprMethodCall(ref method, _, ref args) => {
212 for &(name, len) in COMPLETING_METHODS.iter() {
213 if method.name == name && args.len() == len {
214 return is_infinite(cx, &args[0]);
217 for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
218 if method.name == name && args.len() == len {
219 return MaybeInfinite.and(is_infinite(cx, &args[0]));
222 if method.name == "last" && args.len() == 1 &&
223 get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(
226 !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[])
230 return is_infinite(cx, &args[0]);
233 ExprBinary(op, ref l, ref r) => {
234 if op.node.is_comparison() {
235 return is_infinite(cx, l).and(is_infinite(cx, r)).and(
239 }, //TODO: ExprLoop + Match