• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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