1
1
use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_note, span_lint_and_then} ;
2
2
use clippy_utils:: paths;
3
- use clippy_utils:: ty:: is_copy;
3
+ use clippy_utils:: ty:: { implements_trait , is_copy} ;
4
4
use clippy_utils:: { get_trait_def_id, is_allowed, is_automatically_derived, match_def_path} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_hir:: def_id:: DefId ;
@@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass};
12
12
use rustc_middle:: hir:: map:: Map ;
13
13
use rustc_middle:: ty:: { self , Ty } ;
14
14
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 } ;
16
16
17
17
declare_clippy_lint ! {
18
18
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
@@ -293,48 +293,53 @@ fn check_ord_partial_ord<'tcx>(
293
293
294
294
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
295
295
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 {
303
326
return ;
304
327
}
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
- ) ;
337
328
}
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
+ ) ;
338
343
}
339
344
340
345
/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
0 commit comments