Skip to content

Commit

Permalink
ignore manual_slice_fill in other test files
Browse files Browse the repository at this point in the history
  • Loading branch information
lapla-cogito committed Jan 30, 2025
1 parent 609839e commit 801090f
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 76 deletions.
107 changes: 72 additions & 35 deletions clippy_lints/src/loops/manual_slice_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{HasSession, snippet_with_applicability};
use clippy_utils::ty::implements_trait;
use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment};
use rustc_ast::RangeLimits;
use rustc_ast::ast::LitKind;
use rustc_ast::{RangeLimits, UnOp};
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::QPath::Resolved;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, Pat};
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;
use rustc_span::sym;
Expand All @@ -19,55 +20,91 @@ use super::MANUAL_SLICE_FILL;

pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
msrv: &Msrv,
) {
if msrv.meets(msrvs::SLICE_FILL)
&& let Some(higher::Range {
start: Some(start),
end: Some(end),
limits: RangeLimits::HalfOpen,
}) = higher::Range::hir(arg)
if !msrv.meets(msrvs::SLICE_FILL) {
return;
}

// `for _ in 0..slice.len() { slice[_] = value; }`
if let Some(higher::Range {
start: Some(start),
end: Some(end),
limits: RangeLimits::HalfOpen,
}) = higher::Range::hir(arg)
&& let ExprKind::Lit(Spanned {
node: LitKind::Int(Pu128(0), _),
..
}) = start.kind
&& let ExprKind::Block(_, _) = body.kind
&& let ExprKind::Block(..) = body.kind
// Check if the body is an assignment to a slice element.
&& let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind
&& let ExprKind::Index(slice, _, _) = assignee.kind
&& let ExprKind::MethodCall(path, recv, _, _) = end.kind
&& let ExprKind::Path(Resolved(_, recvpath)) = recv.kind
&& let ExprKind::Path(Resolved(_, slicepath)) = slice.kind
// Check if `len()` is used for the range end.
&& let ExprKind::MethodCall(path, recv,..) = end.kind
&& path.ident.name == sym::len
// Check if the slice which is being assigned to is the same as the one being iterated over.
&& let ExprKind::Path(Resolved(_, recv_path)) = recv.kind
&& let ExprKind::Path(Resolved(_, slice_path)) = slice.kind
&& recv_path.res == slice_path.res
&& !assignval.span.from_expansion()
&& switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span)
&& path.ident.name == sym::len
// Ensure that the slice used as the range in the loop matches the slice being manipulated inside the loop.
&& recvpath.res == slicepath.res
&& slice.span.eq_ctxt(path.ident.span)
// The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[])
{
let mut app = if span_contains_comment(cx.sess().source_map(), body.span) {
Applicability::MaybeIncorrect // Comments may be informational.
} else {
Applicability::MachineApplicable
};

span_lint_and_sugg(
cx,
MANUAL_SLICE_FILL,
expr.span,
"manually filling a slice",
"try",
format!(
"{}.fill({});",
snippet_with_applicability(cx, slice.span, "..", &mut app),
snippet_with_applicability(cx, assignval.span, "..", &mut app),
),
app,
);
sugg(cx, body, expr, slice.span, assignval.span);
}
// `for _ in &mut slice { *_ = value; }`
else if let ExprKind::AddrOf(_, _, recv) = arg.kind
// Check if the body is an assignment to a slice element.
&& let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind
&& let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind
&& let ExprKind::Path(Resolved(_, recv_path)) = recv.kind
// Check if the slice which is being assigned to is the same as the one being iterated over.
&& let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind
&& let Res::Local(local) = slice_path.res
&& local == pat.hir_id
&& !assignval.span.from_expansion()
&& switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span)
// The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[])
{
sugg(cx, body, expr, recv_path.span, assignval.span);
}
}

