From 8d2de2185b754619b454b162cbb33a0e592994d5 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Mon, 6 Apr 2026 20:44:54 +0000 Subject: [PATCH 1/3] Apply `byte_char_slices` to Clippy itself --- clippy_lints/src/returns/needless_return.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs index 04e4f379e37c..619a70cd8dd1 100644 --- a/clippy_lints/src/returns/needless_return.rs +++ b/clippy_lints/src/returns/needless_return.rs @@ -259,7 +259,7 @@ fn emit_return_lint( // Go backwards while encountering whitespace and extend the given Span to that point. fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [b' ', b'\t', b'\n']; + let ws = *b" \t\n"; if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { let len = prev_source.len() - non_ws_pos - 1; return sp.with_lo(sp.lo() - BytePos::from_usize(len)); From d0c0f3a308b71cf87b8e81a86c3d5025a2fd71de Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 26 Mar 2026 22:03:08 +0000 Subject: [PATCH 2/3] Enhance `byte_char_slices` to cover arrays --- clippy_lints/src/byte_char_slices.rs | 92 +++++++++++++++++++--------- clippy_lints/src/lib.rs | 2 +- tests/ui/byte_char_slices.fixed | 40 +++++++++++- tests/ui/byte_char_slices.rs | 38 +++++++++++- tests/ui/byte_char_slices.stderr | 43 +++++++++---- tests/ui/ptr_offset_by_literal.fixed | 2 +- tests/ui/ptr_offset_by_literal.rs | 2 +- 7 files changed, 170 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs index 6c023189f69e..5104594790fe 100644 --- a/clippy_lints/src/byte_char_slices.rs +++ b/clippy_lints/src/byte_char_slices.rs @@ -1,9 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{BorrowKind, Expr, ExprKind, Mutability}; -use rustc_ast::token::{Lit, LitKind}; +use std::borrow::Cow; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, span_contains_cfg, span_contains_comment}; +use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -30,47 +36,73 @@ declare_clippy_lint! { declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]); -impl EarlyLintPass for ByteCharSlice { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { +impl<'tcx> LateLintPass<'tcx> for ByteCharSlice { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if !expr.span.from_expansion() - && let Some(slice) = is_byte_char_slices(expr) + && let Some((has_ref, slice)) = is_byte_char_slices(cx, expr) { - span_lint_and_sugg( + span_lint_and_then( cx, BYTE_CHAR_SLICES, expr.span, "can be more succinctly written as a byte str", - "try", - format!("b\"{slice}\""), - Applicability::MachineApplicable, + |diag| { + let mut app = Applicability::MachineApplicable; + let mut sugg = Sugg::hir_from_snippet(cx, expr, |_| { + let mut slice = slice.iter().fold("b\"".to_owned(), |mut acc, span| { + let snippet = snippet_with_applicability(cx, *span, "b'?'", &mut app); + acc.push_str(match &snippet[2..snippet.len() - 1] { + "\"" => "\\\"", + "\\'" => "'", + other => other, + }); + acc + }); + slice.push('"'); + Cow::Owned(slice) + }); + if !has_ref && !cx.typeck_results().expr_ty_adjusted(expr).is_array_slice() { + sugg = sugg.deref(); + } + + diag.span_suggestion(expr.span, "try", sugg, app); + }, ); } } } /// Checks whether the slice is that of byte chars, and if so, builds a byte-string out of it -fn is_byte_char_slices(expr: &Expr) -> Option { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind - && let ExprKind::Array(members) = &expr.kind +fn is_byte_char_slices<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<(bool, Vec)> { + let (has_ref, expr) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = expr.kind { + (true, inner) + } else if let Some(parent) = get_parent_expr(cx, expr) // Already checked by the parent expr. + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind + { + return None; + } else { + (false, expr) + }; + + if let ExprKind::Array(members) = expr.kind && !members.is_empty() + && !span_contains_comment(cx, expr.span) + && !span_contains_cfg(cx, expr.span) { - members + return members .iter() - .map(|member| match &member.kind { - ExprKind::Lit(Lit { - kind: LitKind::Byte, - symbol, - .. - }) => Some(symbol.as_str()), - _ => None, - }) - .map(|maybe_quote| match maybe_quote { - Some("\"") => Some("\\\""), - Some("\\'") => Some("'"), - other => other, + .try_fold(Vec::new(), |mut acc, member| { + if let ExprKind::Lit(lit) = member.kind + && let LitKind::Byte(_) = lit.node + && expr.span.eq_ctxt(member.span) + { + acc.push(lit.span); + return Some(acc); + } + None }) - .collect::>() - } else { - None + .map(|s| (has_ref, s)); } + + None } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 61c54678c4b2..72ee5cca0397 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -515,7 +515,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(|| Box::new(visibility::Visibility)), Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)), Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)), - Box::new(|| Box::new(byte_char_slices::ByteCharSlice)), Box::new(|| Box::new(cfg_not_test::CfgNotTest)), Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())), // add early passes here, used by `cargo dev new_lint` @@ -867,6 +866,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)), Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))), Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))), + Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)), // add late passes here, used by `cargo dev new_lint` ]; store.late_passes.extend(late_lints); diff --git a/tests/ui/byte_char_slices.fixed b/tests/ui/byte_char_slices.fixed index 87934d6362f7..661ae8fc85fe 100644 --- a/tests/ui/byte_char_slices.fixed +++ b/tests/ui/byte_char_slices.fixed @@ -1,4 +1,5 @@ #![warn(clippy::byte_char_slices)] +#![allow(clippy::useless_vec)] fn main() { let bad = b"abc"; @@ -11,7 +12,40 @@ fn main() { //~^ byte_char_slices let good = &[b'a', 0x42]; - let good = [b'a', b'a']; - //~^ useless_vec - let good: u8 = [b'a', b'c'].into_iter().sum(); + let good = vec![b'a', b'a']; +} + +fn takes_array_ref(_: &[u8; 2]) {} + +fn takes_array_ref_ref(_: &&[u8; 2]) {} + +fn issue16759(bytes: [u32; 3]) { + const START: u32 = u32::from_le_bytes(*b"WORK"); + //~^ byte_char_slices + + let auto_deref_to_slice: u8 = b"ac".iter().copied().sum(); + //~^ byte_char_slices + + let with_comment = [ + // 1 2 3 + b'a', b'b', b'c', // x + b'd', b'e', b'f', // 2x + b'g', b'h', b'i', // 3x + ]; + let with_cfg = [ + b'a', + b'b', + b'c', + #[cfg(feature = "foo")] + b'd', + ]; + + let with_escape: u8 = b"'\"\x00\n\\".iter().copied().sum(); + //~^ byte_char_slices + + takes_array_ref(b"ab"); + //~^ byte_char_slices + + takes_array_ref_ref(&b"ab"); + //~^ byte_char_slices } diff --git a/tests/ui/byte_char_slices.rs b/tests/ui/byte_char_slices.rs index 0de7cf66fda8..e8dc9e9611de 100644 --- a/tests/ui/byte_char_slices.rs +++ b/tests/ui/byte_char_slices.rs @@ -1,4 +1,5 @@ #![warn(clippy::byte_char_slices)] +#![allow(clippy::useless_vec)] fn main() { let bad = &[b'a', b'b', b'c']; @@ -12,6 +13,39 @@ fn main() { let good = &[b'a', 0x42]; let good = vec![b'a', b'a']; - //~^ useless_vec - let good: u8 = [b'a', b'c'].into_iter().sum(); +} + +fn takes_array_ref(_: &[u8; 2]) {} + +fn takes_array_ref_ref(_: &&[u8; 2]) {} + +fn issue16759(bytes: [u32; 3]) { + const START: u32 = u32::from_le_bytes([b'W', b'O', b'R', b'K']); + //~^ byte_char_slices + + let auto_deref_to_slice: u8 = [b'a', b'c'].iter().copied().sum(); + //~^ byte_char_slices + + let with_comment = [ + // 1 2 3 + b'a', b'b', b'c', // x + b'd', b'e', b'f', // 2x + b'g', b'h', b'i', // 3x + ]; + let with_cfg = [ + b'a', + b'b', + b'c', + #[cfg(feature = "foo")] + b'd', + ]; + + let with_escape: u8 = [b'\'', b'"', b'\x00', b'\n', b'\\'].iter().copied().sum(); + //~^ byte_char_slices + + takes_array_ref(&[b'a', b'b']); + //~^ byte_char_slices + + takes_array_ref_ref(&&[b'a', b'b']); + //~^ byte_char_slices } diff --git a/tests/ui/byte_char_slices.stderr b/tests/ui/byte_char_slices.stderr index c1b7e4ca2f17..0f72cf13a0b6 100644 --- a/tests/ui/byte_char_slices.stderr +++ b/tests/ui/byte_char_slices.stderr @@ -1,5 +1,5 @@ error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:4:15 + --> tests/ui/byte_char_slices.rs:5:15 | LL | let bad = &[b'a', b'b', b'c']; | ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"` @@ -8,31 +8,52 @@ LL | let bad = &[b'a', b'b', b'c']; = help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:6:18 + --> tests/ui/byte_char_slices.rs:7:18 | LL | let quotes = &[b'"', b'H', b'i']; | ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:8:18 + --> tests/ui/byte_char_slices.rs:9:18 | LL | let quotes = &[b'\'', b'S', b'u', b'p']; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:10:19 + --> tests/ui/byte_char_slices.rs:11:19 | LL | let escapes = &[b'\x42', b'E', b's', b'c']; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"` -error: useless use of `vec!` - --> tests/ui/byte_char_slices.rs:14:16 +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:23:43 + | +LL | const START: u32 = u32::from_le_bytes([b'W', b'O', b'R', b'K']); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*b"WORK"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:26:35 + | +LL | let auto_deref_to_slice: u8 = [b'a', b'c'].iter().copied().sum(); + | ^^^^^^^^^^^^ help: try: `b"ac"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:43:27 + | +LL | let with_escape: u8 = [b'\'', b'"', b'\x00', b'\n', b'\\'].iter().copied().sum(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'\"\x00\n\\"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:46:21 | -LL | let good = vec![b'a', b'a']; - | ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']` +LL | takes_array_ref(&[b'a', b'b']); + | ^^^^^^^^^^^^^ help: try: `b"ab"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:49:26 | - = note: `-D clippy::useless-vec` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +LL | takes_array_ref_ref(&&[b'a', b'b']); + | ^^^^^^^^^^^^^ help: try: `b"ab"` -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/ptr_offset_by_literal.fixed b/tests/ui/ptr_offset_by_literal.fixed index bd9e41def938..174616b1e151 100644 --- a/tests/ui/ptr_offset_by_literal.fixed +++ b/tests/ui/ptr_offset_by_literal.fixed @@ -1,5 +1,5 @@ #![warn(clippy::ptr_offset_by_literal)] -#![allow(clippy::inconsistent_digit_grouping)] +#![allow(clippy::inconsistent_digit_grouping, clippy::byte_char_slices)] fn main() { let arr = [b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_by_literal.rs b/tests/ui/ptr_offset_by_literal.rs index b8e3f9b26c68..d3202cbff982 100644 --- a/tests/ui/ptr_offset_by_literal.rs +++ b/tests/ui/ptr_offset_by_literal.rs @@ -1,5 +1,5 @@ #![warn(clippy::ptr_offset_by_literal)] -#![allow(clippy::inconsistent_digit_grouping)] +#![allow(clippy::inconsistent_digit_grouping, clippy::byte_char_slices)] fn main() { let arr = [b'a', b'b', b'c']; From 118cbecf15740504332191c624d1f1408cf537ed Mon Sep 17 00:00:00 2001 From: Denis Pakhomov Date: Sun, 1 Feb 2026 16:55:04 +0100 Subject: [PATCH 3/3] implement fn_arg_mut_rebindings lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/fn_arg_mut_rebindings.rs | 117 ++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/fn_arg_mut_rebindings.rs | 74 ++++++++++++++ tests/ui/fn_arg_mut_rebindings.stderr | 40 ++++++++ tests/ui/unused_io_amount.rs | 2 +- 7 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/fn_arg_mut_rebindings.rs create mode 100644 tests/ui/fn_arg_mut_rebindings.rs create mode 100644 tests/ui/fn_arg_mut_rebindings.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 748e283edffb..8b013bfdf24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6625,6 +6625,7 @@ Released 2018-09-13 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const [`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_arg_mut_rebindings`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_arg_mut_rebindings [`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c164241673a3..a087890fb743 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -168,6 +168,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::fn_arg_mut_rebindings::FN_ARG_MUT_REBINDINGS_INFO, crate::format::USELESS_FORMAT_INFO, crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, crate::format_args::POINTER_FORMAT_INFO, diff --git a/clippy_lints/src/fn_arg_mut_rebindings.rs b/clippy_lints/src/fn_arg_mut_rebindings.rs new file mode 100644 index 000000000000..7b59aacf6a14 --- /dev/null +++ b/clippy_lints/src/fn_arg_mut_rebindings.rs @@ -0,0 +1,117 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_enclosing_block; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{ + BindingMode, Body, BodyId, ExprKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, LetStmt, OwnerNode, + Param, Pat, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for function arguments declared as not mutable and later rebound as mutable. + /// + /// ### Why is this bad? + /// It can be easily improved by just declaring the function argument as mutable and + /// removing the unnecessary assignment. + /// + /// ### Example + /// ```no_run + /// fn foo_bad(bar: Vec) -> Vec { + /// let mut bar = bar; + /// bar.push(42); + /// bar + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn foo(mut bar: Vec) -> Vec { + /// bar.push(42); + /// bar + /// } + /// ``` + #[clippy::version = "1.96.0"] + pub FN_ARG_MUT_REBINDINGS, + style, + "non-mutable function argument rebound as mutable" +} + +declare_lint_pass!(FnArgMutRebindings => [FN_ARG_MUT_REBINDINGS]); + +impl LateLintPass<'_> for FnArgMutRebindings { + fn check_local(&mut self, cx: &LateContext<'_>, st: &'_ LetStmt<'_>) { + if !st.span.in_external_macro(cx.tcx.sess.source_map()) + + // check let statement binds as mutable + && let PatKind::Binding(BindingMode::MUT, _, ident, None) = st.pat.kind + && let Some(init) = st.init + + // check let statement binds to variable with same name + && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind + && path.segments.len() == 1 + && path.segments[0].ident == ident + && let Res::Local(id) = path.res + + // check let statement scope is whole function body + && let Some((_, owner)) = cx.tcx.hir_parent_owner_iter(st.hir_id).next() + && let Some(body_id) = fn_body_id(&owner) + && let &Body { params, value } = cx.tcx.hir_body(body_id) + && let ExprKind::Block(fn_block, _) = value.kind + && let Some(st_block) = get_enclosing_block(cx, st.hir_id) + && fn_block.hir_id == st_block.hir_id + + // check param declares as immutable + && let Some(&Param { + span: pat_span, + pat: + Pat { + kind: PatKind::Binding(BindingMode::NONE, ..), + .. + }, + .. + }) = params.iter().find(|p| p.pat.hir_id == id) + { + span_lint_and_then( + cx, + FN_ARG_MUT_REBINDINGS, + pat_span, + format!( + "argument `{}` is declared as not mutable, and later rebound as mutable", + ident.name + ), + |diag| { + diag.span_suggestion( + pat_span, + "consider just declaring as mutable", + format!("mut {}", snippet(cx, pat_span, "_")), + Applicability::MaybeIncorrect, + ); + diag.span_help(st.span, "and remove this"); + }, + ); + } + } +} + +fn fn_body_id(node: &OwnerNode<'_>) -> Option { + match node { + OwnerNode::Item(Item { + kind: ItemKind::Fn { body: body_id, .. }, + .. + }) + | OwnerNode::ImplItem(ImplItem { + kind: ImplItemKind::Fn(_, body_id), + // avoid false-positive: trait-fn can come from external crate + impl_kind: ImplItemImplKind::Inherent { .. }, + .. + }) + | OwnerNode::TraitItem(TraitItem { + kind: TraitItemKind::Fn(_, TraitFn::Provided(body_id)), + .. + }) => Some(*body_id), + _ => None, + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ee5cca0397..e2b7282f90c7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -135,6 +135,7 @@ mod fallible_impl_from; mod field_scoped_visibility_modifiers; mod float_literal; mod floating_point_arithmetic; +mod fn_arg_mut_rebindings; mod format; mod format_args; mod format_impl; @@ -867,6 +868,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))), Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))), Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)), + Box::new(|_| Box::new(fn_arg_mut_rebindings::FnArgMutRebindings)), // add late passes here, used by `cargo dev new_lint` ]; store.late_passes.extend(late_lints); diff --git a/tests/ui/fn_arg_mut_rebindings.rs b/tests/ui/fn_arg_mut_rebindings.rs new file mode 100644 index 000000000000..866fe4cbd2f9 --- /dev/null +++ b/tests/ui/fn_arg_mut_rebindings.rs @@ -0,0 +1,74 @@ +//@aux-build:proc_macros.rs +//@no-rustfix + +#![warn(clippy::fn_arg_mut_rebindings)] + +extern crate proc_macros; +use proc_macros::external; + +external! { + fn external_macros_rebinding(x: bool, y: bool) { + let mut x = x; + } +} + +fn fn_body_rebinding(x: bool) { + //~^ fn_arg_mut_rebindings + let mut x = x; +} + +fn branch_rebinding(x: bool, m: u32) { + if x { + let mut m = m; + } +} + +fn arm_rebinding(x: Option, m: u32) { + match x { + Some(_) => { + let mut m = m; + }, + None => { + let mut m = 1; + }, + } +} + +fn inner_block_rebinding(x: bool) { + { + let mut x = x; + } +} + +fn shadowed_rebinding(x: bool) { + let x = 0; + let mut x = x; +} + +trait MyTrait { + fn provided_fn_rebinding(&self, x: bool) { + //~^ fn_arg_mut_rebindings + let mut x = x; + } + + fn inherent_fn_rebinding(&self, x: bool); +} + +struct MyStruct; + +impl MyStruct { + fn impl_fn_rebinding(&self, x: bool) { + //~^ fn_arg_mut_rebindings + let mut x = x; + } +} + +impl MyTrait for MyStruct { + fn inherent_fn_rebinding(&self, x: bool) { + let mut x = x; + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/fn_arg_mut_rebindings.stderr b/tests/ui/fn_arg_mut_rebindings.stderr new file mode 100644 index 000000000000..3146dff66fd7 --- /dev/null +++ b/tests/ui/fn_arg_mut_rebindings.stderr @@ -0,0 +1,40 @@ +error: argument `x` is declared as not mutable, and later rebound as mutable + --> tests/ui/fn_arg_mut_rebindings.rs:15:22 + | +LL | fn fn_body_rebinding(x: bool) { + | ^^^^^^^ help: consider just declaring as mutable: `mut x: bool` + | +help: and remove this + --> tests/ui/fn_arg_mut_rebindings.rs:17:5 + | +LL | let mut x = x; + | ^^^^^^^^^^^^^^ + = note: `-D clippy::fn-arg-mut-rebindings` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_arg_mut_rebindings)]` + +error: argument `x` is declared as not mutable, and later rebound as mutable + --> tests/ui/fn_arg_mut_rebindings.rs:49:37 + | +LL | fn provided_fn_rebinding(&self, x: bool) { + | ^^^^^^^ help: consider just declaring as mutable: `mut x: bool` + | +help: and remove this + --> tests/ui/fn_arg_mut_rebindings.rs:51:9 + | +LL | let mut x = x; + | ^^^^^^^^^^^^^^ + +error: argument `x` is declared as not mutable, and later rebound as mutable + --> tests/ui/fn_arg_mut_rebindings.rs:60:33 + | +LL | fn impl_fn_rebinding(&self, x: bool) { + | ^^^^^^^ help: consider just declaring as mutable: `mut x: bool` + | +help: and remove this + --> tests/ui/fn_arg_mut_rebindings.rs:62:9 + | +LL | let mut x = x; + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 32a50375806a..5b0e7a12c9c9 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::needless_pass_by_ref_mut)] +#![allow(dead_code, clippy::needless_pass_by_ref_mut, clippy::fn_arg_mut_rebindings)] #![allow(clippy::redundant_pattern_matching)] #![warn(clippy::unused_io_amount)]