| use clippy_utils::diagnostics::span_lint_and_help; |
| use clippy_utils::fulfill_or_allowed; |
| use clippy_utils::ty::{implements_trait, is_copy}; |
| use rustc_hir::{self as hir, HirId, Item}; |
| use rustc_lint::LateContext; |
| use rustc_middle::ty::{self, GenericArgKind, Ty}; |
| |
| use super::EXPL_IMPL_CLONE_ON_COPY; |
| |
| /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. |
| pub(super) fn check<'tcx>( |
| cx: &LateContext<'tcx>, |
| item: &Item<'_>, |
| trait_ref: &hir::TraitRef<'_>, |
| ty: Ty<'tcx>, |
| adt_hir_id: HirId, |
| ) { |
| let clone_id = match cx.tcx.lang_items().clone_trait() { |
| Some(id) if trait_ref.trait_def_id() == Some(id) => id, |
| _ => return, |
| }; |
| let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { |
| return; |
| }; |
| let (ty_adt, ty_subs) = match *ty.kind() { |
| // Unions can't derive clone. |
| ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), |
| _ => return, |
| }; |
| // If the current self type doesn't implement Copy (due to generic constraints), search to see if |
| // there's a Copy impl for any instance of the adt. |
| if !is_copy(cx, ty) { |
| if ty_subs.non_erasable_generics().next().is_some() { |
| let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { |
| matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) |
| if ty_adt.did() == adt.did()) |
| }); |
| if !has_copy_impl { |
| return; |
| } |
| } else { |
| return; |
| } |
| } |
| // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for |
| // this impl. |
| if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { |
| return; |
| } |
| // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. |
| // https://github.com/rust-lang/rust-clippy/issues/10188 |
| if ty_adt.repr().packed() |
| && ty_subs |
| .iter() |
| .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) |
| { |
| return; |
| } |
| // The presence of `unsafe` fields prevents deriving `Clone` automatically |
| if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { |
| return; |
| } |
| |
| if fulfill_or_allowed(cx, EXPL_IMPL_CLONE_ON_COPY, [adt_hir_id]) { |
| return; |
| } |
| |
| span_lint_and_help( |
| cx, |
| EXPL_IMPL_CLONE_ON_COPY, |
| item.span, |
| "you are implementing `Clone` explicitly on a `Copy` type", |
| None, |
| "consider deriving `Clone` or removing `Copy`", |
| ); |
| } |