Skip to content

Commit

Permalink
skip(Clone)
Browse files Browse the repository at this point in the history
  • Loading branch information
ModProg committed Jan 30, 2025
1 parent b368804 commit 5d00b0c
Show file tree
Hide file tree
Showing 21 changed files with 196 additions and 58 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Allow skipping fields for `Clone`, calling `Default::default()` instead.
**Note:** `Clone` is excluded from `#[derive_where(skip)]` to avoid this being a breaking change.

### Changed
- Use the `Copy` implementation for `Clone` and the `Ord` implementation for
Expand Down
2 changes: 1 addition & 1 deletion src/attr/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl DeriveWhere {
pub fn any_skip(&self) -> bool {
self.traits
.iter()
.any(|trait_| SkipGroup::trait_supported(**trait_))
.any(|trait_| SkipGroup::trait_supported_by_skip_all(**trait_))
}

/// Create [`WhereClause`] for the given parameters.
Expand Down
25 changes: 22 additions & 3 deletions src/attr/skip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::default::Default;

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

use crate::{util::MetaListExt, DeriveWhere, Error, Trait};
use crate::{attr::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};

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

if skip_group == SkipGroup::Clone
&& derive_wheres.iter().any(|derive_where| {
derive_where
.traits
.iter()
.any(|trait_| trait_ == &DeriveTrait::Copy)
}) {
return Err(Error::unable_to_skip_clone_while_deriving_copy(
path.span(),
));
}

// Don't allow to skip the same trait twice.
if traits.contains(&skip_group) {
return Err(Error::option_skip_duplicate(
Expand Down Expand Up @@ -144,7 +156,7 @@ impl Skip {
pub fn trait_skipped(&self, trait_: Trait) -> bool {
match self {
Skip::None => false,
Skip::All => SkipGroup::trait_supported(trait_),
Skip::All => SkipGroup::trait_supported_by_skip_all(trait_),
Skip::Traits(skip_groups) => skip_groups
.iter()
.any(|skip_group| skip_group.traits().any(|this_trait| this_trait == trait_)),
Expand All @@ -166,6 +178,8 @@ impl Skip {
#[derive(Clone, Copy, Eq, PartialEq)]
#[cfg_attr(test, derive(Debug))]
pub enum SkipGroup {
/// [`Clone`].
Clone,
/// [`Debug`].
Debug,
/// [`Eq`], [`Hash`], [`Ord`], [`PartialEq`] and [`PartialOrd`].
Expand All @@ -185,6 +199,7 @@ impl SkipGroup {
use SkipGroup::*;

match ident.to_string().as_str() {
"Clone" => Ok(Clone),
"Debug" => Ok(Debug),
"EqHashOrd" => Ok(EqHashOrd),
"Hash" => Ok(Hash),
Expand All @@ -202,6 +217,7 @@ impl SkipGroup {
/// messages.
const fn as_str(self) -> &'static str {
match self {
Self::Clone => "Clone",
Self::Debug => "Debug",
Self::EqHashOrd => "EqHashOrd",
Self::Hash => "Hash",
Expand All @@ -213,6 +229,9 @@ impl SkipGroup {
/// [`Trait`]s supported by this group.
fn traits(self) -> impl Iterator<Item = Trait> {
match self {
Self::Clone => [Some(Trait::Clone), None, None, None, None]
.into_iter()
.flatten(),
Self::Debug => [Some(Trait::Debug), None, None, None, None]
.into_iter()
.flatten(),
Expand Down Expand Up @@ -242,7 +261,7 @@ impl SkipGroup {
}

/// Returns `true` if [`Trait`] is supported by any group.
pub fn trait_supported(trait_: Trait) -> bool {
pub fn trait_supported_by_skip_all(trait_: Trait) -> bool {
match trait_ {
Trait::Clone | Trait::Copy | Trait::Default => false,
Trait::Debug
Expand Down
2 changes: 1 addition & 1 deletion src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ impl<'a> Data<'a> {
}

/// Returns `true` if all fields are skipped with that [`Trait`].
fn skip(&self, trait_: Trait) -> bool {
pub fn skip(&self, trait_: Trait) -> bool {
self.skip_inner.trait_skipped(trait_)
|| match self.fields() {
Either::Left(fields) => fields.skip(trait_),
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ impl Error {
)
}

/// Unsupported `skip(Clone)` while deriving copy.
pub fn unable_to_skip_clone_while_deriving_copy(skip_clone: Span) -> syn::Error {
syn::Error::new(skip_clone, "Cannot skip `Clone` while deriving `Copy`")
}

/// List of available [`Trait`](crate::Trait)s.
fn trait_list() -> String {
[
Expand All @@ -300,6 +305,7 @@ impl Error {
/// List of available [`SkipGroup`](crate::SkipGroup)s.
fn skip_group_list() -> String {
[
"Clone",
"Debug",
"EqHashOrd",
"Hash",
Expand Down
16 changes: 8 additions & 8 deletions src/test/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn bound() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
}
}
}
Expand Down Expand Up @@ -48,7 +48,7 @@ fn bound_multiple() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
Test(ref __field_0, ref __field_1) => Test{ 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
}
}
}
Expand Down Expand Up @@ -78,7 +78,7 @@ fn custom_bound() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
}
}
}
Expand All @@ -103,7 +103,7 @@ fn where_() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
}
}
}
Expand All @@ -126,7 +126,7 @@ fn associated_type() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
}
}
}
Expand All @@ -149,7 +149,7 @@ fn associated_type_custom_bound() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
}
}
}
Expand All @@ -172,7 +172,7 @@ fn check_trait_bounds() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
}
}
}
Expand Down Expand Up @@ -308,7 +308,7 @@ fn check_multiple_trait_bounds() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
Test(ref __field_0, ref __field_1) => Test { 0: ::core::clone::Clone::clone(__field_0), 1: ::core::clone::Clone::clone(__field_1) },
}
}
}
Expand Down
54 changes: 51 additions & 3 deletions src/test/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,54 @@ fn struct_() -> Result<()> {
)
}

#[test]
fn skip_inner() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone)]
#[derive_where(skip_inner(Clone))]
struct Test<T> {
field: std::marker::PhantomData<T>,
}
},
quote! {
#[automatically_derived]
impl<T> ::core::clone::Clone for Test<T> {
#[inline]
fn clone(&self) -> Self {
match self {
Test { field: ref __field_field } => Test { field: ::core::default::Default::default() },
}
}
}
},
)
}