fn sugg<'tcx>(
cx: &LateContext<'tcx>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
slice_span: rustc_span::Span,
assignval_span: rustc_span::Span,
) {
let mut app = if span_contains_comment(cx.sess().source_map(), body.span) {
Applicability::MaybeIncorrect // Comments may be informational.
} else {
Applicability::MachineApplicable
};

span_lint_and_sugg(
cx,
MANUAL_SLICE_FILL,
expr.span,
"manually filling a slice",
"try",
format!(
"{}.fill({});",
snippet_with_applicability(cx, slice_span, "..", &mut app),
snippet_with_applicability(cx, assignval_span, "..", &mut app),
),
app,
);
}
2 changes: 1 addition & 1 deletion clippy_lints/src/loops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ impl Loops {
) {
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
if !is_manual_memcpy_triggered {
manual_slice_fill::check(cx, arg, body, expr, &self.msrv);
manual_slice_fill::check(cx, pat, arg, body, expr, &self.msrv);
needless_range_loop::check(cx, pat, arg, body, expr);
explicit_counter_loop::check(cx, pat, arg, body, expr, label);
}
Expand Down
7 changes: 6 additions & 1 deletion tests/ui/manual_memcpy/without_loop_counters.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#![warn(clippy::manual_memcpy)]
#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)]
#![allow(
clippy::assigning_clones,
clippy::useless_vec,
clippy::needless_range_loop,
clippy::manual_slice_fill
)]

//@no-rustfix
const LOOP_OFFSET: usize = 5000;
Expand Down
36 changes: 18 additions & 18 deletions tests/ui/manual_memcpy/without_loop_counters.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:9:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:14:5
|
LL | / for i in 0..src.len() {
LL | |
Expand All @@ -12,7 +12,7 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:16:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:21:5
|
LL | / for i in 0..src.len() {
LL | |
Expand All @@ -21,7 +21,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:22:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:27:5
|
LL | / for i in 0..src.len() {
LL | |
Expand All @@ -30,7 +30,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:28:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:33:5
|
LL | / for i in 11..src.len() {
LL | |
Expand All @@ -39,7 +39,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:34:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:39:5
|
LL | / for i in 0..dst.len() {
LL | |
Expand All @@ -48,7 +48,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:48:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:53:5
|
LL | / for i in 10..256 {
LL | |
Expand All @@ -64,7 +64,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]);
|

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:61:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:66:5
|
LL | / for i in 10..LOOP_OFFSET {
LL | |
Expand All @@ -73,7 +73,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:75:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:80:5
|
LL | / for i in 0..src_vec.len() {
LL | |
Expand All @@ -82,7 +82,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:105:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:110:5
|
LL | / for i in from..from + src.len() {
LL | |
Expand All @@ -91,7 +91,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:110:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:115:5
|
LL | / for i in from..from + 3 {
LL | |
Expand All @@ -100,7 +100,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:116:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:121:5
|
LL | / for i in 0..5 {
LL | |
Expand All @@ -109,7 +109,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:122:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:127:5
|
LL | / for i in 0..0 {
LL | |
Expand All @@ -118,7 +118,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:146:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:151:5
|
LL | / for i in 0..4 {
LL | |
Expand All @@ -127,7 +127,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:152:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:157:5
|
LL | / for i in 0..5 {
LL | |
Expand All @@ -136,7 +136,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:158:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:163:5
|
LL | / for i in 0..5 {
LL | |
Expand All @@ -145,7 +145,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:205:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:210:5
|
LL | / for i in 0..5 {
LL | |
Expand All @@ -154,7 +154,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:211:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:216:5
|
LL | / for i in 0..5 {
LL | |
Expand All @@ -163,7 +163,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0][1]);`

error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:219:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:224:5
|
LL | / for i in 0..src.len() {
LL | |
Expand Down
36 changes: 35 additions & 1 deletion tests/ui/manual_slice_fill.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ macro_rules! assign_element {
};
}

macro_rules! assign_element_2 {
($i:expr) => {
$i = 0;
};
}

struct NoClone;

fn num() -> usize {
5
}
Expand All @@ -19,6 +27,8 @@ fn should_lint() {
let x = 5;
some_slice.fill(x);

some_slice.fill(0);

// This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments
// within the loop might be purely informational.
some_slice.fill(0);
Expand Down Expand Up @@ -58,10 +68,34 @@ fn should_not_lint() {
some_slice[i] = 0;
}

struct NoClone;
let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in 0..vec.len() {
vec[i] = None;
}

// Should not lint, as using a function to assign values to elements might be
// intentional. For example, the contents of `num()` could be temporary and subject to change
// later.
for i in &mut some_slice {
*i = num();
}

// Should not lint because this loop isn't equivalent to `fill`.
for i in &mut some_slice {
*i = 0;
println!("foo");
}

// Should not lint because it may be intentional to use a macro to perform an operation equivalent
// to `fill`.
for i in &mut some_slice {
assign_element_2!(*i);
}

let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in &mut vec {
*i = None;
}
}
Loading

0 comments on commit 801090f

Please sign in to comment.