Skip to content

Commit 5b2fb8c

Browse files
committed
Improve expl_impl_clone_on_copy
Check to see if the generic constraints are the same as if using derive
1 parent 6f2a6fe commit 5b2fb8c

File tree

3 files changed

+91
-49
lines changed

3 files changed

+91
-49
lines changed

clippy_lints/src/derive.rs

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
22
use clippy_utils::paths;
3-
use clippy_utils::ty::is_copy;
3+
use clippy_utils::ty::{implements_trait, is_copy};
44
use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
55
use if_chain::if_chain;
66
use rustc_hir::def_id::DefId;
@@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass};
1212
use rustc_middle::hir::map::Map;
1313
use rustc_middle::ty::{self, Ty};
1414
use rustc_session::{declare_lint_pass, declare_tool_lint};
15-
use rustc_span::source_map::Span;
15+
use rustc_span::{def_id::LOCAL_CRATE, source_map::Span};
1616

1717
declare_clippy_lint! {
1818
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
@@ -293,48 +293,53 @@ fn check_ord_partial_ord<'tcx>(
293293

294294
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
295295
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
296-
if cx
297-
.tcx
298-
.lang_items()
299-
.clone_trait()
300-
.map_or(false, |id| Some(id) == trait_ref.trait_def_id())
301-
{
302-
if !is_copy(cx, ty) {
296+
let clone_id = match cx.tcx.lang_items().clone_trait() {
297+
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
298+
_ => return,
299+
};
300+
let copy_id = match cx.tcx.lang_items().copy_trait() {
301+
Some(id) => id,
302+
None => return,
303+
};
304+
let (ty_adt, ty_subs) = match *ty.kind() {
305+
// Unions can't derive clone.
306+
ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
307+
_ => return,
308+
};
309+
// If the current self type doesn't implement Copy (due to generic constraints), search to see if
310+
// there's a Copy impl for any instance of the adt.
311+
if !is_copy(cx, ty) {
312+
if ty_subs.non_erasable_generics().next().is_some() {
313+
let has_copy_impl = cx
314+
.tcx
315+
.all_local_trait_impls(LOCAL_CRATE)
316+
.get(&copy_id)
317+
.map_or(false, |impls| {
318+
impls
319+
.iter()
320+
.any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did))
321+
});
322+
if !has_copy_impl {
323+
return;
324+
}
325+
} else {
303326
return;
304327
}
305-
306-
match *ty.kind() {
307-
ty::Adt(def, _) if def.is_union() => return,
308-
309-
// Some types are not Clone by default but could be cloned “by hand” if necessary
310-
ty::Adt(def, substs) => {
311-
for variant in &def.variants {
312-
for field in &variant.fields {
313-
if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() {
314-
return;
315-
}
316-
}
317-
for subst in substs {
318-
if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() {
319-
if let ty::Param(_) = subst.kind() {
320-
return;
321-
}
322-
}
323-
}
324-
}
325-
},
326-
_ => (),
327-
}
328-
329-
span_lint_and_note(
330-
cx,
331-
EXPL_IMPL_CLONE_ON_COPY,
332-
item.span,
333-
"you are implementing `Clone` explicitly on a `Copy` type",
334-
Some(item.span),
335-
"consider deriving `Clone` or removing `Copy`",
336-
);
337328
}
329+
// Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
330+
// this impl.
331+
if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
332+
return;
333+
}
334+
335+
span_lint_and_note(
336+
cx,
337+
EXPL_IMPL_CLONE_ON_COPY,
338+
item.span,
339+
"you are implementing `Clone` explicitly on a `Copy` type",
340+
Some(item.span),
341+
"consider deriving `Clone` or removing `Copy`",
342+
);
338343
}
339344

340345
/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.

tests/ui/derive.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ impl<'a> Clone for Lt<'a> {
3535
}
3636
}
3737

