Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#479](https://github.com/JelteF/derive_more/pull/479))
- Proxy-pass `#[allow]`/`#[expect]` attributes of the type in `Constructor` derive.
([#477](https://github.com/JelteF/derive_more/pull/477))
- Support `Deref` and `DerefMut` derives for enums.
([#485](https://github.com/JelteF/derive_more/pull/485))

### Changed

Expand Down
93 changes: 91 additions & 2 deletions impl/doc/deref.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Using `#[derive(Deref)]`

Deriving `Deref` only works for a single field of a struct.
Deriving `Deref` only works for a single field of a struct, or a single field of each variant of an enum.
It's possible to use it in two ways:

1. Dereferencing to the field, i.e. like if your type was a reference type.
Expand All @@ -22,10 +22,23 @@ struct Num {
num: i32,
}

#[derive(Deref)]
enum Enum {
V1(i32),
V2 { num: i32 },
}

#[derive(Deref)]
#[deref(forward)]
struct MyBoxedInt(Box<i32>);

#[derive(Deref)]
#[deref(forward)]
enum MyBoxedIntEnum {
V1(Box<i32>),
V2 { num: Box<i32> },
}

// You can specify the field you want to derive `Deref` for.
#[derive(Deref)]
struct CoolVec {
Expand All @@ -34,12 +47,25 @@ struct CoolVec {
vec: Vec<i32>,
}

#[derive(Deref)]
enum CoolVecEnum {
V1(Vec<i32>),
V2 { cool: bool, #[deref] vec: Vec<i32> },
}

let num = Num{num: 123};
let boxed = MyBoxedInt(Box::new(123));
let cool_vec = CoolVec{cool: true, vec: vec![123]};
assert_eq!(123, *num);
assert_eq!(123, *boxed);
assert_eq!(vec![123], *cool_vec);

let num_v2 = Enum::V2{num: 123};
let boxed_v1 = MyBoxedIntEnum::V1(Box::new(123));
let cool_vec_v2 = CoolVecEnum::V2{cool: true, vec: vec![123]};
assert_eq!(123, *num_v2);
assert_eq!(123, *boxed_v1);
assert_eq!(vec![123], *cool_vec_v2);
```


Expand Down Expand Up @@ -104,4 +130,67 @@ impl derive_more::core::ops::Deref for MyBoxedInt {

## Enums

Deriving `Deref` is not supported for enums.
When deriving a non-forwarded `Deref` for an enum:

```rust
# use derive_more::Deref;
#
#[derive(Deref)]
enum CoolVecEnum {
V1(Vec<i32>),
V2 { cool: bool, #[deref] vec: Vec<i32> },
}
```

Code like this will be generated:

```rust
# enum CoolVecEnum {
# V1(Vec<i32>),
# V2 { cool: bool, vec: Vec<i32> },
# }
impl derive_more::with_trait::Deref for CoolVecEnum {
type Target = Vec<i32>;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
CoolVecEnum::V1(__0) => __0,
CoolVecEnum::V2 { cool: _, vec: __0 } => __0,
}
}
}
```

When deriving a forwarded `Deref` for an enum:

```rust
# use derive_more::Deref;
#
#[derive(Deref)]
#[deref(forward)]
enum MyBoxedIntEnum {
V1(Box<i32>),
V2 { num: Box<i32> },
}
```

Code like this will be generated:

```rust
# enum MyBoxedIntEnum {
# V1(Box<i32>),
# V2 { num: Box<i32> },
# }
impl derive_more::with_trait::Deref for MyBoxedIntEnum {
type Target = <Box<i32> as derive_more::with_trait::Deref>::Target;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
MyBoxedIntEnum::V1(__0) =>
<Box<i32> as derive_more::with_trait::Deref>::deref(__0),
MyBoxedIntEnum::V2 { num: __0 } =>
<Box<i32> as derive_more::with_trait::Deref>::deref(__0),
}
}
}
```
128 changes: 125 additions & 3 deletions impl/doc/deref_mut.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# What `#[derive(DerefMut)]` generates

Deriving `Deref` only works for a single field of a struct.
Deriving `Deref` only works for a single field of a struct, or a single field of
each variant of an enum.
Furthermore it requires that the type also implements `Deref`, so usually
`Deref` should also be derived.
The resulting implementation of `Deref` will allow you to mutably dereference
Expand All @@ -26,11 +27,25 @@ struct Num {
num: i32,
}

#[derive(Deref, DerefMut)]
enum Enum {
V1(i32),
V2 { num: i32 },
}

#[derive(Deref, DerefMut)]
#[deref(forward)]
#[deref_mut(forward)]
struct MyBoxedInt(Box<i32>);

#[derive(Deref, DerefMut)]
#[deref(forward)]
#[deref_mut(forward)]
enum MyBoxedIntEnum {
V1(Box<i32>),
V2 { num: Box<i32> },
}

// You can specify the field you want to derive DerefMut for
#[derive(Deref, DerefMut)]
struct CoolVec {
Expand All @@ -40,6 +55,17 @@ struct CoolVec {
vec: Vec<i32>,
}

#[derive(Deref, DerefMut)]
enum CoolVecEnum {
V1(Vec<i32>),
V2 {
cool: bool,
#[deref]
#[deref_mut]
vec: Vec<i32>,
},
}

let mut num = Num{num: 123};
let mut boxed = MyBoxedInt(Box::new(123));
let mut cool_vec = CoolVec{cool: true, vec: vec![123]};
Expand All @@ -56,7 +82,7 @@ assert_eq!(vec![123, 456], *cool_vec);

## Structs

When deriving a non-forwarded `Deref` for a struct:
When deriving a non-forwarded `DerefMut` for a struct:

```rust
# use derive_more::{Deref, DerefMut};
Expand Down Expand Up @@ -129,4 +155,100 @@ impl derive_more::core::ops::DerefMut for MyBoxedInt {

## Enums

Deriving `DerefMut` is not supported for enums.
When deriving a non-forwarded `DerefMut` for an enum:

```rust
# use derive_more::{Deref, DerefMut};
#
#[derive(Deref, DerefMut)]
enum CoolVecEnum {
V1(Vec<i32>),
V2 {
cool: bool,
#[deref]
#[deref_mut]
vec: Vec<i32>,
},
}
```

Code like this will be generated:

```rust
# use ::core::ops::Deref;
# enum CoolVecEnum {
# V1(Vec<i32>),
# V2 {
# cool: bool,
# vec: Vec<i32>,
# },
# }
# impl Deref for CoolVecEnum {
# type Target = Vec<i32>;
# #[inline]
# fn deref(&self) -> &Self::Target {
# match self {
# CoolVecEnum::V1(__0) => __0,
# CoolVecEnum::V2 { cool: _, vec: __0 } => __0,
# }
# }
# }
impl derive_more::with_trait::DerefMut for CoolVecEnum {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
CoolVecEnum::V1(__0) => __0,
CoolVecEnum::V2 { cool: _, vec: __0 } => __0,
}
}
}
```

When deriving a forwarded `DerefMut` for an enum:

```rust
# use derive_more::{Deref, DerefMut};
#
#[derive(Deref, DerefMut)]
#[deref(forward)]
#[deref_mut(forward)]
enum MyBoxedIntEnum {
V1(Box<i32>),
V2 { num: Box<i32> },
}
```

Code like this will be generated:

```rust
# use ::core::ops::Deref;
# enum MyBoxedIntEnum {
# V1(Box<i32>),
# V2 { num: Box<i32> },
# }
# impl Deref for MyBoxedIntEnum {
# type Target = <Box<i32> as derive_more::with_trait::Deref>::Target;
# #[inline]
# fn deref(&self) -> &Self::Target {
# match self {
# MyBoxedIntEnum::V1(__0) =>
# <Box<i32> as derive_more::with_trait::Deref>::deref(__0),
# MyBoxedIntEnum::V2 { num: __0 } =>
# <Box<i32> as derive_more::with_trait::Deref>::deref(__0),
# }
# }
# }
impl derive_more::with_trait::DerefMut for MyBoxedIntEnum {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
MyBoxedIntEnum::V1(__0) => {
<Box<i32> as derive_more::with_trait::DerefMut>::deref_mut(__0)
}
MyBoxedIntEnum::V2 { num: __0 } => {
<Box<i32> as derive_more::with_trait::DerefMut>::deref_mut(__0)
}
}
}
}
```
87 changes: 85 additions & 2 deletions impl/src/deref.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,93 @@
use crate::utils::{add_extra_where_clauses, SingleFieldData, State};
use crate::utils::{
add_extra_where_clauses, numbered_vars, panic_one_field, SingleFieldData, State,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse::Result, DeriveInput};
use syn::{parse::Result, Data, DeriveInput};

/// Provides the hook to expand `#[derive(Deref)]` into an implementation of `Deref`
pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
match input.data {
Data::Struct(_) => expand_struct(input, trait_name),
Data::Enum(_) => expand_enum(input, trait_name),
_ => panic!("only structs and enums can use `derive({trait_name})`"),
}
}

fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
let state = State::with_field_ignore_and_forward(
input,
trait_name,
trait_name.to_lowercase(),
)?;

let trait_path = &state.trait_path;
let enum_name = &input.ident;

let mut target = None;
let mut match_arms = vec![];
let mut predicates = vec![];

for variant_state in state.enabled_variant_data().variant_states.into_iter() {
let data = variant_state.enabled_fields_data();
if data.fields.len() != 1 {
panic_one_field(variant_state.trait_name, &variant_state.trait_attr);
};

let vars = numbered_vars(variant_state.fields.len(), "");
let matcher = data.matcher(&data.field_indexes, &vars);

let info = data.infos[0].clone();
let field_type = data.field_types[0];

let (target_, var) = if info.forward {
let casted_trait = data.casted_traits[0].clone();
predicates.push(quote! { #field_type: #trait_path });
(
quote! { #casted_trait::Target },
quote! { #casted_trait::deref(__0) },
)
} else {
(quote! { #field_type }, quote! { __0 })
};

if target.is_none() {
target = Some(target_);
}

match_arms.push(quote! {
#matcher => #var
});
}

let target = target.unwrap();

let generics = if predicates.is_empty() {
&input.generics
} else {
&add_extra_where_clauses(&input.generics, quote! { where #(#predicates),* })
};

let (imp_generics, type_generics, where_clause) = generics.split_for_impl();

Ok(quote! {
#[allow(deprecated)] // omit warnings on deprecated fields/variants
#[allow(unreachable_code)] // omit warnings for `!` and other unreachable types
#[automatically_derived]
impl #imp_generics #trait_path for #enum_name #type_generics #where_clause {
type Target = #target;

#[inline]
fn deref(&self) -> &Self::Target {
match self {
#(#match_arms),*
}
}
}
})
}

fn expand_struct(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
let state = State::with_field_ignore_and_forward(
input,
trait_name,
Expand Down
Loading