Skip to content

Commit 54c8641

Browse files
committed
Do not lint if only has MaybeUninit fields
1 parent a959061 commit 54c8641

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

clippy_lints/src/incorrect_impls.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait};
1+
use clippy_utils::{
2+
diagnostics::span_lint_and_sugg,
3+
get_parent_node, last_path_segment,
4+
ty::{implements_trait, is_type_lang_item},
5+
};
6+
use itertools::Itertools;
27
use rustc_errors::Applicability;
3-
use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp};
8+
use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp};
49
use rustc_hir_analysis::hir_ty_to_ty;
510
use rustc_lint::{LateContext, LateLintPass};
6-
use rustc_middle::ty::EarlyBinder;
11+
use rustc_middle::ty::{self, EarlyBinder};
712
use rustc_session::{declare_lint_pass, declare_tool_lint};
813
use rustc_span::{sym, symbol};
914

@@ -15,6 +20,14 @@ declare_clippy_lint! {
1520
/// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing
1621
/// `self` in `Clone`'s implementation. Anything else is incorrect.
1722
///
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+
///
1831
/// ### Example
1932
/// ```rust,ignore
2033
/// #[derive(Eq, PartialEq)]
@@ -43,7 +56,7 @@ declare_clippy_lint! {
4356
/// ```
4457
#[clippy::version = "1.72.0"]
4558
pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
46-
correctness,
59+
complexity,
4760
"manual implementation of `Clone` on a `Copy` type"
4861
}
4962
declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]);
@@ -84,7 +97,31 @@ impl LateLintPass<'_> for IncorrectImpls {
8497
copy_def_id,
8598
trait_impl.substs,
8699
)
100+
&& let ty::Adt(def, substs) = hir_ty_to_ty(cx.tcx, imp.self_ty).kind()
87101
{
102+
let fields = def.all_fields().collect_vec();
103+
// Necessary as `all` returns true on an empty iterator
104+
if !fields.is_empty()
105+
&& fields.iter().all(|field| {
106+
let ty = field.ty(cx.tcx, substs);
107+
108+
match ty.kind() {
109+
// `MaybeUninit<T>`
110+
ty::Adt(_, _) if is_type_lang_item(cx, ty, LangItem::MaybeUninit) => true,
111+
// `[MaybeUninit<T>; N]`
112+
ty::Array(inner_ty, _) | ty::Slice(inner_ty)
113+
if is_type_lang_item(cx, *inner_ty, LangItem::MaybeUninit) =>
114+
{
115+
true
116+
},
117+
// Other cases are likely pretty rare.
118+
_ => false,
119+
}
120+
})
121+
{
122+
return;
123+
}
124+
88125
if impl_item.ident.name == sym::clone {
89126
if block.stmts.is_empty()
90127
&& let Some(expr) = block.expr

tests/ui/incorrect_clone_impl_on_copy_type.fixed

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,14 @@ impl<A: Copy> Clone for Uwu<A> {
9595
}
9696

9797
impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
98+
99+
// do not lint if type has only `MaybeUninit` fields
100+
struct G([std::mem::MaybeUninit<u32>; 100]);
101+
102+
impl Clone for G {
103+
fn clone(&self) -> Self {
104+
todo!()
105+
}
106+
}
107+
108+
impl Copy for G {}

tests/ui/incorrect_clone_impl_on_copy_type.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,14 @@ impl<A: Copy> Clone for Uwu<A> {
105105
}
106106

107107
impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
108+
109+
// do not lint if type has only `MaybeUninit` fields
110+
struct G([std::mem::MaybeUninit<u32>; 100]);
111+
112+
impl Clone for G {
113+
fn clone(&self) -> Self {
114+
todo!()
115+
}
116+
}
117+
118+
impl Copy for G {}

tests/ui/incorrect_clone_impl_on_copy_type.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | | Self(self.0)
77
LL | | }
88
| |_____^ help: change this to: `{ *self }`
99
|
10-
= note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default
10+
= note: `-D clippy::incorrect-clone-impl-on-copy-type` implied by `-D warnings`
1111

1212
error: incorrect implementation of `clone_from` on a `Copy` type
1313
--> $DIR/incorrect_clone_impl_on_copy_type.rs:14:5

0 commit comments

Comments
 (0)