Skip to content

Commit 87b5f89

Browse files
committed
Auto merge of #10925 - Centri3:needless_clone_impl2, r=xFrednet
add lint [`incorrect_clone_impl_on_copy_type`] Split off from #10788. Closes #10700 ---- changelog: new lint [`incorrect_clone_impl_on_copy_type`] [#10925](#10925)
2 parents ee67c79 + 10cc168 commit 87b5f89

11 files changed

+377
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4834,6 +4834,7 @@ Released 2018-09-13
48344834
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
48354835
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
48364836
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
4837+
[`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type
48374838
[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
48384839
[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
48394840
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
206206
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
207207
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
208208
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
209+
crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
209210
crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
210211
crate::indexing_slicing::INDEXING_SLICING_INFO,
211212
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,

clippy_lints/src/incorrect_impls.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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)]
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+
trait_impl.substs,
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+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ mod implicit_return;
150150
mod implicit_saturating_add;
151151
mod implicit_saturating_sub;
152152
mod inconsistent_struct_constructor;
153+
mod incorrect_impls;
153154
mod index_refutable_slice;
154155
mod indexing_slicing;
155156
mod infinite_iter;
@@ -1047,6 +1048,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10471048
let stack_size_threshold = conf.stack_size_threshold;
10481049
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
10491050
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
1051+
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
10501052
// add lints here, do not remove this comment, it's used in `new_lint`
10511053
}
10521054

tests/ui/clone_on_copy_impl.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::incorrect_clone_impl_on_copy_type)]
2+
13
use std::fmt;
24
use std::marker::PhantomData;
35

tests/ui/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(dead_code)]
1+
#![allow(clippy::incorrect_clone_impl_on_copy_type, dead_code)]
22
#![warn(clippy::expl_impl_clone_on_copy)]
33

44
#[derive(Copy)]
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//@run-rustfix
2+
#![allow(clippy::clone_on_copy, unused)]
3+
#![no_main]
4+
5+
// lint
6+
7+
struct A(u32);
8+
9+
impl Clone for A {
10+
fn clone(&self) -> Self { *self }
11+
12+
13+
}
14+
15+
impl Copy for A {}
16+
17+
// do not lint
18+
19+
struct B(u32);
20+
21+
impl Clone for B {
22+
fn clone(&self) -> Self {
23+
*self
24+
}
25+
}
26+
27+
impl Copy for B {}
28+
29+
// do not lint derived (clone's implementation is `*self` here anyway)
30+
31+
#[derive(Clone, Copy)]
32+
struct C(u32);
33+
34+
// do not lint derived (fr this time)
35+
36+
struct D(u32);
37+
38+
#[automatically_derived]
39+
impl Clone for D {
40+
fn clone(&self) -> Self {
41+
Self(self.0)
42+
}
43+
44+
fn clone_from(&mut self, source: &Self) {
45+
source.clone();
46+
*self = source.clone();
47+
}
48+
}
49+
50+
impl Copy for D {}
51+
52+
// do not lint if clone is not manually implemented
53+
54+
struct E(u32);
55+
56+
#[automatically_derived]
57+
impl Clone for E {
58+
fn clone(&self) -> Self {
59+
Self(self.0)
60+
}
61+
62+
fn clone_from(&mut self, source: &Self) {
63+
source.clone();
64+
*self = source.clone();
65+
}
66+
}
67+
68+
impl Copy for E {}
69+
70+
// lint since clone is not derived
71+
72+
#[derive(Copy)]
73+
struct F(u32);
74+
75+
impl Clone for F {
76+
fn clone(&self) -> Self { *self }
77+
78+
79+
}
80+
81+
// do not lint since copy has more restrictive bounds
82+
83+
#[derive(Eq, PartialEq)]
84+
struct Uwu<A: Copy>(A);
85+
86+
impl<A: Copy> Clone for Uwu<A> {
87+
fn clone(&self) -> Self {
88+
Self(self.0)
89+
}
90+
91+
fn clone_from(&mut self, source: &Self) {
92+
source.clone();
93+
*self = source.clone();
94+
}
95+
}
96+
97+
impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//@run-rustfix
2+
#![allow(clippy::clone_on_copy, unused)]
3+
#![no_main]
4+
5+
// lint
6+
7+
struct A(u32);
8+
9+
impl Clone for A {
10+
fn clone(&self) -> Self {
11+
Self(self.0)
12+
}
13+
14+
fn clone_from(&mut self, source: &Self) {
15+
source.clone();
16+
*self = source.clone();
17+
}
18+
}
19+
20+
impl Copy for A {}
21+
22+
// do not lint
23+
24+
struct B(u32);
25+
26+
impl Clone for B {
27+
fn clone(&self) -> Self {
28+
*self
29+
}
30+
}
31+
32+
impl Copy for B {}
33+
34+
// do not lint derived (clone's implementation is `*self` here anyway)
35+
36+
#[derive(Clone, Copy)]
37+
struct C(u32);
38+
39+
// do not lint derived (fr this time)
40+
41+
struct D(u32);
42+
43+
#[automatically_derived]
44+
impl Clone for D {
45+
fn clone(&self) -> Self {
46+
Self(self.0)
47+
}
48+
49+
fn clone_from(&mut self, source: &Self) {
50+
source.clone();
51+
*self = source.clone();
52+
}
53+
}
54+
55+
impl Copy for D {}
56+
57+
// do not lint if clone is not manually implemented
58+
59+
struct E(u32);
60+
61+
#[automatically_derived]
62+
impl Clone for E {
63+
fn clone(&self) -> Self {
64+
Self(self.0)
65+
}
66+
67+
fn clone_from(&mut self, source: &Self) {
68+
source.clone();
69+
*self = source.clone();
70+
}
71+
}
72+
73+
impl Copy for E {}
74+
75+
// lint since clone is not derived
76+
77+
#[derive(Copy)]
78+
struct F(u32);
79+
80+
impl Clone for F {
81+
fn clone(&self) -> Self {
82+
Self(self.0)
83+
}
84+
85+
fn clone_from(&mut self, source: &Self) {
86+
source.clone();
87+
*self = source.clone();
88+
}
89+
}
90+
91+
// do not lint since copy has more restrictive bounds
92+
93+
#[derive(Eq, PartialEq)]
94+
struct Uwu<A: Copy>(A);
95+
96+
impl<A: Copy> Clone for Uwu<A> {
97+
fn clone(&self) -> Self {
98+
Self(self.0)
99+
}
100+
101+
fn clone_from(&mut self, source: &Self) {
102+
source.clone();
103+
*self = source.clone();
104+
}
105+
}
106+
107+
impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}

0 commit comments

Comments
 (0)