[`inconsistent_digit_grouping`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#inconsistent_digit_grouping
[`indexing_slicing`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#indexing_slicing
[`ineffective_bit_mask`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#ineffective_bit_mask
+[`infinite_iter`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#infinite_iter
[`inline_always`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#inline_always
[`integer_arithmetic`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#integer_arithmetic
[`invalid_regex`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#invalid_regex
[`match_ref_pats`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#match_ref_pats
[`match_same_arms`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#match_same_arms
[`match_wild_err_arm`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#match_wild_err_arm
+[`maybe_infinite_iter`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#maybe_infinite_iter
[`mem_forget`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#mem_forget
[`min_max`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#min_max
[`misrefactored_assign_op`]: https://github.com/rust-lang-nursery/rust-clippy/wiki#misrefactored_assign_op
## Lints
-There are 206 lints included in this crate:
+There are 208 lints included in this crate:
name | default | triggers on
-----------------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
[inconsistent_digit_grouping](https://github.com/rust-lang-nursery/rust-clippy/wiki#inconsistent_digit_grouping) | warn | integer literals with digits grouped inconsistently
[indexing_slicing](https://github.com/rust-lang-nursery/rust-clippy/wiki#indexing_slicing) | allow | indexing/slicing usage
[ineffective_bit_mask](https://github.com/rust-lang-nursery/rust-clippy/wiki#ineffective_bit_mask) | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`
+[infinite_iter](https://github.com/rust-lang-nursery/rust-clippy/wiki#infinite_iter) | warn | infinite iteration
[inline_always](https://github.com/rust-lang-nursery/rust-clippy/wiki#inline_always) | warn | use of `#[inline(always)]`
[integer_arithmetic](https://github.com/rust-lang-nursery/rust-clippy/wiki#integer_arithmetic) | allow | any integer arithmetic statement
[invalid_regex](https://github.com/rust-lang-nursery/rust-clippy/wiki#invalid_regex) | deny | invalid regular expressions
[match_ref_pats](https://github.com/rust-lang-nursery/rust-clippy/wiki#match_ref_pats) | warn | a match or `if let` with all arms prefixed with `&` instead of deref-ing the match expression
[match_same_arms](https://github.com/rust-lang-nursery/rust-clippy/wiki#match_same_arms) | warn | `match` with identical arm bodies
[match_wild_err_arm](https://github.com/rust-lang-nursery/rust-clippy/wiki#match_wild_err_arm) | warn | a match with `Err(_)` arm and take drastic actions
+[maybe_infinite_iter](https://github.com/rust-lang-nursery/rust-clippy/wiki#maybe_infinite_iter) | allow | possible infinite iteration
[mem_forget](https://github.com/rust-lang-nursery/rust-clippy/wiki#mem_forget) | allow | `mem::forget` usage on `Drop` types, likely to cause memory leaks
[min_max](https://github.com/rust-lang-nursery/rust-clippy/wiki#min_max) | warn | `min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant
[misrefactored_assign_op](https://github.com/rust-lang-nursery/rust-clippy/wiki#misrefactored_assign_op) | warn | having a variable on both sides of an assign op
--- /dev/null
+use rustc::hir::*;
+use rustc::lint::*;
+use utils::{get_trait_def_id, implements_trait, higher, match_path, paths, span_lint};
+
+/// **What it does:** Checks for iteration that is guaranteed to be infinite.
+///
+/// **Why is this bad?** While there may be places where this is acceptable
+/// (e.g. in event streams), in most cases this is simply an error.
+///
+/// **Known problems:** None.
+///
+/// **Example:**
+/// ```rust
+/// repeat(1_u8).iter().collect::<Vec<_>>()
+/// ```
+declare_lint! {
+ pub INFINITE_ITER,
+ Warn,
+ "infinite iteration"
+}
+
+/// **What it does:** Checks for iteration that may be infinite.
+///
+/// **Why is this bad?** While there may be places where this is acceptable
+/// (e.g. in event streams), in most cases this is simply an error.
+///
+/// **Known problems:** The code may have a condition to stop iteration, but
+/// this lint is not clever enough to analyze it.
+///
+/// **Example:**
+/// ```rust
+/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
+/// ```
+declare_lint! {
+ pub MAYBE_INFINITE_ITER,
+ Allow,
+ "possible infinite iteration"
+}
+
+#[derive(Copy, Clone)]
+pub struct Pass;
+
+impl LintPass for Pass {
+ fn get_lints(&self) -> LintArray {
+ lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
+ }
+}
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
+ let (lint, msg) = match complete_infinite_iter(cx, expr) {
+ True => (INFINITE_ITER, "infinite iteration detected"),
+ Unknown => (MAYBE_INFINITE_ITER,
+ "possible infinite iteration detected"),
+ False => { return; }
+ };
+ span_lint(cx, lint, expr.span, msg)
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum TriState {
+ True,
+ Unknown,
+ False
+}
+
+use self::TriState::{True, Unknown, False};
+
+impl TriState {
+ fn and(self, b: Self) -> Self {
+ match (self, b) {
+ (False, _) | (_, False) => False,
+ (Unknown, _) | (_, Unknown) => Unknown,
+ _ => True
+ }
+ }
+
+ fn or(self, b: Self) -> Self {
+ match (self, b) {
+ (True, _) | (_, True) => True,
+ (Unknown, _) | (_, Unknown) => Unknown,
+ _ => False
+ }
+ }
+}
+
+impl From<bool> for TriState {
+ fn from(b: bool) -> Self {
+ if b { True } else { False }
+ }
+}
+
+#[derive(Copy, Clone)]
+enum Heuristic {
+ Always,
+ First,
+ Any,
+ All
+}
+
+use self::Heuristic::{Always, First, Any, All};
+
+// here we use the `TriState` as (Finite, Possible Infinite, Infinite)
+static HEURISTICS : &[(&str, usize, Heuristic, TriState)] = &[
+ ("zip", 2, All, True),
+ ("chain", 2, Any, True),
+ ("cycle", 1, Always, True),
+ ("map", 2, First, True),
+ ("by_ref", 1, First, True),
+ ("cloned", 1, First, True),
+ ("rev", 1, First, True),
+ ("inspect", 1, First, True),
+ ("enumerate", 1, First, True),
+ ("peekable", 2, First, True),
+ ("fuse", 1, First, True),
+ ("skip", 2, First, True),
+ ("skip_while", 1, First, True),
+ ("filter", 2, First, True),
+ ("filter_map", 2, First, True),
+ ("flat_map", 2, First, True),
+ ("unzip", 1, First, True),
+ ("take_while", 2, First, Unknown),
+ ("scan", 3, First, Unknown)
+];
+
+fn is_infinite(cx: &LateContext, expr: &Expr) -> TriState {
+ match expr.node {
+ ExprMethodCall(ref method, _, ref args) => {
+ for &(name, len, heuristic, cap) in HEURISTICS.iter() {
+ if method.name == name && args.len() == len {
+ return (match heuristic {
+ Always => True,
+ First => is_infinite(cx, &args[0]),
+ Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
+ All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
+ }).and(cap);
+ }
+ }
+ if method.name == "flat_map" && args.len() == 2 {
+ if let ExprClosure(_, _, body_id, _) = args[1].node {
+ let body = cx.tcx.hir.body(body_id);
+ return is_infinite(cx, &body.value);
+ }
+ }
+ False
+ },
+ ExprBlock(ref block) =>
+ block.expr.as_ref().map_or(False, |e| is_infinite(cx, e)),
+ ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
+ ExprCall(ref path, _) => {
+ if let ExprPath(ref qpath) = path.node {
+ match_path(qpath, &paths::REPEAT).into()
+ } else { False }
+ },
+ ExprStruct(..) => {
+ higher::range(expr).map_or(false, |r| r.end.is_none()).into()
+ },
+ _ => False
+ }
+}
+
+static POSSIBLY_COMPLETING_METHODS : &[(&str, usize)] = &[
+ ("find", 2),
+ ("rfind", 2),
+ ("position", 2),
+ ("rposition", 2),
+ ("any", 2),
+ ("all", 2)
+];
+
+static COMPLETING_METHODS : &[(&str, usize)] = &[
+ ("count", 1),
+ ("collect", 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)
+];
+
+fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> TriState {
+ match expr.node {
+ ExprMethodCall(ref method, _, ref args) => {
+ for &(name, len) in COMPLETING_METHODS.iter() {
+ if method.name == name && args.len() == len {
+ return is_infinite(cx, &args[0]);
+ }
+ }
+ for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
+ if method.name == name && args.len() == len {
+ return Unknown.and(is_infinite(cx, &args[0]));
+ }
+ }
+ if method.name == "last" && args.len() == 1 &&
+ get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(false,
+ |id| !implements_trait(cx,
+ cx.tables.expr_ty(&args[0]),
+ id,
+ &[])) {
+ return is_infinite(cx, &args[0]);
+ }
+ },
+ ExprBinary(op, ref l, ref r) => {
+ if op.node.is_comparison() {
+ return is_infinite(cx, l).and(is_infinite(cx, r)).and(Unknown)
+ }
+ }, //TODO: ExprLoop + Match
+ _ => ()
+ }
+ False
+}
pub mod identity_op;
pub mod if_let_redundant_pattern_matching;
pub mod if_not_else;
+pub mod infinite_iter;
pub mod items_after_statements;
pub mod large_enum_variant;
pub mod len_zero;
reg.register_early_lint_pass(box literal_digit_grouping::LiteralDigitGrouping);
reg.register_late_lint_pass(box use_self::UseSelf);
reg.register_late_lint_pass(box bytecount::ByteCount);
+ reg.register_late_lint_pass(box infinite_iter::Pass);
reg.register_lint_group("clippy_restrictions", vec![
arithmetic::FLOAT_ARITHMETIC,
enum_variants::PUB_ENUM_VARIANT_NAMES,
enum_variants::STUTTER,
if_not_else::IF_NOT_ELSE,
+ infinite_iter::MAYBE_INFINITE_ITER,
items_after_statements::ITEMS_AFTER_STATEMENTS,
matches::SINGLE_MATCH_ELSE,
mem_forget::MEM_FORGET,
functions::TOO_MANY_ARGUMENTS,
identity_op::IDENTITY_OP,
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
+ infinite_iter::INFINITE_ITER,
large_enum_variant::LARGE_ENUM_VARIANT,
len_zero::LEN_WITHOUT_IS_EMPTY,
len_zero::LEN_ZERO,
pub const DEBUG_FMT_METHOD: [&'static str; 4] = ["core", "fmt", "Debug", "fmt"];
pub const DEFAULT_TRAIT: [&'static str; 3] = ["core", "default", "Default"];
pub const DISPLAY_FMT_METHOD: [&'static str; 4] = ["core", "fmt", "Display", "fmt"];
+pub const DOUBLE_ENDED_ITERATOR: [&'static str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&'static str; 3] = ["core", "mem", "drop"];
pub const FMT_ARGUMENTS_NEWV1: [&'static str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTV1_NEW: [&'static str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const REGEX_BYTES_SET_NEW: [&'static str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&'static str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&'static str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
+pub const REPEAT: [&'static str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&'static str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&'static str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&'static str; 4] = ["core", "result", "Result", "Ok"];
--- /dev/null
+#![feature(plugin)]
+#![feature(iterator_for_each)]
+#![plugin(clippy)]
+use std::iter::repeat;
+
+fn square_is_lower_64(x: &u32) -> bool { x * x < 64 }
+
+#[allow(maybe_infinite_iter)]
+#[deny(infinite_iter)]
+fn infinite_iters() {
+ repeat(0_u8).collect::<Vec<_>>(); // infinite iter
+ (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
+ (0..8_u64).chain(0..).max(); // infinite iter
+ (0_usize..).chain([0usize, 1, 2].iter().cloned()).skip_while(|x| *x != 42).min(); // infinite iter
+ (0..8_u32).rev().cycle().map(|x| x + 1_u32).for_each(|x| println!("{}", x)); // infinite iter
+ (0..3_u32).flat_map(|x| x..).sum::<u32>(); // infinite iter
+ (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
+ (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
+ (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended
+ (0..).next(); // iterator is not exhausted
+}
+
+#[deny(maybe_infinite_iter)]
+fn potential_infinite_iters() {
+ (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
+ repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
+ (1..).scan(0, |state, x| { *state += x; Some(*state) }).min(); // maybe infinite iter
+ (0..).find(|x| *x == 24); // maybe infinite iter
+ (0..).position(|x| x == 24); // maybe infinite iter
+ (0..).any(|x| x == 24); // maybe infinite iter
+ (0..).all(|x| x == 24); // maybe infinite iter
+
+ (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite
+ repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted
+}
+
+fn main() {
+ infinite_iters();
+ potential_infinite_iters();
+}
--- /dev/null
+error: you are collect()ing an iterator and throwing away the result. Consider using an explicit for loop to exhaust the iterator
+ --> $DIR/infinite_iter.rs:11:5
+ |
+11 | repeat(0_u8).collect::<Vec<_>>(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D unused-collect` implied by `-D warnings`
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:11:5
+ |
+11 | repeat(0_u8).collect::<Vec<_>>(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: lint level defined here
+ --> $DIR/infinite_iter.rs:9:8
+ |
+9 | #[deny(infinite_iter)]
+ | ^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:12:5
+ |
+12 | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:13:5
+ |
+13 | (0..8_u64).chain(0..).max(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:15:5
+ |
+15 | (0..8_u32).rev().cycle().map(|x| x + 1_u32).for_each(|x| println!("{}", x)); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:17:5
+ |
+17 | (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:18:5
+ |
+18 | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:25:5
+ |
+25 | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: lint level defined here
+ --> $DIR/infinite_iter.rs:23:8
+ |
+23 | #[deny(maybe_infinite_iter)]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:26:5
+ |
+26 | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:27:5
+ |
+27 | (1..).scan(0, |state, x| { *state += x; Some(*state) }).min(); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:28:5
+ |
+28 | (0..).find(|x| *x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:29:5
+ |
+29 | (0..).position(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:30:5
+ |
+30 | (0..).any(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:31:5
+ |
+31 | (0..).all(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 14 previous errors
+