Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6252,6 +6252,7 @@ Released 2018-09-13
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
[`chunks_exact_with_const_size`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_with_const_size
[`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain
[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
crate::methods::CHARS_LAST_CMP_INFO,
crate::methods::CHARS_NEXT_CMP_INFO,
crate::methods::CHUNKS_EXACT_WITH_CONST_SIZE_INFO,
crate::methods::CLEAR_WITH_DRAIN_INFO,
crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
crate::methods::CLONE_ON_COPY_INFO,
Expand Down
47 changes: 47 additions & 0 deletions clippy_lints/src/methods/chunks_exact_with_const_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::sym;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_span::Symbol;

use super::CHUNKS_EXACT_WITH_CONST_SIZE;

pub(super) fn check(
cx: &LateContext<'_>,
expr: &Expr<'_>,
recv: &Expr<'_>,
arg: &Expr<'_>,
method_name: Symbol,
msrv: Msrv,
) {
// Check if receiver is slice-like
if !cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() {
return;
}

// Check if argument is a constant
let constant_eval = ConstEvalCtxt::new(cx);
if let Some(Constant::Int(_)) = constant_eval.eval(arg) {
// Emit the lint
let suggestion = if method_name == sym::chunks_exact_mut {
"as_chunks_mut"
} else {
"as_chunks"
};
let arg_str = snippet(cx, arg.span, "_");
span_lint_and_help(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be pretty effortlessly switched over to give suggestions, which are quite nice to have. https://doc.rust-lang.org/clippy/development/emitting_lints.html#suggestions-automatic-fixes should help you in that, but don't hesitate to ask if something's unclear

cx,
CHUNKS_EXACT_WITH_CONST_SIZE,
expr.span,
format!("using `{method_name}` with a constant chunk size"),
None,
format!("consider using `{suggestion}::<{arg_str}>()` for better ergonomics"),
);
}

// Check for Rust version
if !msrv.meets(cx, msrvs::AS_CHUNKS) {}
}
31 changes: 31 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod chars_last_cmp;
mod chars_last_cmp_with_unwrap;
mod chars_next_cmp;
mod chars_next_cmp_with_unwrap;
mod chunks_exact_with_const_size;
mod clear_with_drain;
mod clone_on_copy;
mod clone_on_ref_ptr;
Expand Down Expand Up @@ -2087,6 +2088,32 @@ declare_clippy_lint! {
"replace `.bytes().nth()` with `.as_bytes().get()`"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size.
///
/// ### Why is this bad?
/// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices.
/// It was stabilized in Rust 1.88.
///
/// ### Example
/// ```no_run
/// let slice = [1, 2, 3, 4, 5, 6];
/// let mut it = slice.chunks_exact(2);
/// for chunk in it {}
/// ```
/// Use instead:
/// ```no_run
/// let slice = [1, 2, 3, 4, 5, 6];
/// let (chunks, remainder) = slice.as_chunks::<2>();
/// for chunk in chunks {}
/// ```
#[clippy::version = "1.93.0"]
pub CHUNKS_EXACT_WITH_CONST_SIZE,
style,
"using `chunks_exact` with constant when `as_chunks` is more ergonomic"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
Expand Down Expand Up @@ -4787,6 +4814,7 @@ impl_lint_pass!(Methods => [
ITER_NTH,
ITER_NTH_ZERO,
BYTES_NTH,
CHUNKS_EXACT_WITH_CONST_SIZE,
ITER_SKIP_NEXT,
GET_UNWRAP,
GET_LAST_WITH_LEN,
Expand Down Expand Up @@ -5715,6 +5743,9 @@ impl Methods {
unwrap_expect_used::Variant::Unwrap,
);
},
(name @ (sym::chunks_exact | sym::chunks_exact_mut), [arg]) => {
chunks_exact_with_const_size::check(cx, expr, recv, arg, name, self.msrv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the span that the lint points at is the whole method call expression (expr.span) -- as the last result in Lintcheck (https://github.com/rust-lang/rust-clippy/actions/runs/19080004877#user-content-chunks-exact-with-const-size) shows, that might lead to a somewhat confusing diagnostics:

warning: chunks_exact_with_const_size
   --> target/lintcheck/sources/rustc-demangle-0.1.24/src/v0.rs:299:25
    |
299 |           let mut bytes = self
    |  _________________________^
300 | |             .nibbles
301 | |             .as_bytes()
302 | |             .chunks_exact(2)
    | |____________________________^
    |
    = help: consider using `as_chunks::<2>()` for better ergonomics
    = note: `--force-warn clippy::chunks-exact-with-const-size` implied by `--force-warn clippy::all`

You can instead only highlight the method call (in this case, chunks_exact(2), by using call_span, which comes from the call to method_call(expr) above (line 13) -- see filter_map_bool_then for a (somewhat) simple example

},
_ => {},
}
}
Expand Down
2 changes: 1 addition & 1 deletion clippy_utils/src/msrvs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ macro_rules! msrv_aliases {

// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,88,0 { LET_CHAINS }
1,88,0 { LET_CHAINS, AS_CHUNKS }
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
Expand Down
2 changes: 2 additions & 0 deletions clippy_utils/src/sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ generate! {
checked_pow,
checked_rem_euclid,
checked_sub,
chunks_exact,
chunks_exact_mut,
clamp,
clippy_utils,
clone_into,
Expand Down
34 changes: 34 additions & 0 deletions tests/ui/chunks_exact_with_const_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![warn(clippy::chunks_exact_with_const_size)]
#![allow(unused)]

fn main() {
let slice = [1, 2, 3, 4, 5, 6, 7, 8];

// Should trigger lint - literal constant
let mut it = slice.chunks_exact(4);
//~^ chunks_exact_with_const_size
for chunk in it {}

// Should trigger lint - const value
const CHUNK_SIZE: usize = 4;
let mut it = slice.chunks_exact(CHUNK_SIZE);
//~^ chunks_exact_with_const_size
for chunk in it {}

// Should NOT trigger - runtime value
let size = 4;
let mut it = slice.chunks_exact(size);
for chunk in it {}

// Should trigger lint - with remainder
let mut it = slice.chunks_exact(3);
//~^ chunks_exact_with_const_size
for chunk in &mut it {}
for e in it.remainder() {}

// Should trigger - mutable variant
let mut arr = [1, 2, 3, 4, 5, 6, 7, 8];
let mut it = arr.chunks_exact_mut(4);
//~^ chunks_exact_with_const_size
for chunk in it {}
}
36 changes: 36 additions & 0 deletions tests/ui/chunks_exact_with_const_size.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
error: using `chunks_exact` with a constant chunk size
--> tests/ui/chunks_exact_with_const_size.rs:8:18
|
LL | let mut it = slice.chunks_exact(4);
| ^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `as_chunks::<4>()` for better ergonomics
= note: `-D clippy::chunks-exact-with-const-size` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::chunks_exact_with_const_size)]`

error: using `chunks_exact` with a constant chunk size
--> tests/ui/chunks_exact_with_const_size.rs:14:18
|
LL | let mut it = slice.chunks_exact(CHUNK_SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `as_chunks::<CHUNK_SIZE>()` for better ergonomics

error: using `chunks_exact` with a constant chunk size
--> tests/ui/chunks_exact_with_const_size.rs:24:18
|
LL | let mut it = slice.chunks_exact(3);
| ^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `as_chunks::<3>()` for better ergonomics

error: using `chunks_exact_mut` with a constant chunk size
--> tests/ui/chunks_exact_with_const_size.rs:31:18
|
LL | let mut it = arr.chunks_exact_mut(4);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `as_chunks_mut::<4>()` for better ergonomics

error: aborting due to 4 previous errors

Empty file.