Skip to content

Commit a1163af

Browse files
ModProgdaxpedda
authored andcommitted
skip(Clone)
1 parent ec868ce commit a1163af

21 files changed

+199
-58
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
- Allow skipping fields for `Clone`, calling `Default::default()` instead.
12+
**Note:** `Clone` is excluded from `#[derive_where(skip)]` to avoid this being a breaking change.
13+
814
## [1.3.0] - 2025-04-21
915

1016
### Added

src/attr/item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ impl DeriveWhere {
259259
pub fn any_skip(&self) -> bool {
260260
self.traits
261261
.iter()
262-
.any(|trait_| SkipGroup::trait_supported(**trait_))
262+
.any(|trait_| SkipGroup::trait_supported_by_skip_all(**trait_))
263263
}
264264

265265
/// Create [`WhereClause`] for the given parameters.

src/attr/skip.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::default::Default;
44

55
use syn::{spanned::Spanned, Meta, Path, Result};
66

7-
use crate::{util::MetaListExt, DeriveWhere, Error, Trait};
7+
use crate::{attr::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};
88

99
/// Stores what [`Trait`]s to skip this field or variant for.
1010
#[cfg_attr(test, derive(Debug))]
@@ -101,6 +101,18 @@ impl Skip {
101101
if let Meta::Path(path) = nested_meta {
102102
let skip_group = SkipGroup::from_path(path)?;
103103

104+
if skip_group == SkipGroup::Clone
105+
&& derive_wheres.iter().any(|derive_where| {
106+
derive_where
107+
.traits
108+
.iter()
109+
.any(|trait_| trait_ == &DeriveTrait::Copy)
110+
}) {
111+
return Err(Error::unable_to_skip_clone_while_deriving_copy(
112+
path.span(),
113+
));
114+
}
115+
104116
// Don't allow to skip the same trait twice.
105117
if traits.contains(&skip_group) {
106118
return Err(Error::option_skip_duplicate(
@@ -144,7 +156,7 @@ impl Skip {
144156
pub fn trait_skipped(&self, trait_: Trait) -> bool {
145157
match self {
146158
Skip::None => false,
147-
Skip::All => SkipGroup::trait_supported(trait_),
159+
Skip::All => SkipGroup::trait_supported_by_skip_all(trait_),
148160
Skip::Traits(skip_groups) => skip_groups
149161
.iter()
150162
.any(|skip_group| skip_group.traits().any(|this_trait| this_trait == trait_)),
@@ -166,6 +178,8 @@ impl Skip {
166178
#[derive(Clone, Copy, Eq, PartialEq)]
167179
#[cfg_attr(test, derive(Debug))]
168180
pub enum SkipGroup {
181+
/// [`Clone`].
182+
Clone,
169183
/// [`Debug`].
170184
Debug,
171185
/// [`Eq`], [`Hash`], [`Ord`], [`PartialEq`] and [`PartialOrd`].
@@ -185,6 +199,7 @@ impl SkipGroup {
185199
use SkipGroup::*;
186200

187201
match ident.to_string().as_str() {
202+
"Clone" => Ok(Clone),
188203
"Debug" => Ok(Debug),
189204
"EqHashOrd" => Ok(EqHashOrd),
190205
"Hash" => Ok(Hash),
@@ -202,6 +217,7 @@ impl SkipGroup {
202217
/// messages.
203218
const fn as_str(self) -> &'static str {
204219
match self {
220+
Self::Clone => "Clone",
205221
Self::Debug => "Debug",
206222
Self::EqHashOrd => "EqHashOrd",
207223
Self::Hash => "Hash",
@@ -213,6 +229,9 @@ impl SkipGroup {
213229
/// [`Trait`]s supported by this group.
214230
fn traits(self) -> impl Iterator<Item = Trait> {
215231
match self {
232+
Self::Clone => [Some(Trait::Clone), None, None, None, None]
233+
.into_iter()
234+
.flatten(),
216235
Self::Debug => [Some(Trait::Debug), None, None, None, None]
217236
.into_iter()
218237
.flatten(),
@@ -242,7 +261,7 @@ impl SkipGroup {
242261
}
243262

244263
/// Returns `true` if [`Trait`] is supported by any group.
245-
pub fn trait_supported(trait_: Trait) -> bool {
264+
pub fn trait_supported_by_skip_all(trait_: Trait) -> bool {
246265
match trait_ {
247266
Trait::Clone | Trait::Copy | Trait::Default => false,
248267
Trait::Debug

src/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ impl<'a> Data<'a> {
311311
}
312312

313313
/// Returns `true` if all fields are skipped with that [`Trait`].
314-
fn skip(&self, trait_: Trait) -> bool {
314+
pub fn skip(&self, trait_: Trait) -> bool {
315315
self.skip_inner.trait_skipped(trait_)
316316
|| match self.fields() {
317317
Either::Left(fields) => fields.skip(trait_),

src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ impl Error {
277277
)
278278
}
279279

280+
/// Unsupported `skip(Clone)` while deriving copy.
281+
pub fn unable_to_skip_clone_while_deriving_copy(skip_clone: Span) -> syn::Error {
282+
syn::Error::new(skip_clone, "Cannot skip `Clone` while deriving `Copy`")
283+
}
284+
280285
/// List of available [`Trait`](crate::Trait)s.
281286
fn trait_list() -> String {
282287
[
@@ -300,6 +305,7 @@ impl Error {
300305
/// List of available [`SkipGroup`](crate::SkipGroup)s.
301306
fn skip_group_list() -> String {
302307
[
308+
"Clone",
303309
"Debug",
304310
"EqHashOrd",
305311
"Hash",

src/test/bound.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn bound() -> Result<()> {
1818
#[inline]
1919
fn clone(&self) -> Self {
2020
match self {
21-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
21+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
2222
}
2323
}
2424
}
@@ -48,7 +48,7 @@ fn bound_multiple() -> Result<()> {
4848
#[inline]
4949
fn clone(&self) -> Self {
5050
match self {
51-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
51+
Test(ref __field_0, ref __field_1) => Test{ 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
5252
}
5353
}
5454
}
@@ -78,7 +78,7 @@ fn custom_bound() -> Result<()> {
7878
#[inline]
7979
fn clone(&self) -> Self {
8080
match self {
81-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
81+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
8282
}
8383
}
8484
}
@@ -103,7 +103,7 @@ fn where_() -> Result<()> {
103103
#[inline]
104104
fn clone(&self) -> Self {
105105
match self {
106-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
106+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
107107
}
108108
}
109109
}
@@ -151,7 +151,7 @@ fn associated_type() -> Result<()> {
151151
#[inline]
152152
fn clone(&self) -> Self {
153153
match self {
154-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
154+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
155155
}
156156
}
157157
}
@@ -174,7 +174,7 @@ fn associated_type_custom_bound() -> Result<()> {
174174
#[inline]
175175
fn clone(&self) -> Self {
176176
match self {
177-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
177+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
178178
}
179179
}
180180
}
@@ -197,7 +197,7 @@ fn check_trait_bounds() -> Result<()> {
197197
#[inline]
198198
fn clone(&self) -> Self {
199199
match self {
200-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
200+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
201201
}
202202
}
203203
}
@@ -333,7 +333,7 @@ fn check_multiple_trait_bounds() -> Result<()> {
333333
#[inline]
334334
fn clone(&self) -> Self {
335335
match self {
336-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
336+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
337337
}
338338
}
339339
}

src/test/clone.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,54 @@ fn struct_() -> Result<()> {
2626
)
2727
}
2828

29+
#[test]
30+
fn skip_inner() -> Result<()> {
31+
test_derive(
32+
quote! {
33+
#[derive_where(Clone)]
34+
#[derive_where(skip_inner(Clone))]
35+
struct Test<T> {
36+
field: std::marker::PhantomData<T>,
37+
}
38+
},
39+
quote! {
40+
#[automatically_derived]
41+
impl<T> ::core::clone::Clone for Test<T> {
42+
#[inline]
43+
fn clone(&self) -> Self {
44+
match self {
45+
Test { field: ref __field_field } => Test { field: ::core::default::Default::default() },
46+
}
47+
}
48+
}
49+
},
50+
)
51+
}
52+
53+
#[test]
54+
fn skip_field() -> Result<()> {
55+
test_derive(
56+
quote! {
57+
#[derive_where(Clone)]
58+
struct Test<T> {
59+
#[derive_where(skip(Clone))]
60+
field: std::marker::PhantomData<T>,
61+
}
62+
},
63+
quote! {
64+
#[automatically_derived]
65+
impl<T> ::core::clone::Clone for Test<T> {
66+
#[inline]
67+
fn clone(&self) -> Self {
68+
match self {
69+
Test { field: ref __field_field } => Test { field: ::core::default::Default::default() },
70+
}
71+
}
72+
}
73+
},
74+
)
75+
}
76+
2977
#[test]
3078
fn tuple() -> Result<()> {
3179
test_derive(
@@ -39,7 +87,7 @@ fn tuple() -> Result<()> {
3987
#[inline]
4088
fn clone(&self) -> Self {
4189
match self {
42-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
90+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
4391
}
4492
}
4593
}
@@ -68,8 +116,8 @@ fn enum_() -> Result<()> {
68116
match self {
69117
Test::A { field: ref __field_field } => Test::A { field: ::core::clone::Clone::clone(__field_field) },
70118
Test::B { } => Test::B { },
71-
Test::C(ref __field_0) => Test::C(::core::clone::Clone::clone(__field_0)),
72-
Test::D() => Test::D(),
119+
Test::C(ref __field_0) => Test::C { 0: ::core::clone::Clone::clone(__field_0) },
120+
Test::D() => Test::D { },
73121
Test::E => Test::E,
74122
}
75123
}

src/trait_/clone.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use quote::quote;
55
use syn::{TraitBound, TraitBoundModifier, TypeParamBound};
66

77
use crate::{
8-
Data, DataType, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics, Trait, TraitImpl,
8+
data::Field, Data, DataType, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics, Trait,
9+
TraitImpl,
910
};
1011

1112
/// Dummy-struct implement [`Trait`] for [`Clone`](trait@std::clone::Clone).
@@ -99,25 +100,26 @@ impl TraitImpl for Clone {
99100
}
100101

101102
match data.simple_type() {
102-
SimpleType::Struct(fields) => {
103+
SimpleType::Struct(fields) | SimpleType::Tuple(fields) => {
103104
let self_pattern = &fields.self_pattern;
104105
let item_path = &data.path;
105-
let self_ident = data.iter_self_ident(**trait_);
106-
let fields = data.iter_field_ident(**trait_);
107106
let trait_path = trait_.path();
107+
let default_path = DeriveTrait::Default.path();
108108

109-
quote! {
110-
#self_pattern => #item_path { #(#fields: #trait_path::clone(#self_ident)),* },
111-
}
112-
}
113-
SimpleType::Tuple(fields) => {
114-
let self_pattern = &fields.self_pattern;
115-
let item_path = &data.path;
116-
let self_ident = data.iter_self_ident(**trait_);
117-
let trait_path = trait_.path();
109+
let fields = fields.fields.iter().map(
110+
|field @ Field {
111+
self_ident, member, ..
112+
}| {
113+
if field.skip(Trait::Clone) || data.skip(Trait::Clone) {
114+
quote!(#member: #default_path::default())
115+
} else {
116+
quote!(#member: #trait_path::clone(#self_ident))
117+
}
118+
},
119+
);
118120

119121
quote! {
120-
#self_pattern => #item_path(#(#trait_path::clone(#self_ident)),*),
122+
#self_pattern => #item_path { #(#fields),* },
121123
}
122124
}
123125
SimpleType::Unit(pattern) => {

tests/skip/field_trait.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use std::cmp::Ordering;
44
use derive_where::derive_where;
55

66
use crate::util::{
7-
self, AssertDebug, AssertHash, AssertOrd, AssertPartialEq, AssertPartialOrd, Wrapper,
7+
self, AssertClone, AssertDebug, AssertHash, AssertOrd, AssertPartialEq, AssertPartialOrd,
8+
Wrapper,
89
};
910

1011
#[test]
@@ -19,6 +20,18 @@ fn debug() {
1920
assert_eq!(format!("{:?}", test_1), "Test");
2021
}
2122

23+
#[test]
24+
fn clone() {
25+
#[derive_where(Clone)]
26+
struct Test<T>(#[derive_where(skip(Clone))] Wrapper<T>);
27+
28+
let test_1 = Test(42.into());
29+
30+
let _ = AssertClone(&test_1);
31+
32+
assert_eq!(test_1.clone().0, 0)
33+
}
34+
2235
#[test]
2336
fn hash() {
2437
#[derive_where(Hash)]

0 commit comments

Comments
 (0)