Skip to content

Commit

Permalink
Only apply dereference-removing special case for actual references
Browse files Browse the repository at this point in the history
  • Loading branch information
Kobzol committed Jul 25, 2024
1 parent 062b66d commit e9852cc
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 29 deletions.
29 changes: 25 additions & 4 deletions clippy_lints/src/assigning_clones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,22 @@ fn build_sugg<'tcx>(
match call_kind {
CallKind::Method => {
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, "_", app)
// If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make
// the generated code a bit simpler. In other cases, we don't do this special case, to avoid
// having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437).

let ty = cx.typeck_results().expr_ty(ref_expr);
if ty.is_ref() {
// Apply special case, remove `*`
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
} else {
// Keep the original lhs
// `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app)
}
} else {
// Keep the original lhs
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app)
}
Expand All @@ -249,8 +262,16 @@ fn build_sugg<'tcx>(
},
CallKind::Ufcs => {
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, "_", app)
// See special case of removing `*` in method handling above
let ty = cx.typeck_results().expr_ty(ref_expr);
if ty.is_ref() {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
} else {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)`
// mut_addr_deref is used to avoid unnecessary parentheses around `*lhs`
Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref()
}
} else {
// `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr()
Expand Down
68 changes: 68 additions & 0 deletions tests/ui/assigning_clones.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
#![allow(clippy::needless_late_init)]
#![allow(clippy::box_collection)]
#![allow(clippy::boxed_local)]
#![warn(clippy::assigning_clones)]

use std::borrow::ToOwned;
Expand Down Expand Up @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
}
}

// Deref handling
fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
(*a).clone_from(&b);
}

fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
(*a).clone_from(&b);
}

fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
(*a).clone_from(&b);
}

fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
a.clone_from(&b);
}

fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
Clone::clone_from(&mut *a, &b);
}

fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
Clone::clone_from(&mut *a, &b);
}

// ToOwned
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
ref_str.clone_into(mut_string);
Expand Down Expand Up @@ -328,3 +354,45 @@ mod borrowck_conflicts {
path = path.components().as_path().to_owned();
}
}

struct DerefWrapper<T>(T);

impl<T> Deref for DerefWrapper<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> DerefMut for DerefWrapper<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

struct DerefWrapperWithClone<T>(T);

impl<T> Deref for DerefWrapperWithClone<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> DerefMut for DerefWrapperWithClone<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T: Clone> Clone for DerefWrapperWithClone<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}

fn clone_from(&mut self, source: &Self) {
*self = Self(source.0.clone());
}
}
68 changes: 68 additions & 0 deletions tests/ui/assigning_clones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
#![allow(clippy::needless_late_init)]
#![allow(clippy::box_collection)]
#![allow(clippy::boxed_local)]
#![warn(clippy::assigning_clones)]

use std::borrow::ToOwned;
Expand Down Expand Up @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
}
}

// Deref handling
fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
*a = b.clone();
}

fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
*a = b.clone();
}

fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
*a = b.clone();
}

fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
*a = b.clone();
}

fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
*a = Clone::clone(&b);
}

fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
*a = Clone::clone(&b);
}

// ToOwned
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
*mut_string = ref_str.to_owned();
Expand Down Expand Up @@ -328,3 +354,45 @@ mod borrowck_conflicts {
path = path.components().as_path().to_owned();
}
}

struct DerefWrapper<T>(T);

impl<T> Deref for DerefWrapper<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> DerefMut for DerefWrapper<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

struct DerefWrapperWithClone<T>(T);

impl<T> Deref for DerefWrapperWithClone<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> DerefMut for DerefWrapperWithClone<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T: Clone> Clone for DerefWrapperWithClone<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}

fn clone_from(&mut self, source: &Self) {
*self = Self(source.0.clone());
}
}
Loading

0 comments on commit e9852cc

Please sign in to comment.