Skip to content

Commit 210a439

Browse files
committed
Add lint to suggest as_chunks over chunks_exact with constant
Suggest using slice.as_chunks::<N>() when chunks_exact(N) is called with a compile-time constant. This provides better ergonomics and type safety. Fixes #15882
1 parent e629869 commit 210a439

File tree

8 files changed

+150
-1
lines changed

8 files changed

+150
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6252,6 +6252,7 @@ Released 2018-09-13
62526252
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
62536253
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
62546254
[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
6255+
[`chunks_exact_to_as_chunks`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_to_as_chunks
62556256
[`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain
62566257
[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
62576258
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
352352
crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
353353
crate::methods::CHARS_LAST_CMP_INFO,
354354
crate::methods::CHARS_NEXT_CMP_INFO,
355+
crate::methods::CHUNKS_EXACT_TO_AS_CHUNKS_INFO,
355356
crate::methods::CLEAR_WITH_DRAIN_INFO,
356357
crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
357358
crate::methods::CLONE_ON_COPY_INFO,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use clippy_utils::consts::{ConstEvalCtxt, Constant};
2+
use clippy_utils::diagnostics::span_lint_and_help;
3+
use clippy_utils::msrvs::{self, Msrv};
4+
use rustc_hir::Expr;
5+
use rustc_lint::LateContext;
6+
7+
use super::CHUNKS_EXACT_TO_AS_CHUNKS;
8+
9+
pub(super) fn check(
10+
cx: &LateContext<'_>,
11+
expr: &Expr<'_>,
12+
recv: &Expr<'_>,
13+
arg: &Expr<'_>,
14+
method_name: &str,
15+
msrv: &Msrv,
16+
) {
17+
// Check for Rust version
18+
if !msrv.meets(cx, msrvs::AS_CHUNKS) {
19+
return;
20+
}
21+
22+
// Check receiver is slice or array type
23+
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
24+
if !recv_ty.is_slice() && !recv_ty.is_array() {
25+
return;
26+
}
27+
28+
// Check if argument is a constant
29+
let constant_eval = ConstEvalCtxt::new(cx);
30+
if let Some(Constant::Int(chunk_size)) = constant_eval.eval(arg) {
31+
// Emit the lint
32+
let suggestion = if method_name == "chunks_exact_mut" {
33+
"as_chunks_mut"
34+
} else {
35+
"as_chunks"
36+
};
37+
38+
span_lint_and_help(
39+
cx,
40+
CHUNKS_EXACT_TO_AS_CHUNKS,
41+
expr.span,
42+
format!("using `{method_name}` with a constant chunk size"),
43+
None,
44+
format!("consider using `{suggestion}::<{chunk_size}>()` for better ergonomics"),
45+
);
46+
}
47+
}

clippy_lints/src/methods/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod chars_last_cmp;
99
mod chars_last_cmp_with_unwrap;
1010
mod chars_next_cmp;
1111
mod chars_next_cmp_with_unwrap;
12+
mod chunks_exact_to_as_chunks;
1213
mod clear_with_drain;
1314
mod clone_on_copy;
1415
mod clone_on_ref_ptr;
@@ -2088,6 +2089,32 @@ declare_clippy_lint! {
20882089
"replace `.bytes().nth()` with `.as_bytes().get()`"
20892090
}
20902091

2092+
declare_clippy_lint! {
2093+
/// ### What it does
2094+
/// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size.
2095+
///
2096+
/// ### Why is this bad?
2097+
/// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices.
2098+
/// It was stabilized in Rust 1.88.
2099+
///
2100+
/// ### Example
2101+
/// ```no_run
2102+
/// let slice = [1, 2, 3, 4, 5, 6];
2103+
/// let mut it = slice.chunks_exact(2);
2104+
/// for chunk in it {}
2105+
/// ```
2106+
/// Use instead:
2107+
/// ```no_run
2108+
/// let slice = [1, 2, 3, 4, 5, 6];
2109+
/// let (chunks, remainder) = slice.as_chunks::<2>();
2110+
/// for chunk in chunks {}
2111+
/// ```
2112+
#[clippy::version = "1.93.0"]
2113+
pub CHUNKS_EXACT_TO_AS_CHUNKS,
2114+
style,
2115+
"using `chunks_exact` with constant when `as_chunks` is more ergonomic"
2116+
}
2117+
20912118
declare_clippy_lint! {
20922119
/// ### What it does
20932120
/// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
@@ -5725,6 +5752,9 @@ impl Methods {
57255752
unwrap_expect_used::Variant::Unwrap,
57265753
);
57275754
},
5755+
(name, [arg]) if matches!(name.as_str(), "chunks_exact" | "chunks_exact_mut") => {
5756+
chunks_exact_to_as_chunks::check(cx, expr, recv, arg, name.as_str(), &self.msrv);
5757+
},
57285758
_ => {},
57295759
}
57305760
}

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ macro_rules! msrv_aliases {
2323

2424
// names may refer to stabilized feature flags or library items
2525
msrv_aliases! {
26-
1,88,0 { LET_CHAINS }
26+
1,88,0 { LET_CHAINS, AS_CHUNKS }
2727
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
2828
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
2929
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#![warn(clippy::chunks_exact_to_as_chunks)]
2+
#![allow(unused)]
3+
4+
fn main() {
5+
let slice = [1, 2, 3, 4, 5, 6, 7, 8];
6+
7+
// Should trigger lint - literal constant
8+
let mut it = slice.chunks_exact(4);
9+
//~^ ERROR: using `chunks_exact` with a constant chunk size
10+
for chunk in it {}
11+
12+
// Should trigger lint - const value
13+
const CHUNK_SIZE: usize = 4;
14+
let mut it = slice.chunks_exact(CHUNK_SIZE);
15+
//~^ ERROR: using `chunks_exact` with a constant chunk size
16+
for chunk in it {}
17+
18+
// Should NOT trigger - runtime value
19+
let size = 4;
20+
let mut it = slice.chunks_exact(size);
21+
for chunk in it {}
22+
23+
// Should trigger lint - with remainder
24+
let mut it = slice.chunks_exact(3);
25+
//~^ ERROR: using `chunks_exact` with a constant chunk size
26+
for chunk in &mut it {}
27+
for e in it.remainder() {}
28+
29+
// Should trigger - mutable variant
30+
let mut arr = [1, 2, 3, 4, 5, 6, 7, 8];
31+
let mut it = arr.chunks_exact_mut(4);
32+
//~^ ERROR: using `chunks_exact_mut` with a constant chunk size
33+
for chunk in it {}
34+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: using `chunks_exact` with a constant chunk size
2+
--> tests/ui/chunks_exact_to_as_chunks.rs:8:18
3+
|
4+
LL | let mut it = slice.chunks_exact(4);
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: consider using `as_chunks::<4>()` for better ergonomics
8+
= note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`
10+
11+
error: using `chunks_exact` with a constant chunk size
12+
--> tests/ui/chunks_exact_to_as_chunks.rs:14:18
13+
|
14+
LL | let mut it = slice.chunks_exact(CHUNK_SIZE);
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= help: consider using `as_chunks::<4>()` for better ergonomics
18+
19+
error: using `chunks_exact` with a constant chunk size
20+
--> tests/ui/chunks_exact_to_as_chunks.rs:24:18
21+
|
22+
LL | let mut it = slice.chunks_exact(3);
23+
| ^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= help: consider using `as_chunks::<3>()` for better ergonomics
26+
27+
error: using `chunks_exact_mut` with a constant chunk size
28+
--> tests/ui/chunks_exact_to_as_chunks.rs:31:18
29+
|
30+
LL | let mut it = arr.chunks_exact_mut(4);
31+
| ^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= help: consider using `as_chunks_mut::<4>()` for better ergonomics
34+
35+
error: aborting due to 4 previous errors
36+

tests/ui/explicit_write_in_test.stderr

Whitespace-only changes.

0 commit comments

Comments
 (0)