1
1
use clippy_utils:: {
2
2
diagnostics:: { span_lint_and_sugg, span_lint_and_then} ,
3
3
get_parent_node, is_res_lang_ctor, last_path_segment, path_res,
4
- ty:: implements_trait,
4
+ ty:: { implements_trait, is_type_lang_item } ,
5
5
} ;
6
+ use itertools:: Itertools ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: { def:: Res , Expr , ExprKind , ImplItem , ImplItemKind , ItemKind , LangItem , Node , UnOp } ;
8
9
use rustc_hir_analysis:: hir_ty_to_ty;
9
10
use rustc_lint:: { LateContext , LateLintPass } ;
10
- use rustc_middle:: ty:: EarlyBinder ;
11
+ use rustc_middle:: ty:: { self , EarlyBinder } ;
11
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
13
use rustc_span:: { sym, symbol:: kw} ;
13
14
@@ -19,6 +20,14 @@ declare_clippy_lint! {
19
20
/// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing
20
21
/// `self` in `Clone`'s implementation. Anything else is incorrect.
21
22
///
23
+ /// ### Known issues
24
+ /// While anything other than `*self` is *technically* incorrect, it can often be done as an
25
+ /// optimization, like in the case of `MaybeUninit` for example. Returning a new `MaybeUninit`
26
+ /// is both faster and as correct as `memcpy`ing the original. If this is not the case however,
27
+ /// the lint's advice should almost always be applied.
28
+ ///
29
+ /// Note: This lint ignores `Clone` implementations on types that are just `MaybeUninit`.
30
+ ///
22
31
/// ### Example
23
32
/// ```rust,ignore
24
33
/// #[derive(Eq, PartialEq)]
@@ -47,7 +56,7 @@ declare_clippy_lint! {
47
56
/// ```
48
57
#[ clippy:: version = "1.72.0" ]
49
58
pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE ,
50
- correctness ,
59
+ complexity ,
51
60
"manual implementation of `Clone` on a `Copy` type"
52
61
}
53
62
declare_clippy_lint ! {
@@ -140,7 +149,31 @@ impl LateLintPass<'_> for IncorrectImpls {
140
149
copy_def_id,
141
150
& [ ] ,
142
151
)
152
+ && let ty:: Adt ( def, substs) = hir_ty_to_ty ( cx. tcx , imp. self_ty ) . kind ( )
143
153
{
154
+ let fields = def. all_fields ( ) . collect_vec ( ) ;
155
+ // Necessary as `all` returns true on an empty iterator
156
+ if !fields. is_empty ( )
157
+ && fields. iter ( ) . all ( |field| {
158
+ let ty = field. ty ( cx. tcx , substs) ;
159
+
160
+ match ty. kind ( ) {
161
+ // `MaybeUninit<T>`
162
+ ty:: Adt ( _, _) if is_type_lang_item ( cx, ty, LangItem :: MaybeUninit ) => true ,
163
+ // `[MaybeUninit<T>; N]`
164
+ ty:: Array ( inner_ty, _) | ty:: Slice ( inner_ty)
165
+ if is_type_lang_item ( cx, * inner_ty, LangItem :: MaybeUninit ) =>
166
+ {
167
+ true
168
+ } ,
169
+ // Other cases are likely pretty rare.
170
+ _ => false ,
171
+ }
172
+ } )
173
+ {
174
+ return ;
175
+ }
176
+
144
177
if impl_item. ident . name == sym:: clone {
145
178
if block. stmts . is_empty ( )
146
179
&& let Some ( expr) = block. expr
0 commit comments