Skip to content

Commit b3837dc

Browse files
committed
add lint for checking unnecessary iter().any()
1 parent b3a1693 commit b3837dc

File tree

11 files changed

+143
-84
lines changed

11 files changed

+143
-84
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6116,6 +6116,7 @@ Released 2018-09-13
61166116
[`unnecessary_first_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_first_then_check
61176117
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
61186118
[`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
6119+
[`unnecessary_iter_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_iter_any
61196120
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
61206121
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
61216122
[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
483483
crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO,
484484
crate::methods::UNNECESSARY_FOLD_INFO,
485485
crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO,
486+
crate::methods::UNNECESSARY_ITER_ANY_INFO,
486487
crate::methods::UNNECESSARY_JOIN_INFO,
487488
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
488489
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,

clippy_lints/src/methods/iter_any.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_hir::{Expr, ExprKind};
3+
use rustc_lint::LateContext;
4+
use rustc_middle::ty::{self};
5+
6+
use super::{SLICE_ITER_ANY, UNNECESSARY_ITER_ANY, method_call};
7+
8+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
9+
if !expr.span.from_expansion()
10+
// any()
11+
&& let Some((name, recv, args, _, _)) = method_call(expr)
12+
&& name == "any"
13+
// check if the inner closure is a equality check
14+
&& args.len() == 1
15+
&& let ExprKind::Closure(closure) = args[0].kind
16+
&& let body = cx.tcx.hir().body(closure.body)
17+
&& let ExprKind::Binary(op, _, _) = body.value.kind
18+
&& op.node == rustc_ast::ast::BinOpKind::Eq
19+
// iter()
20+
&& let Some((name, recv, _, _, _)) = method_call(recv)
21+
&& name == "iter"
22+
{
23+
let ref_type = cx.typeck_results().expr_ty(recv);
24+
25+
match ref_type.kind() {
26+
ty::Ref(_, inner_type, _) if inner_type.is_slice() => {
27+
// check if the receiver is a u8/i8 slice
28+
if let ty::Slice(slice_type) = inner_type.kind()
29+
&& (slice_type.to_string() == "u8" || slice_type.to_string() == "i8")
30+
{
31+
span_lint(
32+
cx,
33+
SLICE_ITER_ANY,
34+
expr.span,
35+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient",
36+
);
37+
}
38+
},
39+
// if it's an array that uses `iter().any()` and its closure is an equality check, suggest using
40+
// `contains()`
41+
ty::Array(array_type, _) if array_type.is_numeric() => span_lint(
42+
cx,
43+
UNNECESSARY_ITER_ANY,
44+
expr.span,
45+
"using `contains()` instead of `iter().any()` is more readable",
46+
),
47+
_ => {},
48+
}
49+
}
50+
}

clippy_lints/src/methods/mod.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod inspect_for_each;
3737
mod into_iter_on_ref;
3838
mod is_digit_ascii_radix;
3939
mod is_empty;
40+
mod iter_any;
4041
mod iter_cloned_collect;
4142
mod iter_count;
4243
mod iter_filter;
@@ -100,7 +101,6 @@ mod single_char_add_str;
100101
mod single_char_insert_string;
101102
mod single_char_push_string;
102103
mod skip_while_next;
103-
mod slice_iter_any;
104104
mod stable_sort_primitive;
105105
mod str_split;
106106
mod str_splitn;
@@ -4310,6 +4310,29 @@ declare_clippy_lint! {
43104310
"using `contains()` instead of `iter().any()` on `u8`/`i8` slices is more fast"
43114311
}
43124312

4313+
declare_clippy_lint! {
4314+
/// ### What it does
4315+
/// Checks for usage of `iter().any()` when it can be replaced with `contains()` and suggests doing so.
4316+
///
4317+
/// ### Why is this bad?
4318+
/// It makes the code less readable.
4319+
///
4320+
/// ### Example
4321+
/// ```no_run
4322+
/// let values = &[1, 2, 3];
4323+
/// let _ = values.iter().any(|&v| v == 2);
4324+
/// ```
4325+
/// Use instead:
4326+
/// ```no_run
4327+
/// let values = &[1, 2, 3];
4328+
/// let _ = values.contains(&2);
4329+
/// ```
4330+
#[clippy::version = "1.85.0"]
4331+
pub UNNECESSARY_ITER_ANY,
4332+
style,
4333+
"using `contains()` instead of `iter().any()` is more readable"
4334+
}
4335+
43134336
pub struct Methods {
43144337
avoid_breaking_exported_api: bool,
43154338
msrv: Msrv,
@@ -4476,6 +4499,7 @@ impl_lint_pass!(Methods => [
44764499
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
44774500
UNNECESSARY_MAP_OR,
44784501
SLICE_ITER_ANY,
4502+
UNNECESSARY_ITER_ANY,
44794503
]);
44804504

44814505
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4710,7 +4734,7 @@ impl Methods {
47104734
("any", [arg]) => {
47114735
unused_enumerate_index::check(cx, expr, recv, arg);
47124736
needless_character_iteration::check(cx, expr, recv, arg, false);
4713-
slice_iter_any::check(cx, expr);
4737+
iter_any::check(cx, expr);
47144738
match method_call(recv) {
47154739
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
47164740
cx,

clippy_lints/src/methods/slice_iter_any.rs

Lines changed: 0 additions & 42 deletions
This file was deleted.

tests/ui/slice_iter_any.rs renamed to tests/ui/iter_any.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn main() {
1818

1919
let values: [u8; 6] = [3, 14, 15, 92, 6, 5];
2020
let _ = values.iter().any(|&v| v == 10);
21-
// no error, because it's an array
21+
//~^ ERROR: using `contains()` instead of `iter().any()` is more readable
2222

2323
let values: [u8; 6] = [3, 14, 15, 92, 6, 5];
2424
let _ = values.iter().any(|&v| v > 10);
@@ -32,5 +32,5 @@ fn foo(values: &[u8]) -> bool {
3232

3333
fn bar(values: [u8; 3]) -> bool {
3434
values.iter().any(|&v| v == 10)
35-
// no error, because it's an array
35+
//~^ ERROR: using `contains()` instead of `iter().any()` is more readable
3636
}

tests/ui/iter_any.stderr

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
2+
--> tests/ui/iter_any.rs:6:13
3+
|
4+
LL | let _ = values.iter().any(|&v| v == 4);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::slice-iter-any` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::slice_iter_any)]`
9+
10+
error: using `contains()` instead of `iter().any()` is more readable
11+
--> tests/ui/iter_any.rs:20:13
12+
|
13+
LL | let _ = values.iter().any(|&v| v == 10);
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= note: `-D clippy::unnecessary-iter-any` implied by `-D warnings`
17+
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_iter_any)]`
18+
19+
error: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
20+
--> tests/ui/iter_any.rs:29:5
21+
|
22+
LL | values.iter().any(|&v| v == 10)
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: using `contains()` instead of `iter().any()` is more readable
26+
--> tests/ui/iter_any.rs:34:5
27+
|
28+
LL | values.iter().any(|&v| v == 10)
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
error: aborting due to 4 previous errors
32+

tests/ui/needless_collect.fixed

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
44

55
#[warn(clippy::needless_collect)]
6-
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
6+
#[allow(
7+
unused_variables,
8+
clippy::unnecessary_iter_any,
9+
clippy::iter_cloned_collect,
10+
clippy::iter_next_slice
11+
)]
712
fn main() {
813
let sample = [1; 5];
914
let len = sample.iter().count();

tests/ui/needless_collect.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
44

55
#[warn(clippy::needless_collect)]
6-
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
6+
#[allow(
7+
unused_variables,
8+
clippy::unnecessary_iter_any,
9+
clippy::iter_cloned_collect,
10+
clippy::iter_next_slice
11+
)]
712
fn main() {
813
let sample = [1; 5];
914
let len = sample.iter().collect::<Vec<_>>().len();

tests/ui/needless_collect.stderr

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: avoid using `collect()` when not needed
2-
--> tests/ui/needless_collect.rs:9:29
2+
--> tests/ui/needless_collect.rs:14:29
33
|
44
LL | let len = sample.iter().collect::<Vec<_>>().len();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
@@ -8,109 +8,109 @@ LL | let len = sample.iter().collect::<Vec<_>>().len();
88
= help: to override `-D warnings` add `#[allow(clippy::needless_collect)]`
99

1010
error: avoid using `collect()` when not needed
11-
--> tests/ui/needless_collect.rs:10:22
11+
--> tests/ui/needless_collect.rs:15:22
1212
|
1313
LL | if sample.iter().collect::<Vec<_>>().is_empty() {
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
1515

1616
error: avoid using `collect()` when not needed
17-
--> tests/ui/needless_collect.rs:13:28
17+
--> tests/ui/needless_collect.rs:18:28
1818
|
1919
LL | sample.iter().cloned().collect::<Vec<_>>().contains(&1);
2020
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
2121

2222
error: avoid using `collect()` when not needed
23-
--> tests/ui/needless_collect.rs:18:35
23+
--> tests/ui/needless_collect.rs:23:35
2424
|
2525
LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty();
2626
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
2727

2828
error: avoid using `collect()` when not needed
29-
--> tests/ui/needless_collect.rs:19:35
29+
--> tests/ui/needless_collect.rs:24:35
3030
|
3131
LL | sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty();
3232
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
3333

3434
error: avoid using `collect()` when not needed
35-
--> tests/ui/needless_collect.rs:26:19
35+
--> tests/ui/needless_collect.rs:31:19
3636
|
3737
LL | sample.iter().collect::<LinkedList<_>>().len();
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
3939

4040
error: avoid using `collect()` when not needed
41-
--> tests/ui/needless_collect.rs:27:19
41+
--> tests/ui/needless_collect.rs:32:19
4242
|
4343
LL | sample.iter().collect::<LinkedList<_>>().is_empty();
4444
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
4545

4646
error: avoid using `collect()` when not needed
47-
--> tests/ui/needless_collect.rs:28:28
47+
--> tests/ui/needless_collect.rs:33:28
4848
|
4949
LL | sample.iter().cloned().collect::<LinkedList<_>>().contains(&1);
5050
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
5151

5252
error: avoid using `collect()` when not needed
53-
--> tests/ui/needless_collect.rs:29:19
53+
--> tests/ui/needless_collect.rs:34:19
5454
|
5555
LL | sample.iter().collect::<LinkedList<_>>().contains(&&1);
5656
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)`
5757

5858
error: avoid using `collect()` when not needed
59-
--> tests/ui/needless_collect.rs:32:19
59+
--> tests/ui/needless_collect.rs:37:19
6060
|
6161
LL | sample.iter().collect::<BinaryHeap<_>>().len();
6262
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
6363

6464
error: avoid using `collect()` when not needed
65-
--> tests/ui/needless_collect.rs:33:19
65+
--> tests/ui/needless_collect.rs:38:19
6666
|
6767
LL | sample.iter().collect::<BinaryHeap<_>>().is_empty();
6868
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
6969

7070
error: avoid using `collect()` when not needed
71-
--> tests/ui/needless_collect.rs:38:27
71+
--> tests/ui/needless_collect.rs:43:27
7272
|
7373
LL | let _ = sample.iter().collect::<HashSet<_>>().is_empty();
7474
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
7575

7676
error: avoid using `collect()` when not needed
77-
--> tests/ui/needless_collect.rs:39:27
77+
--> tests/ui/needless_collect.rs:44:27
7878
|
7979
LL | let _ = sample.iter().collect::<HashSet<_>>().contains(&&0);
8080
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)`
8181

8282
error: avoid using `collect()` when not needed
83-
--> tests/ui/needless_collect.rs:61:27
83+
--> tests/ui/needless_collect.rs:66:27
8484
|
8585
LL | let _ = sample.iter().collect::<VecWrapper<_>>().is_empty();
8686
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
8787

8888
error: avoid using `collect()` when not needed
89-
--> tests/ui/needless_collect.rs:62:27
89+
--> tests/ui/needless_collect.rs:67:27
9090
|
9191
LL | let _ = sample.iter().collect::<VecWrapper<_>>().contains(&&0);
9292
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)`
9393

9494
error: avoid using `collect()` when not needed
95-
--> tests/ui/needless_collect.rs:66:40
95+
--> tests/ui/needless_collect.rs:71:40
9696
|
9797
LL | Vec::<u8>::new().extend((0..10).collect::<Vec<_>>());
9898
| ^^^^^^^^^^^^^^^^^^^^ help: remove this call
9999

100100
error: avoid using `collect()` when not needed
101-
--> tests/ui/needless_collect.rs:67:20
101+
--> tests/ui/needless_collect.rs:72:20
102102
|
103103
LL | foo((0..10).collect::<Vec<_>>());
104104
| ^^^^^^^^^^^^^^^^^^^^ help: remove this call
105105

106106
error: avoid using `collect()` when not needed
107-
--> tests/ui/needless_collect.rs:68:49
107+
--> tests/ui/needless_collect.rs:73:49
108108
|
109109
LL | bar((0..10).collect::<Vec<_>>(), (0..10).collect::<Vec<_>>());
110110
| ^^^^^^^^^^^^^^^^^^^^ help: remove this call
111111

112112
error: avoid using `collect()` when not needed
113-
--> tests/ui/needless_collect.rs:69:37
113+
--> tests/ui/needless_collect.rs:74:37
114114
|
115115
LL | baz((0..10), (), ('a'..='z').collect::<Vec<_>>())
116116
| ^^^^^^^^^^^^^^^^^^^^ help: remove this call

0 commit comments

Comments
 (0)