Skip to content

Commit 19357ca

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

File tree

11 files changed

+160
-85
lines changed

11 files changed

+160
-85
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,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
} else if let ty::Slice(slice_type) = inner_type.kind()
38+
&& slice_type.is_numeric()
39+
{
40+
span_lint(
41+
cx,
42+
UNNECESSARY_ITER_ANY,
43+
expr.span,
44+
"using `contains()` instead of `iter().any()` is more readable",
45+
);
46+
}
47+
},
48+
// if it's an array that uses `iter().any()` and its closure is an equality check, suggest using
49+
// `contains()` (currently only for numeric arrays because of the difficulty in determining whether
50+
// `contains()` can be replaced by `contains()` for arrays of general types)
51+
ty::Array(array_type, _) if array_type.is_numeric() => span_lint(
52+
cx,
53+
UNNECESSARY_ITER_ANY,
54+
expr.span,
55+
"using `contains()` instead of `iter().any()` is more readable",
56+
),
57+
_ => {},
58+
}
59+
}
60+
}

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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ fn main() {
1414
let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
1515
let values = &vec[..];
1616
let _ = values.iter().any(|&v| v == 4);
17-
// no error, because it's not a slice of u8/i8
17+
//~^ ERROR: using `contains()` instead of `iter().any()` is more readable
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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:16:13
12+
|
13+
LL | let _ = values.iter().any(|&v| v == 4);
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()` is more readable
20+
--> tests/ui/iter_any.rs:20:13
21+
|
22+
LL | let _ = values.iter().any(|&v| v == 10);
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
26+
--> tests/ui/iter_any.rs:29:5
27+
|
28+
LL | values.iter().any(|&v| v == 10)
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
error: using `contains()` instead of `iter().any()` is more readable
32+
--> tests/ui/iter_any.rs:34:5
33+
|
34+
LL | values.iter().any(|&v| v == 10)
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
37+
error: aborting due to 5 previous errors
38+

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();

0 commit comments

Comments
 (0)