Skip to content

Commit 14694b1

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

File tree

6 files changed

+115
-0
lines changed

6 files changed

+115
-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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_hir::Expr;
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, _, _, _)) = method_call(expr)
12+
&& name == "any"
13+
// iter()
14+
&& let Some((name, recv, _, _, _)) = method_call(recv)
15+
&& name == "iter"
16+
{
17+
// check if the receiver is a u8/i8 slice
18+
let ref_type = cx.typeck_results().expr_ty(recv);
19+
20+
match ref_type.kind() {
21+
ty::Ref(_, inner_type, _)
22+
if inner_type.is_slice()
23+
&& let ty::Slice(slice_type) = inner_type.kind()
24+
&& (slice_type.to_string() == "u8" || slice_type.to_string() == "i8") =>
25+
{
26+
span_lint(
27+
cx,
28+
CONTAINS_FOR_SLICE,
29+
expr.span,
30+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient",
31+
);
32+
},
33+
_ => {},
34+
}
35+
}
36+
}

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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
24+
fn foo(values: &[u8]) -> bool {
25+
values.iter().any(|&v| v == 10)
26+
//~^ ERROR: using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient
27+
}
28+
29+
fn bar(values: [u8; 3]) -> bool {
30+
values.iter().any(|&v| v == 10)
31+
// no error, because it's an array
32+
}

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:25: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)