38-
// Ok, `Clone` cannot be derived because of the big array
3938
#[derive(Copy)]
4039
struct BigArray {
4140
a: [u8; 65],
@@ -47,7 +46,6 @@ impl Clone for BigArray {
4746
}
4847
}
4948

50-
// Ok, function pointers are not always Clone
5149
#[derive(Copy)]
5250
struct FnPtr {
5351
a: fn() -> !,
@@ -59,7 +57,7 @@ impl Clone for FnPtr {
5957
}
6058
}
6159

62-
// Ok, generics
60+
// Ok, Clone trait impl doesn't have constrained generics.
6361
#[derive(Copy)]
6462
struct Generic<T> {
6563
a: T,
@@ -71,4 +69,23 @@ impl<T> Clone for Generic<T> {
7169
}
7270
}
7371

72+
#[derive(Copy)]
73+
struct Generic2<T>(T);
74+
impl<T: Clone> Clone for Generic2<T> {
75+
fn clone(&self) -> Self {
76+
Self(self.0.clone())
77+
}
78+
}
79+
80+
// Ok, Clone trait impl doesn't have constrained generics.
81+
#[derive(Copy)]
82+
struct GenericRef<'a, T, U>(T, &'a U);
83+
impl<T: Clone, U> Clone for GenericRef<'_, T, U> {
84+
fn clone(&self) -> Self {
85+
Self(self.0.clone(), self.1)
86+
}
87+
}
88+
89+
90+
7491
fn main() {}

tests/ui/derive.stderr

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ LL | | }
4040
| |_^
4141

4242
error: you are implementing `Clone` explicitly on a `Copy` type
43-
--> $DIR/derive.rs:44:1
43+
--> $DIR/derive.rs:43:1
4444
|
4545
LL | / impl Clone for BigArray {
4646
LL | | fn clone(&self) -> Self {
@@ -50,7 +50,7 @@ LL | | }
5050
| |_^
5151
|
5252
note: consider deriving `Clone` or removing `Copy`
53-
--> $DIR/derive.rs:44:1
53+
--> $DIR/derive.rs:43:1
5454
|
5555
LL | / impl Clone for BigArray {
5656
LL | | fn clone(&self) -> Self {
@@ -60,7 +60,7 @@ LL | | }
6060
| |_^
6161

6262
error: you are implementing `Clone` explicitly on a `Copy` type
63-
--> $DIR/derive.rs:56:1
63+
--> $DIR/derive.rs:54:1
6464
|
6565
LL | / impl Clone for FnPtr {
6666
LL | | fn clone(&self) -> Self {
@@ -70,7 +70,7 @@ LL | | }
7070
| |_^
7171
|
7272
note: consider deriving `Clone` or removing `Copy`
73-
--> $DIR/derive.rs:56:1
73+
--> $DIR/derive.rs:54:1
7474
|
7575
LL | / impl Clone for FnPtr {
7676
LL | | fn clone(&self) -> Self {
@@ -79,5 +79,25 @@ LL | | }
7979
LL | | }
8080
| |_^
8181

82-
error: aborting due to 4 previous errors
82+
error: you are implementing `Clone` explicitly on a `Copy` type
83+
--> $DIR/derive.rs:74:1
84+
|
85+
LL | / impl<T: Clone> Clone for Generic2<T> {
86+
LL | | fn clone(&self) -> Self {
87+
LL | | Self(self.0.clone())
88+
LL | | }
89+
LL | | }
90+
| |_^
91+
|
92+
note: consider deriving `Clone` or removing `Copy`
93+
--> $DIR/derive.rs:74:1
94+
|
95+
LL | / impl<T: Clone> Clone for Generic2<T> {
96+
LL | | fn clone(&self) -> Self {
97+
LL | | Self(self.0.clone())
98+
LL | | }
99+
LL | | }
100+
| |_^
101+
102+
error: aborting due to 5 previous errors
83103

0 commit comments

Comments
 (0)