1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
11 use crate::rustc::hir::*;
12 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13 use crate::rustc::{declare_tool_lint, lint_array};
14 use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, paths, span_lint};
16 /// **What it does:** Checks for iteration that is guaranteed to be infinite.
18 /// **Why is this bad?** While there may be places where this is acceptable
19 /// (e.g. in event streams), in most cases this is simply an error.
21 /// **Known problems:** None.
25 /// repeat(1_u8).iter().collect::<Vec<_>>()
27 declare_clippy_lint! {
33 /// **What it does:** Checks for iteration that may be infinite.
35 /// **Why is this bad?** While there may be places where this is acceptable
36 /// (e.g. in event streams), in most cases this is simply an error.
38 /// **Known problems:** The code may have a condition to stop iteration, but
39 /// this lint is not clever enough to analyze it.
43 /// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
45 declare_clippy_lint! {
46 pub MAYBE_INFINITE_ITER,
48 "possible infinite iteration"
51 #[derive(Copy, Clone)]
54 impl LintPass for Pass {
55 fn get_lints(&self) -> LintArray {
56 lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
60 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
61 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
62 let (lint, msg) = match complete_infinite_iter(cx, expr) {
63 Infinite => (INFINITE_ITER, "infinite iteration detected"),
64 MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
69 span_lint(cx, lint, expr.span, msg)
73 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
80 use self::Finiteness::{Finite, Infinite, MaybeInfinite};
83 fn and(self, b: Self) -> Self {
85 (Finite, _) | (_, Finite) => Finite,
86 (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
91 fn or(self, b: Self) -> Self {
93 (Infinite, _) | (_, Infinite) => Infinite,
94 (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
100 impl From<bool> for Finiteness {
101 fn from(b: bool) -> Self {
110 /// This tells us what to look for to know if the iterator returned by
111 /// this method is infinite
112 #[derive(Copy, Clone)]
114 /// infinite no matter what
116 /// infinite if the first argument is
118 /// infinite if any of the supplied arguments is
120 /// infinite if all of the supplied arguments are
124 use self::Heuristic::{All, Always, Any, First};
126 /// a slice of (method name, number of args, heuristic, bounds) tuples
127 /// that will be used to determine whether the method in question
128 /// returns an infinite or possibly infinite iterator. The finiteness
129 /// is an upper bound, e.g. some methods can return a possibly
130 /// infinite iterator at worst, e.g. `take_while`.
131 static HEURISTICS: &[(&str, usize, Heuristic, Finiteness)] = &[
132 ("zip", 2, All, Infinite),
133 ("chain", 2, Any, Infinite),
134 ("cycle", 1, Always, Infinite),
135 ("map", 2, First, Infinite),
136 ("by_ref", 1, First, Infinite),
137 ("cloned", 1, First, Infinite),
138 ("rev", 1, First, Infinite),
139 ("inspect", 1, First, Infinite),
140 ("enumerate", 1, First, Infinite),
141 ("peekable", 2, First, Infinite),
142 ("fuse", 1, First, Infinite),
143 ("skip", 2, First, Infinite),
144 ("skip_while", 1, First, Infinite),
145 ("filter", 2, First, Infinite),
146 ("filter_map", 2, First, Infinite),
147 ("flat_map", 2, First, Infinite),
148 ("unzip", 1, First, Infinite),
149 ("take_while", 2, First, MaybeInfinite),
150 ("scan", 3, First, MaybeInfinite),
153 fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
155 ExprKind::MethodCall(ref method, _, ref args) => {
156 for &(name, len, heuristic, cap) in HEURISTICS.iter() {
157 if method.ident.name == name && args.len() == len {
158 return (match heuristic {
160 First => is_infinite(cx, &args[0]),
161 Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
162 All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
166 if method.ident.name == "flat_map" && args.len() == 2 {
167 if let ExprKind::Closure(_, _, body_id, _, _) = args[1].node {
168 let body = cx.tcx.hir.body(body_id);
169 return is_infinite(cx, &body.value);
174 ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
175 ExprKind::Box(ref e) | ExprKind::AddrOf(_, ref e) => is_infinite(cx, e),
176 ExprKind::Call(ref path, _) => if let ExprKind::Path(ref qpath) = path.node {
177 match_qpath(qpath, &paths::REPEAT).into()
181 ExprKind::Struct(..) => higher::range(cx, expr)
182 .map_or(false, |r| r.end.is_none())
188 /// the names and argument lengths of methods that *may* exhaust their
190 static POSSIBLY_COMPLETING_METHODS: &[(&str, usize)] = &[
199 /// the names and argument lengths of methods that *always* exhaust
201 static COMPLETING_METHODS: &[(&str, usize)] = &[
217 fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
219 ExprKind::MethodCall(ref method, _, ref args) => {
220 for &(name, len) in COMPLETING_METHODS.iter() {
221 if method.ident.name == name && args.len() == len {
222 return is_infinite(cx, &args[0]);
225 for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
226 if method.ident.name == name && args.len() == len {
227 return MaybeInfinite.and(is_infinite(cx, &args[0]));
230 if method.ident.name == "last" && args.len() == 1 {
231 let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR)
232 .map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[]));
233 if not_double_ended {
234 return is_infinite(cx, &args[0]);
238 ExprKind::Binary(op, ref l, ref r) => if op.node.is_comparison() {
239 return is_infinite(cx, l)
240 .and(is_infinite(cx, r))
242 }, // TODO: ExprKind::Loop + Match