11use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_note, span_lint_and_then} ;
22use clippy_utils:: paths;
3- use clippy_utils:: ty:: is_copy;
3+ use clippy_utils:: ty:: { implements_trait , is_copy} ;
44use clippy_utils:: { get_trait_def_id, is_allowed, is_automatically_derived, match_def_path} ;
55use if_chain:: if_chain;
66use rustc_hir:: def_id:: DefId ;
@@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass};
1212use rustc_middle:: hir:: map:: Map ;
1313use rustc_middle:: ty:: { self , Ty } ;
1414use 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
1717declare_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.
295295fn 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.
0 commit comments