Skip to content

Commit 93aad23

Browse files
authored
Merge pull request #105 from ModProg/clone-skip
skip(Clone)
2 parents ec868ce + 78507a2 commit 93aad23

24 files changed

+257
-60
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ 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 blanket skipping and can only be used with
13+
selective skipping to avoid this being a breaking change.
14+
815
## [1.3.0] - 2025-04-21
916

1017
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Selective skipping of fields for certain traits is also an option, both in
144144
`skip` and `skip_inner`. To prevent breaking invariants defined for these
145145
traits, some of them can only be skipped in groups. The following groups are
146146
available:
147+
- [`Clone`]: Uses [`Default`] instead of [`Clone`].
147148
- [`Debug`]
148149
- `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and
149150
[`PartialEq`].

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/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
//! `skip` and `skip_inner`. To prevent breaking invariants defined for these
171171
//! traits, some of them can only be skipped in groups. The following groups are
172172
//! available:
173+
//! - [`Clone`]: Uses [`Default`] instead of [`Clone`].
173174
//! - [`Debug`]
174175
//! - `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and
175176
//! [`PartialEq`].

src/test/bound.rs

Lines changed: 12 additions & 9 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
}
@@ -128,7 +128,10 @@ fn for_lifetime() -> Result<()> {
128128
#[inline]
129129
fn clone(&self) -> Self {
130130
match self {
131-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
131+
Test(ref __field_0, ref __field_1) => Test {
132+
0: ::core::clone::Clone::clone(__field_0),
133+
1: ::core::clone::Clone::clone(__field_1)
134+
},
132135
}
133136
}
134137
}
@@ -151,7 +154,7 @@ fn associated_type() -> Result<()> {
151154
#[inline]
152155
fn clone(&self) -> Self {
153156
match self {
154-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
157+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
155158
}
156159
}
157160
}
@@ -174,7 +177,7 @@ fn associated_type_custom_bound() -> Result<()> {
174177
#[inline]
175178
fn clone(&self) -> Self {
176179
match self {
177-
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
180+
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
178181
}
179182
}
180183
}
@@ -197,7 +200,7 @@ fn check_trait_bounds() -> Result<()> {
197200
#[inline]
198201
fn clone(&self) -> Self {
199202
match self {
200-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
203+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
201204
}
202205
}
203206
}
@@ -333,7 +336,7 @@ fn check_multiple_trait_bounds() -> Result<()> {
333336
#[inline]
334337
fn clone(&self) -> Self {
335338
match self {
336-
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
339+
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
337340
}
338341
}
339342
}

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) => {

0 commit comments

Comments
 (0)