Skip to content

Commit 533c377

Browse files
committed
Auto merge of #12473 - Kobzol:assigning-clones-deref, r=Alexendoo
Fix handling of `Deref` in `assigning_clones` The `assigning_clones` lint had a special case for producing a bit nicer code for mutable references: ```rust fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { *mut_thing = Clone::clone(ref_thing); } //v fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) { Clone::clone_from(mut_thing, ref_thing); } ``` However, this could [break](#12437) when combined with `Deref`. This PR removes the special case, so that the generated code should work more generally. Later we can improve the detection of `Deref` and put the special case back in a way that does not break code. Fixes: #12437 r? `@blyxyas` changelog: [`assigning_clones`]: change applicability to `Unspecified` and fix a problem with `Deref`.
2 parents 062b66d + e9852cc commit 533c377

File tree

4 files changed

+222
-29
lines changed

4 files changed

+222
-29
lines changed

clippy_lints/src/assigning_clones.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,22 @@ fn build_sugg<'tcx>(
227227
match call_kind {
228228
CallKind::Method => {
229229
let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
230-
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
231-
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
230+
// If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make
231+
// the generated code a bit simpler. In other cases, we don't do this special case, to avoid
232+
// having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437).
233+
234+
let ty = cx.typeck_results().expr_ty(ref_expr);
235+
if ty.is_ref() {
236+
// Apply special case, remove `*`
237+
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
238+
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
239+
} else {
240+
// Keep the original lhs
241+
// `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)`
242+
Sugg::hir_with_applicability(cx, lhs, "_", app)
243+
}
232244
} else {
245+
// Keep the original lhs
233246
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
234247
Sugg::hir_with_applicability(cx, lhs, "_", app)
235248
}
@@ -249,8 +262,16 @@ fn build_sugg<'tcx>(
249262
},
250263
CallKind::Ufcs => {
251264
let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
252-
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
253-
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
265+
// See special case of removing `*` in method handling above
266+
let ty = cx.typeck_results().expr_ty(ref_expr);
267+
if ty.is_ref() {
268+
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
269+
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
270+
} else {
271+
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)`
272+
// mut_addr_deref is used to avoid unnecessary parentheses around `*lhs`
273+
Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref()
274+
}
254275
} else {
255276
// `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
256277
Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr()

tests/ui/assigning_clones.fixed

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
44
#![allow(clippy::needless_late_init)]
55
#![allow(clippy::box_collection)]
6+
#![allow(clippy::boxed_local)]
67
#![warn(clippy::assigning_clones)]
78

89
use std::borrow::ToOwned;
@@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
182183
}
183184
}
184185

186+
// Deref handling
187+
fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
188+
(*a).clone_from(&b);
189+
}
190+
191+
fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
192+
(*a).clone_from(&b);
193+
}
194+
195+
fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
196+
(*a).clone_from(&b);
197+
}
198+
199+
fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
200+
a.clone_from(&b);
201+
}
202+
203+
fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
204+
Clone::clone_from(&mut *a, &b);
205+
}
206+
207+
fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
208+
Clone::clone_from(&mut *a, &b);
209+
}
210+
185211
// ToOwned
186212
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
187213
ref_str.clone_into(mut_string);
@@ -328,3 +354,45 @@ mod borrowck_conflicts {
328354
path = path.components().as_path().to_owned();
329355
}
330356
}
357+
358+
struct DerefWrapper<T>(T);
359+
360+
impl<T> Deref for DerefWrapper<T> {
361+
type Target = T;
362+
363+
fn deref(&self) -> &Self::Target {
364+
&self.0
365+
}
366+
}
367+
368+
impl<T> DerefMut for DerefWrapper<T> {
369+
fn deref_mut(&mut self) -> &mut Self::Target {
370+
&mut self.0
371+
}
372+
}
373+
374+
struct DerefWrapperWithClone<T>(T);
375+
376+
impl<T> Deref for DerefWrapperWithClone<T> {
377+
type Target = T;
378+
379+
fn deref(&self) -> &Self::Target {
380+
&self.0
381+
}
382+
}
383+
384+
impl<T> DerefMut for DerefWrapperWithClone<T> {
385+
fn deref_mut(&mut self) -> &mut Self::Target {
386+
&mut self.0
387+
}
388+
}
389+
390+
impl<T: Clone> Clone for DerefWrapperWithClone<T> {
391+
fn clone(&self) -> Self {
392+
Self(self.0.clone())
393+
}
394+
395+
fn clone_from(&mut self, source: &Self) {
396+
*self = Self(source.0.clone());
397+
}
398+
}

tests/ui/assigning_clones.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
44
#![allow(clippy::needless_late_init)]
55
#![allow(clippy::box_collection)]
6+
#![allow(clippy::boxed_local)]
67
#![warn(clippy::assigning_clones)]
78

89
use std::borrow::ToOwned;
@@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
182183
}
183184
}
184185

186+
// Deref handling
187+
fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
188+
*a = b.clone();
189+
}
190+
191+
fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
192+
*a = b.clone();
193+
}
194+
195+
fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
196+
*a = b.clone();
197+
}
198+
199+
fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
200+
*a = b.clone();
201+
}
202+
203+
fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
204+
*a = Clone::clone(&b);
205+
}
206+
207+
fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
208+
*a = Clone::clone(&b);
209+
}
210+
185211
// ToOwned
186212
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
187213
*mut_string = ref_str.to_owned();
@@ -328,3 +354,45 @@ mod borrowck_conflicts {
328354
path = path.components().as_path().to_owned();
329355
}
330356
}
357+
358+
struct DerefWrapper<T>(T);
359+
360+
impl<T> Deref for DerefWrapper<T> {
361+
type Target = T;
362+
363+
fn deref(&self) -> &Self::Target {
364+
&self.0
365+
}
366+
}
367+
368+
impl<T> DerefMut for DerefWrapper<T> {
369+
fn deref_mut(&mut self) -> &mut Self::Target {
370+
&mut self.0
371+
}
372+
}
373+
374+
struct DerefWrapperWithClone<T>(T);
375+
376+
impl<T> Deref for DerefWrapperWithClone<T> {
377+
type Target = T;
378+
379+
fn deref(&self) -> &Self::Target {
380+
&self.0
381+
}
382+
}
383+
384+
impl<T> DerefMut for DerefWrapperWithClone<T> {
385+
fn deref_mut(&mut self) -> &mut Self::Target {
386+
&mut self.0
387+
}
388+
}
389+
390+
impl<T: Clone> Clone for DerefWrapperWithClone<T> {
391+
fn clone(&self) -> Self {
392+
Self(self.0.clone())
393+
}
394+
395+
fn clone_from(&mut self, source: &Self) {
396+
*self = Self(source.0.clone());
397+
}
398+
}

0 commit comments

Comments
 (0)