Skip to content

Commit c52ebf2

Browse files
committed
new lint to use contains() instead of iter().any() for u8 and i8 slices
1 parent c2d23ad commit c52ebf2

File tree

6 files changed

+125
-0
lines changed

6 files changed

+125
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5455,6 +5455,7 @@ Released 2018-09-13
54555455
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
54565456
[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty
54575457
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
5458+
[`contains_for_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#contains_for_slice
54585459
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
54595460
[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
54605461
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
371371
crate::methods::CLONE_ON_REF_PTR_INFO,
372372
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
373373
crate::methods::CONST_IS_EMPTY_INFO,
374+
crate::methods::CONTAINS_FOR_SLICE_INFO,
374375
crate::methods::DRAIN_COLLECT_INFO,
375376
crate::methods::ERR_EXPECT_INFO,
376377
crate::methods::EXPECT_FUN_CALL_INFO,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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::{CONTAINS_FOR_SLICE, 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+
// check if the receiver is a u8/i8 slice
24+
let ref_type = cx.typeck_results().expr_ty(recv);
25+
26+
match ref_type.kind() {
27+
ty::Ref(_, inner_type, _)
28+
if inner_type.is_slice()
29+
&& let ty::Slice(slice_type) = inner_type.kind()
30+
&& (slice_type.to_string() == "u8" || slice_type.to_string() == "i8") =>
31+
{
32+
span_lint(
33+
cx,
34+
CONTAINS_FOR_SLICE,
35+
expr.span,
36+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient",
37+
);
38+
},
39+
_ => {},
40+
}
41+
}
42+
}

clippy_lints/src/methods/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod clone_on_copy;
1414
mod clone_on_ref_ptr;
1515
mod cloned_instead_of_copied;
1616
mod collapsible_str_replace;
17+
mod contains_for_slice;
1718
mod drain_collect;
1819
mod err_expect;
1920
mod expect_fun_call;
@@ -4284,6 +4285,31 @@ declare_clippy_lint! {
42844285
"map of a trivial closure (not dependent on parameter) over a range"
42854286
}
42864287

4288+
declare_clippy_lint! {
4289+
/// ### What it does
4290+
/// Checks for usage of `iter().any()` on slices of `u8` or `i8` and suggests using `contains()` instead.
4291+
///
4292+
/// ### Why is this bad?
4293+
/// `iter().any()` on slices of `u8` or `i8` is optimized to use `memchr`.
4294+
///
4295+
/// ### Example
4296+
/// ```no_run
4297+
/// fn foo(values: &[u8]) -> bool {
4298+
/// values.iter().any(|&v| v == 10)
4299+
/// }
4300+
/// ```
4301+
/// Use instead:
4302+
/// ```no_run
4303+
/// fn foo(values: &[u8]) -> bool {
4304+
/// values.contains(&10)
4305+
/// }
4306+
/// ```
4307+
#[clippy::version = "1.85.0"]
4308+
pub CONTAINS_FOR_SLICE,
4309+
perf,
4310+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient"
4311+
}
4312+
42874313
pub struct Methods {
42884314
avoid_breaking_exported_api: bool,
42894315
msrv: Msrv,
@@ -4449,6 +4475,7 @@ impl_lint_pass!(Methods => [
44494475
MAP_ALL_ANY_IDENTITY,
44504476
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
44514477
UNNECESSARY_MAP_OR,
4478+
CONTAINS_FOR_SLICE,
44524479
]);
44534480

44544481
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4683,6 +4710,7 @@ impl Methods {
46834710
("any", [arg]) => {
46844711
unused_enumerate_index::check(cx, expr, recv, arg);
46854712
needless_character_iteration::check(cx, expr, recv, arg, false);
4713+
contains_for_slice::check(cx, expr);
46864714
match method_call(recv) {
46874715
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
46884716
cx,

tests/ui/contains_for_slice.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![warn(clippy::contains_for_slice)]
2+
3+
fn main() {
4+
let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
5+
let values = &vec[..];
6+
let _ = values.iter().any(|&v| v == 4);
7+
//~^ ERROR: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
8+
9+
let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
10+
let values = &vec[..];
11+
let _ = values.contains(&4);
12+
// no error, because it uses `contains()`
13+
14+
let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
15+
let values = &vec[..];
16+
let _ = values.iter().any(|&v| v == 4);
17+
// no error, because it's not a slice of u8/i8
18+
19+
let values: [u8; 6] = [3, 14, 15, 92, 6, 5];
20+
let _ = values.iter().any(|&v| v == 10);
21+
// no error, because it's an array
22+
23+
let values: [u8; 6] = [3, 14, 15, 92, 6, 5];
24+
let _ = values.iter().any(|&v| v > 10);
25+
// no error, because this `any()` is not for equality
26+
}
27+
28+
fn foo(values: &[u8]) -> bool {
29+
values.iter().any(|&v| v == 10)
30+
//~^ ERROR: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
31+
}
32+
33+
fn bar(values: [u8; 3]) -> bool {
34+
values.iter().any(|&v| v == 10)
35+
// no error, because it's an array
36+
}

tests/ui/contains_for_slice.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
2+
--> tests/ui/contains_for_slice.rs:6:13
3+
|
4+
LL | let _ = values.iter().any(|&v| v == 4);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::contains-for-slice` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::contains_for_slice)]`
9+
10+
error: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
11+
--> tests/ui/contains_for_slice.rs:29:5
12+
|
13+
LL | values.iter().any(|&v| v == 10)
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)