#[test]
fn skip_field() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone)]
struct Test<T> {
#[derive_where(skip(Clone))]
field: std::marker::PhantomData<T>,
}
},
quote! {
#[automatically_derived]
impl<T> ::core::clone::Clone for Test<T> {
#[inline]
fn clone(&self) -> Self {
match self {
Test { field: ref __field_field } => Test { field: ::core::default::Default::default() },
}
}
}
},
)
}

#[test]
fn tuple() -> Result<()> {
test_derive(
Expand All @@ -39,7 +87,7 @@ fn tuple() -> Result<()> {
#[inline]
fn clone(&self) -> Self {
match self {
Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)),
Test(ref __field_0) => Test { 0: ::core::clone::Clone::clone(__field_0) },
}
}
}
Expand Down Expand Up @@ -68,8 +116,8 @@ fn enum_() -> Result<()> {
match self {
Test::A { field: ref __field_field } => Test::A { field: ::core::clone::Clone::clone(__field_field) },
Test::B { } => Test::B { },
Test::C(ref __field_0) => Test::C(::core::clone::Clone::clone(__field_0)),
Test::D() => Test::D(),
Test::C(ref __field_0) => Test::C { 0: ::core::clone::Clone::clone(__field_0) },
Test::D() => Test::D { },
Test::E => Test::E,
}
}
Expand Down
30 changes: 16 additions & 14 deletions src/trait_/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use quote::quote;
use syn::{TraitBound, TraitBoundModifier, TypeParamBound};

use crate::{
Data, DataType, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics, Trait, TraitImpl,
data::Field, Data, DataType, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics, Trait,
TraitImpl,
};

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

match data.simple_type() {
SimpleType::Struct(fields) => {
SimpleType::Struct(fields) | SimpleType::Tuple(fields) => {
let self_pattern = &fields.self_pattern;
let item_path = &data.path;
let self_ident = data.iter_self_ident(**trait_);
let fields = data.iter_field_ident(**trait_);
let trait_path = trait_.path();
let default_path = DeriveTrait::Default.path();

quote! {
#self_pattern => #item_path { #(#fields: #trait_path::clone(#self_ident)),* },
}
}
SimpleType::Tuple(fields) => {
let self_pattern = &fields.self_pattern;
let item_path = &data.path;
let self_ident = data.iter_self_ident(**trait_);
let trait_path = trait_.path();
let fields = fields.fields.iter().map(
|field @ Field {
self_ident, member, ..
}| {
if field.skip(Trait::Clone) || data.skip(Trait::Clone) {
quote!(#member: #default_path::default())
} else {
quote!(#member: #trait_path::clone(#self_ident))
}
},
);

quote! {
#self_pattern => #item_path(#(#trait_path::clone(#self_ident)),*),
#self_pattern => #item_path { #(#fields),* },
}
}
SimpleType::Unit(pattern) => {
Expand Down
15 changes: 14 additions & 1 deletion tests/skip/field_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::cmp::Ordering;
use derive_where::derive_where;

use crate::util::{
self, AssertDebug, AssertHash, AssertOrd, AssertPartialEq, AssertPartialOrd, Wrapper,
self, AssertClone, AssertDebug, AssertHash, AssertOrd, AssertPartialEq, AssertPartialOrd,
Wrapper,
};

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

#[test]
fn clone() {
#[derive_where(Clone)]
struct Test<T>(#[derive_where(skip(Clone))] Wrapper<T>);

let test_1 = Test(42.into());

let _ = AssertClone(&test_1);

assert_eq!(test_1.clone().0, 0)
}

#[test]
fn hash() {
#[derive_where(Hash)]
Expand Down
Loading

0 comments on commit 5d00b0c

Please sign in to comment.