Auto merge of #3607 - detrumi:limit_infinite_iter_to_known_types, r=phansch

Only trigger `infinite_iter` lint for infinitely allocating `collect()` calls

Fixes  #3538

~Oh, I guess this should actually check other methods like `count` as well, not only `collect()`.~
Never mind, `collect` is the only of these functions that allocates a data structure.
This commit is contained in:
bors 2019-01-03 00:12:02 +00:00
commit 84aa027888
3 changed files with 46 additions and 3 deletions

View File

@ -7,7 +7,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, paths, span_lint}; use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array}; use rustc::{declare_tool_lint, lint_array};
@ -200,7 +200,6 @@ fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
/// their iterators /// their iterators
static COMPLETING_METHODS: &[(&str, usize)] = &[ static COMPLETING_METHODS: &[(&str, usize)] = &[
("count", 1), ("count", 1),
("collect", 1),
("fold", 3), ("fold", 3),
("for_each", 2), ("for_each", 2),
("partition", 2), ("partition", 2),
@ -214,6 +213,18 @@ fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
("product", 1), ("product", 1),
]; ];
/// the paths of types that are known to be infinitely allocating
static INFINITE_COLLECTORS: &[&[&str]] = &[
&paths::BINARY_HEAP,
&paths::BTREEMAP,
&paths::BTREESET,
&paths::HASHMAP,
&paths::HASHSET,
&paths::LINKED_LIST,
&paths::VEC,
&paths::VEC_DEQUE,
];
fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness { fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
match expr.node { match expr.node {
ExprKind::MethodCall(ref method, _, ref args) => { ExprKind::MethodCall(ref method, _, ref args) => {
@ -233,6 +244,11 @@ fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr) -> Finiteness {
if not_double_ended { if not_double_ended {
return is_infinite(cx, &args[0]); return is_infinite(cx, &args[0]);
} }
} else if method.ident.name == "collect" {
let ty = cx.tables.expr_ty(expr);
if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) {
return is_infinite(cx, &args[0]);
}
} }
}, },
ExprKind::Binary(op, ref l, ref r) => { ExprKind::Binary(op, ref l, ref r) => {

View File

@ -58,3 +58,22 @@ fn main() {
infinite_iters(); infinite_iters();
potential_infinite_iters(); potential_infinite_iters();
} }
mod finite_collect {
use std::collections::HashSet;
use std::iter::FromIterator;
struct C;
impl FromIterator<i32> for C {
fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self {
C
}
}
fn check_collect() {
let _: HashSet<i32> = (0..).collect(); // Infinite iter
// Some data structures don't collect infinitely, such as `ArrayVec`
let _: C = (0..).collect();
}
}

View File

@ -105,5 +105,13 @@ error: possible infinite iteration detected
LL | (0..).all(|x| x == 24); // maybe infinite iter LL | (0..).all(|x| x == 24); // maybe infinite iter
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 14 previous errors error: infinite iteration detected
--> $DIR/infinite_iter.rs:74:31
|
LL | let _: HashSet<i32> = (0..).collect(); // Infinite iter
| ^^^^^^^^^^^^^^^
|
= note: #[deny(clippy::infinite_iter)] on by default
error: aborting due to 15 previous errors