1 use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait}; 2 use rustc_errors::Applicability; 3 use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp}; 4 use rustc_hir_analysis::hir_ty_to_ty; 5 use rustc_lint::{LateContext, LateLintPass}; 6 use rustc_middle::ty::EarlyBinder; 7 use rustc_session::{declare_lint_pass, declare_tool_lint}; 8 use rustc_span::{sym, symbol}; 9 10 declare_clippy_lint! { 11 /// ### What it does 12 /// Checks for manual implementations of `Clone` when `Copy` is already implemented. 13 /// 14 /// ### Why is this bad? 15 /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing 16 /// `self` in `Clone`'s implementation. Anything else is incorrect. 17 /// 18 /// ### Example 19 /// ```rust,ignore 20 /// #[derive(Eq, PartialEq)] 21 /// struct A(u32); 22 /// 23 /// impl Clone for A { 24 /// fn clone(&self) -> Self { 25 /// Self(self.0) 26 /// } 27 /// } 28 /// 29 /// impl Copy for A {} 30 /// ``` 31 /// Use instead: 32 /// ```rust,ignore 33 /// #[derive(Eq, PartialEq)] 34 /// struct A(u32); 35 /// 36 /// impl Clone for A { 37 /// fn clone(&self) -> Self { 38 /// *self 39 /// } 40 /// } 41 /// 42 /// impl Copy for A {} 43 /// ``` 44 #[clippy::version = "1.72.0"] 45 pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE, 46 correctness, 47 "manual implementation of `Clone` on a `Copy` type" 48 } 49 declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]); 50 51 impl LateLintPass<'_> for IncorrectImpls { 52 #[expect(clippy::needless_return)] check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>)53 fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { 54 let node = get_parent_node(cx.tcx, impl_item.hir_id()); 55 let Some(Node::Item(item)) = node else { 56 return; 57 }; 58 let ItemKind::Impl(imp) = item.kind else { 59 return; 60 }; 61 let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { 62 return; 63 }; 64 let trait_impl_def_id = trait_impl.def_id; 65 if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { 66 return; 67 } 68 let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { 69 return; 70 }; 71 let body = cx.tcx.hir().body(impl_item_id); 72 let ExprKind::Block(block, ..) = body.value.kind else { 73 return; 74 }; 75 // Above is duplicated from the `duplicate_manual_partial_ord_impl` branch. 76 // Remove it while solving conflicts once that PR is merged. 77 78 // Actual implementation; remove this comment once aforementioned PR is merged 79 if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl_def_id) 80 && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) 81 && implements_trait( 82 cx, 83 hir_ty_to_ty(cx.tcx, imp.self_ty), 84 copy_def_id, 85 &[], 86 ) 87 { 88 if impl_item.ident.name == sym::clone { 89 if block.stmts.is_empty() 90 && let Some(expr) = block.expr 91 && let ExprKind::Unary(UnOp::Deref, inner) = expr.kind 92 && let ExprKind::Path(qpath) = inner.kind 93 && last_path_segment(&qpath).ident.name == symbol::kw::SelfLower 94 {} else { 95 span_lint_and_sugg( 96 cx, 97 INCORRECT_CLONE_IMPL_ON_COPY_TYPE, 98 block.span, 99 "incorrect implementation of `clone` on a `Copy` type", 100 "change this to", 101 "{ *self }".to_owned(), 102 Applicability::MaybeIncorrect, 103 ); 104 105 return; 106 } 107 } 108 109 if impl_item.ident.name == sym::clone_from { 110 span_lint_and_sugg( 111 cx, 112 INCORRECT_CLONE_IMPL_ON_COPY_TYPE, 113 impl_item.span, 114 "incorrect implementation of `clone_from` on a `Copy` type", 115 "remove this", 116 String::new(), 117 Applicability::MaybeIncorrect, 118 ); 119 120 return; 121 } 122 } 123 } 124 } 125