diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7507064032..d3b2c0a7bf6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5046,6 +5046,7 @@ Released 2018-09-13 [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`assigning_clones`]: https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs new file mode 100644 index 000000000000..b1c552c7a8dd --- /dev/null +++ b/clippy_lints/src/assigning_clones.rs @@ -0,0 +1,322 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::HirNode; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_trait_method, path_to_local}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Expr, ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Instance, Mutability}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::DefId; +use rustc_span::symbol::sym; +use rustc_span::ExpnKind; + +declare_clippy_lint! { + /// ### What it does + /// Checks for code like `foo = bar.clone();` + /// + /// ### Why is this bad? + /// Custom `Clone::clone_from()` or `ToOwned::clone_into` implementations allow the objects + /// to share resources and therefore avoid allocations. + /// + /// ### Example + /// ```rust + /// struct Thing; + /// + /// impl Clone for Thing { + /// fn clone(&self) -> Self { todo!() } + /// fn clone_from(&mut self, other: &Self) { todo!() } + /// } + /// + /// pub fn assign_to_ref(a: &mut Thing, b: Thing) { + /// *a = b.clone(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Thing; + /// impl Clone for Thing { + /// fn clone(&self) -> Self { todo!() } + /// fn clone_from(&mut self, other: &Self) { todo!() } + /// } + /// + /// pub fn assign_to_ref(a: &mut Thing, b: Thing) { + /// a.clone_from(&b); + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub ASSIGNING_CLONES, + perf, + "assigning the result of cloning may be inefficient" +} +declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + +impl<'tcx> LateLintPass<'tcx> for AssigningClones { + fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { + // Do not fire the lint in macros + let expn_data = assign_expr.span().ctxt().outer_expn_data(); + match expn_data.kind { + ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, + ExpnKind::Root => {}, + } + + let ExprKind::Assign(lhs, rhs, _span) = assign_expr.kind else { + return; + }; + + let Some(call) = extract_call(cx, rhs) else { + return; + }; + + if is_ok_to_suggest(cx, lhs, &call) { + suggest(cx, assign_expr, lhs, &call); + } + } +} + +// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`. +fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + let fn_def_id = clippy_utils::fn_def_id(cx, expr)?; + + // Fast paths to only check method calls without arguments or function calls with a single argument + let (target, kind, resolved_method) = match expr.kind { + ExprKind::MethodCall(path, receiver, [], _span) => { + let args = cx.typeck_results().node_args(expr.hir_id); + + // If we could not resolve the method, don't apply the lint + let Ok(Some(resolved_method)) = Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args) else { + return None; + }; + if is_trait_method(cx, expr, sym::Clone) && path.ident.name == sym::clone { + (TargetTrait::Clone, CallKind::MethodCall { receiver }, resolved_method) + } else if is_trait_method(cx, expr, sym::ToOwned) && path.ident.name.as_str() == "to_owned" { + (TargetTrait::ToOwned, CallKind::MethodCall { receiver }, resolved_method) + } else { + return None; + } + }, + ExprKind::Call(function, [arg]) => { + let kind = cx.typeck_results().node_type(function.hir_id).kind(); + + // If we could not resolve the method, don't apply the lint + let Ok(Some(resolved_method)) = (match kind { + ty::FnDef(_, args) => Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args), + _ => Ok(None), + }) else { + return None; + }; + if cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) { + ( + TargetTrait::ToOwned, + CallKind::FunctionCall { self_arg: arg }, + resolved_method, + ) + } else if let Some(trait_did) = cx.tcx.trait_of_item(fn_def_id) + && cx.tcx.is_diagnostic_item(sym::Clone, trait_did) + { + ( + TargetTrait::Clone, + CallKind::FunctionCall { self_arg: arg }, + resolved_method, + ) + } else { + return None; + } + }, + _ => return None, + }; + + Some(CallCandidate { + target, + kind, + method_def_id: resolved_method.def_id(), + }) +} + +// Return true if we find that the called method has a custom implementation and isn't derived or +// provided by default by the corresponding trait. +fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) -> bool { + // If the left-hand side is a local variable, it might be uninitialized at this point. + // In that case we do not want to suggest the lint. + if let Some(local) = path_to_local(lhs) { + // TODO: This check currently bails if the local variable has no initializer. + // That is overly conservative - the lint should fire even if there was no initializer, + // but the variable has been initialized before `lhs` was evaluated. + if let Some(Node::Local(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) + && local.init.is_none() + { + return false; + } + } + + let Some(impl_block) = cx.tcx.impl_of_method(call.method_def_id) else { + return false; + }; + + // If the method implementation comes from #[derive(Clone)], then don't suggest the lint. + // Automatically generated Clone impls do not currently override `clone_from`. + // See e.g. https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 for more details. + if cx.tcx.is_builtin_derived(impl_block) { + return false; + } + + // Find the function for which we want to check that it is implemented. + let provided_fn = match call.target { + TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| { + cx.tcx + .provided_trait_methods(clone) + .find(|item| item.name == sym::clone_from) + }), + TargetTrait::ToOwned => cx.tcx.get_diagnostic_item(sym::ToOwned).and_then(|to_owned| { + cx.tcx + .provided_trait_methods(to_owned) + .find(|item| item.name.as_str() == "clone_into") + }), + }; + let Some(provided_fn) = provided_fn else { + return false; + }; + + // Now take a look if the impl block defines an implementation for the method that we're interested + // in. If not, then we're using a default implementation, which is not interesting, so we will + // not suggest the lint. + let implemented_fns = cx.tcx.impl_item_implementor_ids(impl_block); + implemented_fns.contains_key(&provided_fn.def_id) +} + +fn suggest<'tcx>( + cx: &LateContext<'tcx>, + assign_expr: &hir::Expr<'tcx>, + lhs: &hir::Expr<'tcx>, + call: &CallCandidate<'tcx>, +) { + span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { + let mut applicability = Applicability::MachineApplicable; + + diag.span_suggestion( + assign_expr.span, + call.suggestion_msg(), + call.suggested_replacement(cx, lhs, &mut applicability), + applicability, + ); + }); +} + +#[derive(Copy, Clone, Debug)] +enum CallKind<'tcx> { + MethodCall { receiver: &'tcx Expr<'tcx> }, + FunctionCall { self_arg: &'tcx Expr<'tcx> }, +} + +#[derive(Copy, Clone, Debug)] +enum TargetTrait { + Clone, + ToOwned, +} + +#[derive(Debug)] +struct CallCandidate<'tcx> { + target: TargetTrait, + kind: CallKind<'tcx>, + // DefId of the called method from an impl block that implements the target trait + method_def_id: DefId, +} + +impl<'tcx> CallCandidate<'tcx> { + #[inline] + fn message(&self) -> &'static str { + match self.target { + TargetTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient", + TargetTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient", + } + } + + #[inline] + fn suggestion_msg(&self) -> &'static str { + match self.target { + TargetTrait::Clone => "use `clone_from()`", + TargetTrait::ToOwned => "use `clone_into()`", + } + } + + fn suggested_replacement( + &self, + cx: &LateContext<'tcx>, + lhs: &hir::Expr<'tcx>, + applicability: &mut Applicability, + ) -> String { + match self.target { + TargetTrait::Clone => { + match self.kind { + CallKind::MethodCall { receiver } => { + let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) + } else { + // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", applicability) + } + .maybe_par(); + + // Determine whether we need to reference the argument to clone_from(). + let clone_receiver_type = cx.typeck_results().expr_ty(receiver); + let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); + let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + if clone_receiver_type != clone_receiver_adj_type { + // The receiver may have been a value type, so we need to add an `&` to + // be sure the argument to clone_from will be a reference. + arg_sugg = arg_sugg.addr(); + }; + + format!("{receiver_sugg}.clone_from({arg_sugg})") + }, + CallKind::FunctionCall { self_arg, .. } => { + let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) + } else { + // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() + }; + // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. + let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + + format!("Clone::clone_from({self_sugg}, {rhs_sugg})") + }, + } + }, + TargetTrait::ToOwned => { + let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` + // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` + let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", applicability).maybe_par(); + let inner_type = cx.typeck_results().expr_ty(ref_expr); + // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it + // deref to a mutable reference. + if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) { + sugg + } else { + sugg.mut_addr() + } + } else { + // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` + // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` + Sugg::hir_with_applicability(cx, lhs, "_", applicability) + .maybe_par() + .mut_addr() + }; + + match self.kind { + CallKind::MethodCall { receiver } => { + let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + format!("{receiver_sugg}.clone_into({rhs_sugg})") + }, + CallKind::FunctionCall { self_arg, .. } => { + let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") + }, + } + }, + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a453386154d9..2b324f5f96e8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -47,6 +47,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO, crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO, crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO, + crate::assigning_clones::ASSIGNING_CLONES_INFO, crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO, crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 76e759683145..b930175c4d89 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -80,6 +80,7 @@ mod as_conversions; mod asm_syntax; mod assertions_on_constants; mod assertions_on_result_states; +mod assigning_clones; mod async_yields_async; mod attrs; mod await_holding_invalid; @@ -1118,6 +1119,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); + store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b355e66b7b12..0dc389993dde 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -994,7 +994,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // no adjustment needed here, as field projections are handled by the compiler ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() { ty::Adt(..) | ty::Tuple(_) => { - replacement_str = ident_str_with_proj.clone(); + replacement_str.clone_from(&ident_str_with_proj); projections_handled = true; }, _ => (), diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 0d35a22cd9a4..141c11ddb47f 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -54,7 +54,7 @@ fn explore_directory(dir: &Path) -> Vec { let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string(); if let Some(ext) = path.extension() { match ext.to_str().unwrap() { - "rs" | "toml" => current_file = file_prefix.clone(), + "rs" | "toml" => current_file.clone_from(&file_prefix), "stderr" | "stdout" => { if file_prefix != current_file { missing_files.push(path.to_str().unwrap().to_string()); diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed new file mode 100644 index 000000000000..c66e0c1f6028 --- /dev/null +++ b/tests/ui/assigning_clones.fixed @@ -0,0 +1,222 @@ +#![allow(unused)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 +#![allow(clippy::needless_late_init)] +#![allow(clippy::box_collection)] +#![warn(clippy::assigning_clones)] + +use std::borrow::ToOwned; +use std::ops::{Add, Deref, DerefMut}; + +// Clone +pub struct HasCloneFrom; + +impl Clone for HasCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = HasCloneFrom; + } +} + +fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) { + mut_thing.clone_from(&value_thing); +} + +fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing.clone_from(ref_thing); +} + +fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing.clone_from(ref_thing); +} + +fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(&mut mut_thing, ref_thing); +} + +fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + Clone::clone_from(mut_thing, ref_thing); +} + +fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be kept as necessary for a receiver + (mut_thing + &mut HasCloneFrom).clone_from(ref_thing); +} + +fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be removed since they are not needed in a function argument + mut_thing.clone_from(ref_thing + ref_thing); +} + +fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { + let mut a = HasCloneFrom; + for _ in 1..10 { + a.clone_from(&b); + } + a +} + +fn assign_to_late_init_mut_var(b: HasCloneFrom) { + let mut a; + a = HasCloneFrom; + a = b.clone(); +} + +fn assign_to_uninit_var(b: HasCloneFrom) { + let a; + a = b.clone(); +} + +fn assign_to_uninit_mut_var(b: HasCloneFrom) { + let mut a; + a = b.clone(); +} + +#[derive(Clone)] +pub struct HasDeriveClone; + +fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) { + // Should not be linted, since the Clone impl is derived + *a = b.clone(); +} + +pub struct HasCloneImpl; + +impl Clone for HasCloneImpl { + fn clone(&self) -> Self { + Self + } +} + +fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) { + // Should not be linted, since the Clone impl doesn't override clone_from + *a = b.clone(); +} + +struct FakeClone; + +impl FakeClone { + /// This looks just like `Clone::clone` + fn clone(&self) -> Self { + FakeClone + } +} + +fn ignore_fake_clone() { + let mut a = FakeClone; + let b = FakeClone; + // Should not be linted, since the Clone impl doesn't come from std + a = b.clone(); +} + +fn ignore_generic_clone(a: &mut T, b: &T) { + // Should not be linted, since we don't know the actual clone impl + *a = b.clone(); +} + +macro_rules! clone_inside { + ($a:expr, $b: expr) => { + $a = $b.clone(); + }; +} + +fn clone_inside_macro() { + let mut a = String::new(); + let b = String::new(); + clone_inside!(a, b); +} + +// ToOwned +fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { + ref_str.clone_into(mut_string); +} + +fn owned_method_val(mut mut_string: String, ref_str: &str) { + ref_str.clone_into(&mut mut_string); +} + +struct HasDeref { + a: String, +} + +impl Deref for HasDeref { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.a + } +} + +impl DerefMut for HasDeref { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.a + } +} + +fn owned_method_box(mut_box_string: &mut Box, ref_str: &str) { + ref_str.clone_into(&mut (*mut_box_string)); +} + +fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) { + ref_str.clone_into(&mut (*mut_box_string)); +} + +fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) { + ToOwned::clone_into(ref_str, mut_thing); +} + +fn owned_function_val(mut mut_thing: String, ref_str: &str) { + ToOwned::clone_into(ref_str, &mut mut_thing); +} + +struct FakeToOwned; +impl FakeToOwned { + /// This looks just like `ToOwned::to_owned` + fn to_owned(&self) -> Self { + FakeToOwned + } +} + +fn fake_to_owned() { + let mut a = FakeToOwned; + let b = FakeToOwned; + // Should not be linted, since the ToOwned impl doesn't come from std + a = b.to_owned(); +} + +fn main() {} + +/// Trait implementation to allow producing a `Thing` with a low-precedence expression. +impl Add for HasCloneFrom { + type Output = Self; + fn add(self, _: HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&Thing` with a low-precedence expression. +impl<'a> Add for &'a HasCloneFrom { + type Output = Self; + fn add(self, _: &'a HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression. +impl<'a> Add for &'a mut HasCloneFrom { + type Output = Self; + fn add(self, _: &'a mut HasCloneFrom) -> Self { + self + } +} diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs new file mode 100644 index 000000000000..b9f994d3e034 --- /dev/null +++ b/tests/ui/assigning_clones.rs @@ -0,0 +1,222 @@ +#![allow(unused)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 +#![allow(clippy::needless_late_init)] +#![allow(clippy::box_collection)] +#![warn(clippy::assigning_clones)] + +use std::borrow::ToOwned; +use std::ops::{Add, Deref, DerefMut}; + +// Clone +pub struct HasCloneFrom; + +impl Clone for HasCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = HasCloneFrom; + } +} + +fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) { + *mut_thing = value_thing.clone(); +} + +fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = ref_thing.clone(); +} + +fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing = ref_thing.clone(); +} + +fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) { + mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = Clone::clone(ref_thing); +} + +fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = HasCloneFrom::clone(ref_thing); +} + +fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + *mut_thing = ::clone(ref_thing); +} + +fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be kept as necessary for a receiver + *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); +} + +fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { + // These parens should be removed since they are not needed in a function argument + *mut_thing = (ref_thing + ref_thing).clone(); +} + +fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { + let mut a = HasCloneFrom; + for _ in 1..10 { + a = b.clone(); + } + a +} + +fn assign_to_late_init_mut_var(b: HasCloneFrom) { + let mut a; + a = HasCloneFrom; + a = b.clone(); +} + +fn assign_to_uninit_var(b: HasCloneFrom) { + let a; + a = b.clone(); +} + +fn assign_to_uninit_mut_var(b: HasCloneFrom) { + let mut a; + a = b.clone(); +} + +#[derive(Clone)] +pub struct HasDeriveClone; + +fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) { + // Should not be linted, since the Clone impl is derived + *a = b.clone(); +} + +pub struct HasCloneImpl; + +impl Clone for HasCloneImpl { + fn clone(&self) -> Self { + Self + } +} + +fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) { + // Should not be linted, since the Clone impl doesn't override clone_from + *a = b.clone(); +} + +struct FakeClone; + +impl FakeClone { + /// This looks just like `Clone::clone` + fn clone(&self) -> Self { + FakeClone + } +} + +fn ignore_fake_clone() { + let mut a = FakeClone; + let b = FakeClone; + // Should not be linted, since the Clone impl doesn't come from std + a = b.clone(); +} + +fn ignore_generic_clone(a: &mut T, b: &T) { + // Should not be linted, since we don't know the actual clone impl + *a = b.clone(); +} + +macro_rules! clone_inside { + ($a:expr, $b: expr) => { + $a = $b.clone(); + }; +} + +fn clone_inside_macro() { + let mut a = String::new(); + let b = String::new(); + clone_inside!(a, b); +} + +// ToOwned +fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { + *mut_string = ref_str.to_owned(); +} + +fn owned_method_val(mut mut_string: String, ref_str: &str) { + mut_string = ref_str.to_owned(); +} + +struct HasDeref { + a: String, +} + +impl Deref for HasDeref { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.a + } +} + +impl DerefMut for HasDeref { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.a + } +} + +fn owned_method_box(mut_box_string: &mut Box, ref_str: &str) { + **mut_box_string = ref_str.to_owned(); +} + +fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) { + **mut_box_string = ref_str.to_owned(); +} + +fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) { + *mut_thing = ToOwned::to_owned(ref_str); +} + +fn owned_function_val(mut mut_thing: String, ref_str: &str) { + mut_thing = ToOwned::to_owned(ref_str); +} + +struct FakeToOwned; +impl FakeToOwned { + /// This looks just like `ToOwned::to_owned` + fn to_owned(&self) -> Self { + FakeToOwned + } +} + +fn fake_to_owned() { + let mut a = FakeToOwned; + let b = FakeToOwned; + // Should not be linted, since the ToOwned impl doesn't come from std + a = b.to_owned(); +} + +fn main() {} + +/// Trait implementation to allow producing a `Thing` with a low-precedence expression. +impl Add for HasCloneFrom { + type Output = Self; + fn add(self, _: HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&Thing` with a low-precedence expression. +impl<'a> Add for &'a HasCloneFrom { + type Output = Self; + fn add(self, _: &'a HasCloneFrom) -> Self { + self + } +} +/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression. +impl<'a> Add for &'a mut HasCloneFrom { + type Output = Self; + fn add(self, _: &'a mut HasCloneFrom) -> Self { + self + } +} diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr new file mode 100644 index 000000000000..b76323f36063 --- /dev/null +++ b/tests/ui/assigning_clones.stderr @@ -0,0 +1,107 @@ +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:24:5 + | +LL | *mut_thing = value_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)` + | + = note: `-D clippy::assigning-clones` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:28:5 + | +LL | *mut_thing = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:32:5 + | +LL | mut_thing = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:36:5 + | +LL | *mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:40:5 + | +LL | mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:44:5 + | +LL | *mut_thing = Clone::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:48:5 + | +LL | *mut_thing = HasCloneFrom::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:52:5 + | +LL | *mut_thing = ::clone(ref_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:57:5 + | +LL | *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:62:5 + | +LL | *mut_thing = (ref_thing + ref_thing).clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:68:9 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:145:5 + | +LL | *mut_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:149:5 + | +LL | mut_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:170:5 + | +LL | **mut_box_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:174:5 + | +LL | **mut_box_string = ref_str.to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:178:5 + | +LL | *mut_thing = ToOwned::to_owned(ref_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:182:5 + | +LL | mut_thing = ToOwned::to_owned(ref_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` + +error: aborting due to 17 previous errors + diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 2045b1eebcd7..620cffb4f04d 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -2,6 +2,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::field_reassign_with_default)] +#![allow(clippy::assigning_clones)] #[macro_use] extern crate proc_macro_derive; diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index bc0328349208..ae909475c6f2 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,11 +1,11 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:56:5 + --> tests/ui/field_reassign_with_default.rs:57:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:55:5 + --> tests/ui/field_reassign_with_default.rs:56:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,121 +13,121 @@ LL | let mut a: A = Default::default(); = help: to override `-D warnings` add `#[allow(clippy::field_reassign_with_default)]` error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:96:5 + --> tests/ui/field_reassign_with_default.rs:97:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:95:5 + --> tests/ui/field_reassign_with_default.rs:96:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:101:5 + --> tests/ui/field_reassign_with_default.rs:102:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:100:5 + --> tests/ui/field_reassign_with_default.rs:101:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:107:5 + --> tests/ui/field_reassign_with_default.rs:108:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:106:5 + --> tests/ui/field_reassign_with_default.rs:107:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:117:5 + --> tests/ui/field_reassign_with_default.rs:118:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:116:5 + --> tests/ui/field_reassign_with_default.rs:117:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:121:5 + --> tests/ui/field_reassign_with_default.rs:122:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:120:5 + --> tests/ui/field_reassign_with_default.rs:121:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:143:5 + --> tests/ui/field_reassign_with_default.rs:144:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:142:5 + --> tests/ui/field_reassign_with_default.rs:143:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:161:5 + --> tests/ui/field_reassign_with_default.rs:162:5 | LL | a.i = true; | ^^^^^^^^^^^ | note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:160:5 + --> tests/ui/field_reassign_with_default.rs:161:5 | LL | let mut a: Wrapper = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:164:5 + --> tests/ui/field_reassign_with_default.rs:165:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:163:5 + --> tests/ui/field_reassign_with_default.rs:164:5 | LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:235:13 + --> tests/ui/field_reassign_with_default.rs:236:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:234:13 + --> tests/ui/field_reassign_with_default.rs:235:13 | LL | let mut f = ImplDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> tests/ui/field_reassign_with_default.rs:251:13 + --> tests/ui/field_reassign_with_default.rs:252:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> tests/ui/field_reassign_with_default.rs:250:13 + --> tests/ui/field_reassign_with_default.rs:251:13 | LL | let mut f = NoDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs index 5210b86a0f4e..c917fa7f2d0b 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -1,5 +1,6 @@ #![warn(clippy::manual_memcpy)] -#![allow(clippy::useless_vec, clippy::needless_range_loop)] +#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)] + //@no-rustfix const LOOP_OFFSET: usize = 5000; diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr index 6dcc0bf4c551..803053b2db2e 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:8:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:9:5 | LL | / for i in 0..src.len() { LL | | @@ -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:15:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:16:5 | LL | / for i in 0..src.len() { LL | | @@ -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:21:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:22:5 | LL | / for i in 0..src.len() { LL | | @@ -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:27:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:28:5 | LL | / for i in 11..src.len() { LL | | @@ -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:33:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:34:5 | LL | / for i in 0..dst.len() { LL | | @@ -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:47:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:48:5 | LL | / for i in 10..256 { LL | | @@ -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:60:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:61:5 | LL | / for i in 10..LOOP_OFFSET { LL | | @@ -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:74:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:75:5 | LL | / for i in 0..src_vec.len() { LL | | @@ -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:104:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:105:5 | LL | / for i in from..from + src.len() { LL | | @@ -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:109:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 | LL | / for i in from..from + 3 { LL | | @@ -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:115:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:116:5 | LL | / for i in 0..5 { LL | | @@ -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:121:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:122:5 | LL | / for i in 0..0 { LL | | @@ -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:145:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:146:5 | LL | / for i in 0..4 { LL | | @@ -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:151:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:152:5 | LL | / for i in 0..5 { LL | | @@ -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:157:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:158:5 | LL | / for i in 0..5 { LL | | @@ -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:204:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:205:5 | LL | / for i in 0..5 { LL | | @@ -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:210:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:211:5 | LL | / for i in 0..5 { LL | | @@ -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:218:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:219:5 | LL | / for i in 0..src.len() { LL | | diff --git a/tests/ui/non_canonical_clone_impl.fixed b/tests/ui/non_canonical_clone_impl.fixed index 165702b30411..7d1be412e54f 100644 --- a/tests/ui/non_canonical_clone_impl.fixed +++ b/tests/ui/non_canonical_clone_impl.fixed @@ -1,4 +1,5 @@ #![allow(clippy::clone_on_copy, unused)] +#![allow(clippy::assigning_clones)] #![no_main] // lint diff --git a/tests/ui/non_canonical_clone_impl.rs b/tests/ui/non_canonical_clone_impl.rs index 3b07dd5ce62f..beae05efb2fb 100644 --- a/tests/ui/non_canonical_clone_impl.rs +++ b/tests/ui/non_canonical_clone_impl.rs @@ -1,4 +1,5 @@ #![allow(clippy::clone_on_copy, unused)] +#![allow(clippy::assigning_clones)] #![no_main] // lint diff --git a/tests/ui/non_canonical_clone_impl.stderr b/tests/ui/non_canonical_clone_impl.stderr index 8eff322fa2d7..6bfc99d988bb 100644 --- a/tests/ui/non_canonical_clone_impl.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:9:29 + --> tests/ui/non_canonical_clone_impl.rs:10:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:13:5 + --> tests/ui/non_canonical_clone_impl.rs:14:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -20,7 +20,7 @@ LL | | } | |_____^ help: remove it error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:80:29 + --> tests/ui/non_canonical_clone_impl.rs:81:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -29,7 +29,7 @@ LL | | } | |_____^ help: change this to: `{ *self }` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:84:5 + --> tests/ui/non_canonical_clone_impl.rs:85:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone();