Skip to content

Commit e850d90

Browse files
committed
Merge remote-tracking branch 'JelteF/master' into hash-derive
2 parents 832dfba + 6d3eb15 commit e850d90

16 files changed

+102
-37
lines changed

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8-
## Unreleased
8+
## master
99

1010
### Added
11-
- Add `Hash` derive similar to `std`'s one, but considering generics correctly,
12-
and supporting custom hash functions per field or skipping fields.
13-
([#532](https://github.com/JelteF/derive_more/pull/532))
11+
- Add `Hash` derive similar to `std`'s one, but considering generics correctly,
12+
and supporting custom hash functions per field or skipping fields.
13+
([#532](https://github.com/JelteF/derive_more/pull/532))
1414

15+
### Fixed
16+
17+
- Mistakenly generated code for `owned` type in `TryInto`, `Unwrap` and `TryUnwrap`
18+
derives when it shouldn't be there (`ref`/`ref_mut` specified without `owned`).
19+
([#531](https://github.com/JelteF/derive_more/pull/531))
1520

1621
## 2.1.1 - 2025-12-22
1722

impl/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ proc-macro2 = "1.0"
2727
quote = "1.0"
2828
syn = "2.0.45"
2929
convert_case = { version = "0.10", optional = true }
30-
unicode-xid = { version = "0.2.2", optional = true }
30+
unicode-ident = { version = "1.0", optional = true }
3131

3232
[build-dependencies]
3333
rustc_version = "0.4"
@@ -53,10 +53,10 @@ add = ["syn/extra-traits", "syn/visit"]
5353
add_assign = ["syn/extra-traits", "syn/visit"]
5454
as_ref = ["syn/extra-traits", "syn/visit"]
5555
constructor = []
56-
debug = ["syn/extra-traits", "dep:unicode-xid"]
56+
debug = ["syn/extra-traits", "dep:unicode-ident"]
5757
deref = []
5858
deref_mut = []
59-
display = ["syn/extra-traits", "dep:unicode-xid", "dep:convert_case"]
59+
display = ["syn/extra-traits", "dep:unicode-ident", "dep:convert_case"]
6060
eq = ["syn/extra-traits", "syn/visit"]
6161
error = ["syn/extra-traits"]
6262
from = ["syn/extra-traits"]

impl/doc/try_into.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ let mut mixed_int3 = mixed_int1.clone();
3838

3939
assert_eq!(123u32, mixed_int1.try_into().unwrap());
4040

41-
let int_ref : &u32 = (&mixed_int2).try_into().unwrap();
41+
let int_ref: &u32 = (&mixed_int2).try_into().unwrap();
4242
assert_eq!(&123u32, int_ref);
4343

44-
let int_ref_mut : &mut u32 = (&mut mixed_int3).try_into().unwrap();
44+
let int_ref_mut: &mut u32 = (&mut mixed_int3).try_into().unwrap();
4545
assert_eq!(&mut 123u32, int_ref_mut);
4646

4747
assert_eq!("foo".to_string(), String::try_from(mixed_string.clone()).unwrap());

impl/doc/try_unwrap.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22

33
This works almost like `Unwrap`.
44
When an enum is decorated with `#[derive(TryUnwrap)]`, for each variant `foo` in the enum, with fields `(a, b, c, ...)` a public instance method `try_unwrap_foo(self) -> Result<(a, b, c, ...), TryUnwrapError<Self>>` is generated.
5+
56
If you don't want the `try_unwrap_foo` method generated for a variant, you can put the `#[try_unwrap(ignore)]` attribute on that variant.
6-
If you want to treat a reference, you can put the `#[try_unwrap(ref)]` attribute on the enum declaration or that variant, then `try_unwrap_foo_ref(self) -> Result<(&a, &b, &c, ...), TryUnwrapError<&Self>>` will be generated. You can also use mutable references by putting `#[unwrap(ref_mut)]`.
7+
8+
By using `#[try_unwrap(owned, ref, ref_mut)]` it's possible to generate methods implementation for reference types as well (like `try_unwrap_foo_ref(&self) -> Result<(&a, &b, &c, ...), TryUnwrapError<&Self>>`).
9+
You can pick any combination of `owned`, `ref` and `ref_mut`. If that's not provided the default is `#[try_unwrap(owned)]`.
10+
711
However, unlike `Unwrap`, it does not panic if the conversion fails. Also, values that fail to convert are not dropped but returned as an `Err`.
812

913
## Example usage
1014

1115
```rust
1216
# use derive_more::TryUnwrap;
13-
#
17+
#
1418
# #[derive(Debug, PartialEq)]
1519
#[derive(TryUnwrap)]
16-
#[try_unwrap(ref)]
20+
#[try_unwrap(owned, ref, ref_mut)]
1721
enum Maybe<T> {
1822
Nothing,
1923
Just(T),
@@ -37,6 +41,7 @@ fn main() {
3741
);
3842

3943
assert_eq!((&Maybe::Just(42)).try_unwrap_just_ref(), Ok(&42));
44+
assert_eq!((&mut Maybe::Just(42)).try_unwrap_just_mut(), Ok(&mut 42));
4045
}
4146
```
4247

@@ -64,6 +69,12 @@ impl<T> Maybe<T> {
6469
val @ _ => Err(todo!("TryUnwrapError::new(val, /* omitted */)")),
6570
}
6671
}
72+
pub fn try_unwrap_nothing_mut(&mut self) -> Result<(), TryUnwrapError<&mut Self>> {
73+
match self {
74+
Maybe::Nothing => Ok(()),
75+
val @ _ => Err(todo!("TryUnwrapError::new(val, /* omitted */)")),
76+
}
77+
}
6778
pub fn try_unwrap_just(self) -> Result<T, TryUnwrapError<Self>> {
6879
match self {
6980
Maybe::Just(field_0) => Ok(field_0),
@@ -76,5 +87,11 @@ impl<T> Maybe<T> {
7687
val @ _ => Err(todo!("TryUnwrapError::new(val, /* omitted */)")),
7788
}
7889
}
90+
pub fn try_unwrap_just_mut(&mut self) -> Result<&mut T, TryUnwrapError<&mut Self>> {
91+
match self {
92+
Maybe::Just(field_0) => Ok(field_0),
93+
val @ _ => Err(todo!("TryUnwrapError::new(val, /* omitted */)")),
94+
}
95+
}
7996
}
8097
```

impl/doc/unwrap.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# What `#[derive(Unwrap)]` generates
22

33
When an enum is decorated with `#[derive(Unwrap)]`, for each variant `foo` in the enum, with fields `(a, b, c, ...)` a public instance method `unwrap_foo(self) -> (a, b, c, ...)` is generated.
4+
45
If you don't want the `unwrap_foo` method generated for a variant, you can put the `#[unwrap(ignore)]` attribute on that variant.
5-
If you want to treat a reference, you can put the `#[unwrap(ref)]` attribute on the enum declaration or that variant, then `unwrap_foo_ref(self) -> (&a, &b, &c, ...)` will be generated. You can also use mutable references by putting `#[unwrap(ref_mut)]`.
6+
7+
By using `#[unwrap(owned, ref, ref_mut)]` it's possible to generate methods implementation for reference types as well (like `unwrap_foo_ref(&self) -> (&a, &b, &c, ...)`).
8+
You can pick any combination of `owned`, `ref` and `ref_mut`. If that's not provided the default is `#[unwrap(owned)]`.
69

710

811

@@ -11,10 +14,10 @@ If you want to treat a reference, you can put the `#[unwrap(ref)]` attribute on
1114

1215
```rust
1316
# use derive_more::Unwrap;
14-
#
17+
#
1518
# #[derive(Debug, PartialEq)]
1619
#[derive(Unwrap)]
17-
#[unwrap(ref)]
20+
#[unwrap(owned, ref, ref_mut)]
1821
enum Maybe<T> {
1922
Just(T),
2023
Nothing,
@@ -28,6 +31,7 @@ fn main() {
2831
// assert_eq!(Maybe::Just(2).unwrap_nothing(), /* panic */);
2932

3033
assert_eq!((&Maybe::Just(42)).unwrap_just_ref(), &42);
34+
assert_eq!((&mut Maybe::Just(42)).unwrap_just_mut(), &mut 42);
3135
}
3236
```
3337

@@ -54,6 +58,12 @@ impl<T> Maybe<T> {
5458
_ => panic!(),
5559
}
5660
}
61+
pub fn unwrap_nothing_mut(&mut self) -> () {
62+
match self {
63+
Maybe::Nothing => (),
64+
_ => panic!(),
65+
}
66+
}
5767
pub fn unwrap_just(self) -> T {
5868
match self {
5969
Maybe::Just(field_0) => field_0,
@@ -66,5 +76,11 @@ impl<T> Maybe<T> {
6676
_ => panic!(),
6777
}
6878
}
79+
pub fn unwrap_just_mut(&mut self) -> &mut T {
80+
match self {
81+
Maybe::Just(field_0) => field_0,
82+
_ => panic!(),
83+
}
84+
}
6985
}
7086
```

impl/src/fmt/parsing.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use std::iter;
66

7-
use unicode_xid::UnicodeXID as XID;
7+
use unicode_ident::{is_xid_continue, is_xid_start};
88

99
/// Output of the [`format_string`] parser.
1010
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -517,10 +517,10 @@ fn identifier(input: &str) -> Option<(LeftToParse<'_>, Identifier<'_>)> {
517517
map(
518518
alt(&mut [
519519
&mut map(
520-
check_char(XID::is_xid_start),
521-
take_while0(check_char(XID::is_xid_continue)),
520+
check_char(is_xid_start),
521+
take_while0(check_char(is_xid_continue)),
522522
),
523-
&mut and_then(char('_'), take_while1(check_char(XID::is_xid_continue))),
523+
&mut and_then(char('_'), take_while1(check_char(is_xid_continue))),
524524
]),
525525
|(i, _)| (i, &input[..(input.len() - i.len())]),
526526
)(input)

impl/src/utils.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -447,17 +447,17 @@ impl<'input> State<'input> {
447447
first_match.map_or(true, |info| !info.enabled.unwrap())
448448
};
449449

450+
// Default to owned true, unless the struct_meta_info or first attribute has ref type specified.
451+
// - struct attribute with ref type specified means default false
452+
// - not a single attribute means default true
453+
// - an attribute, but non of owned, ref or ref_mut means default true
454+
// - an attribute, and owned, ref or ref_mut means default false
455+
let default_owned = !struct_meta_info.is_ref_type_specified()
456+
&& first_match.map_or(true, |info| !info.is_ref_type_specified());
450457
let defaults = struct_meta_info.into_full(FullMetaInfo {
451458
enabled: default_enabled,
452459
forward: false,
453-
// Default to owned true, except when first attribute has one of owned,
454-
// ref or ref_mut
455-
// - not a single attribute means default true
456-
// - an attribute, but non of owned, ref or ref_mut means default true
457-
// - an attribute, and owned, ref or ref_mut means default false
458-
owned: first_match.map_or(true, |info| {
459-
info.owned.is_none() && info.ref_.is_none() || info.ref_mut.is_none()
460-
}),
460+
owned: default_owned,
461461
ref_: false,
462462
ref_mut: false,
463463
info: MetaInfo::default(),
@@ -1248,6 +1248,10 @@ impl MetaInfo {
12481248
info: self,
12491249
}
12501250
}
1251+
1252+
fn is_ref_type_specified(&self) -> bool {
1253+
self.owned.is_some() || self.ref_.is_some() || self.ref_mut.is_some()
1254+
}
12511255
}
12521256

12531257
impl FullMetaInfo {

tests/compile_fail/as_mut/unknown_field_attr_arg.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0412]: cannot find type `baz` in this scope
1+
error[E0425]: cannot find type `baz` in this scope
22
--> tests/compile_fail/as_mut/unknown_field_attr_arg.rs:3:14
33
|
44
3 | #[as_mut(baz)]

tests/compile_fail/as_mut/unknown_struct_attr_arg.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0412]: cannot find type `baz` in this scope
1+
error[E0425]: cannot find type `baz` in this scope
22
--> tests/compile_fail/as_mut/unknown_struct_attr_arg.rs:2:10
33
|
44
2 | #[as_mut(baz)]

tests/compile_fail/as_ref/unknown_field_attr_arg.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0412]: cannot find type `baz` in this scope
1+
error[E0425]: cannot find type `baz` in this scope
22
--> tests/compile_fail/as_ref/unknown_field_attr_arg.rs:3:14
33
|
44
3 | #[as_ref(baz)]

0 commit comments

Comments
 (0)