Skip to content

Commit acb27cb

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

File tree

6 files changed

+118
-0
lines changed

6 files changed

+118
-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
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::methods::method_call;
2+
use clippy_utils::diagnostics::span_lint;
3+
use rustc_hir::Expr;
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::ty::{self};
6+
use rustc_session::declare_lint_pass;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for usage of `iter().any()` on slices of `u8` or `i8` and suggests using `contains()` instead.
11+
///
12+
/// ### Why is this bad?
13+
/// `iter().any()` on slices of `u8` or `i8` is optimized to use `memchr`.
14+
///
15+
/// ### Example
16+
/// ```no_run
17+
/// fn foo(values: &[u8]) -> bool {
18+
/// values.iter().any(|&v| v == 10)
19+
/// }
20+
/// ```
21+
/// Use instead:
22+
/// ```no_run
23+
/// fn foo(values: &[u8]) -> bool {
24+
/// values.contains(&10)
25+
/// }
26+
/// ```
27+
#[clippy::version = "1.85.0"]
28+
pub CONTAINS_FOR_SLICE,
29+
perf,
30+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient"
31+
}
32+
33+
declare_lint_pass!(ContainsForSlice => [CONTAINS_FOR_SLICE]);
34+
35+
impl LateLintPass<'_> for ContainsForSlice {
36+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
37+
if !expr.span.from_expansion()
38+
// any()
39+
&& let Some((name, recv, _, _, _)) = method_call(expr)
40+
&& name == "any"
41+
// iter()
42+
&& let Some((name, recv, _, _, _)) = method_call(recv)
43+
&& name == "iter"
44+
{
45+
// check if the receiver is a u8/i8 slice
46+
let ref_type = cx.typeck_results().expr_ty(recv);
47+
48+
match ref_type.kind() {
49+
ty::Ref(_, inner_type, _) if inner_type.is_slice() => {
50+
if let ty::Slice(slice_type) = inner_type.kind()
51+
&& (slice_type.to_string() == "u8" || slice_type.to_string() == "i8")
52+
{
53+
span_lint(
54+
cx,
55+
CONTAINS_FOR_SLICE,
56+
expr.span,
57+
"using `contains()` instead of `iter().any()` on u8/i8 slices is more efficient",
58+
);
59+
}
60+
},
61+
_ => {},
62+
}
63+
}
64+
}
65+
}

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
107107
crate::collapsible_if::COLLAPSIBLE_IF_INFO,
108108
crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
109109
crate::comparison_chain::COMPARISON_CHAIN_INFO,
110+
crate::contains_for_slice::CONTAINS_FOR_SLICE_INFO,
110111
crate::copies::BRANCHES_SHARING_CODE_INFO,
111112
crate::copies::IFS_SAME_COND_INFO,
112113
crate::copies::IF_SAME_THEN_ELSE_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ mod cognitive_complexity;
100100
mod collapsible_if;
101101
mod collection_is_never_read;
102102
mod comparison_chain;
103+
mod contains_for_slice;
103104
mod copies;
104105
mod copy_iterator;
105106
mod crate_in_macro_def;
@@ -967,5 +968,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
967968
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
968969
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
969970
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
971+
store.register_late_pass(|_| Box::new(contains_for_slice::ContainsForSlice));
970972
// add lints here, do not remove this comment, it's used in `new_lint`
971973
}

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)