Skip to content

Commit ea3fbab

Browse files
authored
Fix merging types in multiple #[into] attributes (#309)
## Synopsis Types are not always merged correctly when multiple `#[into]` attributes are specified. ```rust #[derive(Clone, Copy, Debug, Into)] #[into(ref)] #[into(owned)] #[into((Wrapped<i32>, Transmuted<f32>))] #[into(ref_mut((Wrapped<i32>, Transmuted<f32>)))] struct Tuple( Wrapped<i32>, Wrapped<f32>, ); ``` expands as ```rust #[automatically_derived] impl ::core::convert::From<Tuple> for (Wrapped<i32>, Transmuted<f32>) { #[inline] fn from(value: Tuple) -> Self { (<Wrapped<i32> as ::core::convert::From<_>>::from(value.0), <Transmuted<f32> as ::core::convert::From<_>>::from(value.1)) } } #[automatically_derived] impl<'__derive_more_into> ::core::convert::From<&'__derive_more_into Tuple> for (&'__derive_more_into Wrapped<i32>, &'__derive_more_into Wrapped<f32>) { #[inline] fn from(value: &'__derive_more_into Tuple) -> Self { (<&Wrapped<i32> as ::core::convert::From<_>>::from(&value.0), <&Wrapped<f32> as ::core::convert::From<_>>::from(&value.1)) } } #[automatically_derived] impl<'__derive_more_into> ::core::convert::From<&'__derive_more_into mut Tuple> for (&'__derive_more_into mut Wrapped<i32>, &'__derive_more_into mut Transmuted<f32>) { #[inline] fn from(value: &'__derive_more_into mut Tuple) -> Self { (<&mut Wrapped<i32> as ::core::convert::From<_>>::from(&mut value.0), <&mut Transmuted<f32> as ::core::convert::From<_>>::from(&mut value.1)) } } ``` So, the `#[into((Wrapped<i32>, Transmuted<f32>))]` is totally missed in the expanded macro. ## Solution Fix merging multiple `#[into(<types>)]` attributes so all the specified conversions are used
1 parent 1196b2d commit ea3fbab

File tree

2 files changed

+155
-75
lines changed

2 files changed

+155
-75
lines changed

impl/src/into.rs

Lines changed: 76 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -150,49 +150,48 @@ impl<'a> Expansion<'a> {
150150
(&convs.ref_mut, true, true),
151151
]
152152
.into_iter()
153-
.filter_map(|(out_tys, ref_, mut_)| {
154-
out_tys.as_ref().map(|out_tys| {
155-
let lf = ref_.then(|| {
156-
syn::Lifetime::new("'__derive_more_into", Span::call_site())
157-
});
158-
let r = ref_.then(token::And::default);
159-
let m = mut_.then(token::Mut::default);
160-
161-
let gens = if let Some(lf) = lf.clone() {
162-
let mut gens = input_generics.clone();
163-
gens.params.push(syn::LifetimeParam::new(lf).into());
164-
Cow::Owned(gens)
165-
} else {
166-
Cow::Borrowed(input_generics)
167-
};
168-
let (impl_gens, _, where_clause) = gens.split_for_impl();
169-
let (_, ty_gens, _) = input_generics.split_for_impl();
170-
171-
if out_tys.is_empty() {
172-
Either::Left(iter::once(&fields_tuple))
173-
} else {
174-
Either::Right(out_tys.iter())
175-
}.map(|out_ty| {
176-
let tys: Vec<_> = fields_tys.validate_type(out_ty)?.collect();
177-
178-
Ok(quote! {
179-
#[automatically_derived]
180-
impl #impl_gens ::core::convert::From<#r #lf #m #input_ident #ty_gens>
181-
for ( #( #r #lf #m #tys ),* ) #where_clause
182-
{
183-
#[inline]
184-
fn from(value: #r #lf #m #input_ident #ty_gens) -> Self {
185-
(#(
186-
<#r #m #tys as ::core::convert::From<_>>::from(
187-
#r #m value. #fields_idents
188-
)
189-
),*)
190-
}
153+
.filter(|(conv, _, _)| conv.consider_fields_ty || !conv.tys.is_empty())
154+
.map(|(conv, ref_, mut_)| {
155+
let lf = ref_.then(|| syn::Lifetime::new("'__derive_more_into", Span::call_site()));
156+
let r = ref_.then(token::And::default);
157+
let m = mut_.then(token::Mut::default);
158+
159+
let gens = if let Some(lf) = lf.clone() {
160+
let mut gens = input_generics.clone();
161+
gens.params.push(syn::LifetimeParam::new(lf).into());
162+
Cow::Owned(gens)
163+
} else {
164+
Cow::Borrowed(input_generics)
165+
};
166+
let (impl_gens, _, where_clause) = gens.split_for_impl();
167+
let (_, ty_gens, _) = input_generics.split_for_impl();
168+
169+
if conv.consider_fields_ty {
170+
Either::Left(iter::once(&fields_tuple))
171+
} else {
172+
Either::Right(iter::empty())
173+
}
174+
.chain(&conv.tys)
175+
.map(|out_ty| {
176+
let tys: Vec<_> = fields_tys.validate_type(out_ty)?.collect();
177+
178+
Ok(quote! {
179+
#[automatically_derived]
180+
impl #impl_gens ::core::convert::From<#r #lf #m #input_ident #ty_gens>
181+
for ( #( #r #lf #m #tys ),* ) #where_clause
182+
{
183+
#[inline]
184+
fn from(value: #r #lf #m #input_ident #ty_gens) -> Self {
185+
(#(
186+
<#r #m #tys as ::core::convert::From<_>>::from(
187+
#r #m value. #fields_idents
188+
)
189+
),*)
191190
}
192-
})
191+
}
193192
})
194-
.collect::<syn::Result<TokenStream>>()
195193
})
194+
.collect::<syn::Result<TokenStream>>()
196195
})
197196
.collect()
198197
}
@@ -289,64 +288,75 @@ impl attr::ParseMultiple for FieldAttribute {
289288
}
290289
}
291290

291+
/// [`Into`] conversions specified by a [`ConversionsAttribute`].
292+
#[derive(Clone, Debug, Default)]
293+
struct Conversions {
294+
/// Indicator whether these [`Conversions`] should contain a conversion into fields type.
295+
consider_fields_ty: bool,
296+
297+
/// [`syn::Type`]s explicitly specified in a [`ConversionsAttribute`].
298+
tys: Punctuated<syn::Type, token::Comma>,
299+
}
300+
292301
/// Representation of an [`Into`] derive macro attribute describing specified [`Into`] conversions.
293302
///
294303
/// ```rust,ignore
295304
/// #[into(<types>)]
296305
/// #[into(owned(<types>), ref(<types>), ref_mut(<types>))]
297306
/// ```
298-
///
299-
/// For each field:
300-
/// - [`None`] represents no conversions.
301-
/// - Empty [`Punctuated`] represents a conversion into the field type.
302307
#[derive(Clone, Debug)]
303308
struct ConversionsAttribute {
304309
/// [`Type`]s wrapped into `owned(...)` or simply `#[into(...)]`.
305-
owned: Option<Punctuated<syn::Type, token::Comma>>,
310+
owned: Conversions,
306311

307312
/// [`Type`]s wrapped into `ref(...)`.
308-
r#ref: Option<Punctuated<syn::Type, token::Comma>>,
313+
r#ref: Conversions,
309314

310315
/// [`Type`]s wrapped into `ref_mut(...)`.
311-
ref_mut: Option<Punctuated<syn::Type, token::Comma>>,
316+
ref_mut: Conversions,
312317
}
313318

314319
impl Default for ConversionsAttribute {
315320
fn default() -> Self {
316321
Self {
317-
owned: Some(Punctuated::new()),
318-
r#ref: None,
319-
ref_mut: None,
322+
owned: Conversions {
323+
consider_fields_ty: true,
324+
tys: Punctuated::new(),
325+
},
326+
r#ref: Conversions::default(),
327+
ref_mut: Conversions::default(),
320328
}
321329
}
322330
}
323331

324332
impl Parse for ConversionsAttribute {
325333
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
326334
let mut out = Self {
327-
owned: None,
328-
r#ref: None,
329-
ref_mut: None,
335+
owned: Conversions::default(),
336+
r#ref: Conversions::default(),
337+
ref_mut: Conversions::default(),
330338
};
331339

332-
let parse_inner = |ahead, types: &mut Option<_>| {
340+
let parse_inner = |ahead, convs: &mut Conversions| {
333341
input.advance_to(&ahead);
334342

335-
let types = types.get_or_insert_with(Punctuated::new);
336343
if input.peek(token::Paren) {
337344
let inner;
338345
syn::parenthesized!(inner in input);
339346

340-
types.extend(
347+
convs.tys.extend(
341348
inner
342349
.parse_terminated(syn::Type::parse, token::Comma)?
343350
.into_pairs(),
344351
);
352+
} else {
353+
convs.consider_fields_ty = true;
345354
}
355+
346356
if input.peek(token::Comma) {
347357
let comma = input.parse::<token::Comma>()?;
348-
if !types.empty_or_trailing() {
349-
types.push_punct(comma);
358+
if !convs.tys.empty_or_trailing() {
359+
convs.tys.push_punct(comma);
350360
}
351361
}
352362

@@ -379,12 +389,10 @@ impl Parse for ConversionsAttribute {
379389
_ => {
380390
let ty = input.parse::<syn::Type>()?;
381391
let _ = top_level_type.get_or_insert_with(|| ty.clone());
382-
out.owned.get_or_insert_with(Punctuated::new).push_value(ty);
392+
out.owned.tys.push_value(ty);
383393

384394
if input.peek(token::Comma) {
385-
out.owned
386-
.get_or_insert_with(Punctuated::new)
387-
.push_punct(input.parse::<token::Comma>()?)
395+
out.owned.tys.push_punct(input.parse::<token::Comma>()?)
388396
}
389397
}
390398
}
@@ -411,14 +419,6 @@ impl attr::ParseMultiple for ConversionsAttribute {
411419
new: Spanning<Self>,
412420
_: &syn::Ident,
413421
) -> syn::Result<Spanning<Self>> {
414-
let merge = |out: &mut Option<_>, tys| match (out.as_mut(), tys) {
415-
(None, Some(tys)) => {
416-
*out = Some::<Punctuated<_, _>>(tys);
417-
}
418-
(Some(out), Some(tys)) => out.extend(tys),
419-
(Some(_), None) | (None, None) => {}
420-
};
421-
422422
let Spanning {
423423
span: prev_span,
424424
item: mut prev,
@@ -428,9 +428,12 @@ impl attr::ParseMultiple for ConversionsAttribute {
428428
item: new,
429429
} = new;
430430

431-
merge(&mut prev.owned, new.owned);
432-
merge(&mut prev.r#ref, new.r#ref);
433-
merge(&mut prev.ref_mut, new.ref_mut);
431+
prev.owned.tys.extend(new.owned.tys);
432+
prev.owned.consider_fields_ty |= new.owned.consider_fields_ty;
433+
prev.r#ref.tys.extend(new.r#ref.tys);
434+
prev.r#ref.consider_fields_ty |= new.r#ref.consider_fields_ty;
435+
prev.ref_mut.tys.extend(new.ref_mut.tys);
436+
prev.ref_mut.consider_fields_ty |= new.ref_mut.consider_fields_ty;
434437

435438
Ok(Spanning::new(
436439
prev,

tests/into.rs

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ unsafe fn transmute<From, To>(from: From) -> To {
2828
to
2929
}
3030

31-
#[derive(Debug, PartialEq)]
31+
#[derive(Clone, Copy, Debug, PartialEq)]
3232
#[repr(transparent)]
3333
struct Wrapped<T>(T);
3434

35-
#[derive(Debug, PartialEq)]
35+
#[derive(Clone, Copy, Debug, PartialEq)]
3636
#[repr(transparent)]
3737
struct Transmuted<T>(T);
3838

@@ -1433,5 +1433,82 @@ mod with_fields {
14331433
assert_eq!(&Wrapped(2.0), <&Wrapped<f32>>::from(&foo));
14341434
assert_eq!(Wrapped(1), foo.into());
14351435
}
1436+
1437+
mod separate {
1438+
use super::*;
1439+
1440+
#[derive(Clone, Copy, Debug, Into)]
1441+
#[into(ref)]
1442+
#[into(owned)]
1443+
#[into((Wrapped<i32>, Transmuted<f32>))]
1444+
#[into(ref_mut((Wrapped<i32>, Transmuted<f32>)))]
1445+
struct Tuple(
1446+
#[into(ref)]
1447+
#[into(ref(Transmuted<i32>))]
1448+
#[into]
1449+
Wrapped<i32>,
1450+
#[into(ref_mut)]
1451+
#[into(ref_mut(Transmuted<f32>))]
1452+
#[into(owned)]
1453+
Wrapped<f32>,
1454+
);
1455+
1456+
#[test]
1457+
fn tuple() {
1458+
let mut foo = Tuple(Wrapped(1), Wrapped(2.0));
1459+
1460+
assert_eq!((&Wrapped(1), &Wrapped(2.0)), (&foo).into());
1461+
assert_eq!((Wrapped(1), Wrapped(2.0)), foo.into());
1462+
assert_eq!((Wrapped(1), Transmuted(2.0)), foo.into());
1463+
assert_eq!((&mut Wrapped(1), &mut Transmuted(2.0)), (&mut foo).into());
1464+
assert_eq!(&Wrapped(1), <&Wrapped<i32>>::from(&foo));
1465+
assert_eq!(&Transmuted(1), <&Transmuted<i32>>::from(&foo));
1466+
assert_eq!(Wrapped(1), <Wrapped<i32>>::from(foo));
1467+
assert_eq!(&mut Wrapped(2.0), <&mut Wrapped<f32>>::from(&mut foo));
1468+
assert_eq!(
1469+
&mut Transmuted(2.0),
1470+
<&mut Transmuted<f32>>::from(&mut foo),
1471+
);
1472+
assert_eq!(Wrapped(2.0), <Wrapped<f32>>::from(foo));
1473+
}
1474+
1475+
#[derive(Clone, Copy, Debug, Into)]
1476+
#[into(ref)]
1477+
#[into(owned)]
1478+
#[into((Wrapped<i32>, Transmuted<f32>))]
1479+
#[into(ref_mut((Wrapped<i32>, Transmuted<f32>)))]
1480+
struct Struct {
1481+
#[into(ref)]
1482+
#[into(ref (Transmuted < i32 >))]
1483+
#[into]
1484+
a: Wrapped<i32>,
1485+
#[into(ref_mut)]
1486+
#[into(ref_mut(Transmuted < f32 >))]
1487+
#[into(owned)]
1488+
b: Wrapped<f32>,
1489+
}
1490+
1491+
#[test]
1492+
fn named() {
1493+
let mut foo = Struct {
1494+
a: Wrapped(1),
1495+
b: Wrapped(2.0),
1496+
};
1497+
1498+
assert_eq!((&Wrapped(1), &Wrapped(2.0)), (&foo).into());
1499+
assert_eq!((Wrapped(1), Wrapped(2.0)), foo.into());
1500+
assert_eq!((Wrapped(1), Transmuted(2.0)), foo.into());
1501+
assert_eq!((&mut Wrapped(1), &mut Transmuted(2.0)), (&mut foo).into());
1502+
assert_eq!(&Wrapped(1), <&Wrapped<i32>>::from(&foo));
1503+
assert_eq!(&Transmuted(1), <&Transmuted<i32>>::from(&foo));
1504+
assert_eq!(Wrapped(1), <Wrapped<i32>>::from(foo));
1505+
assert_eq!(&mut Wrapped(2.0), <&mut Wrapped<f32>>::from(&mut foo));
1506+
assert_eq!(
1507+
&mut Transmuted(2.0),
1508+
<&mut Transmuted<f32>>::from(&mut foo),
1509+
);
1510+
assert_eq!(Wrapped(2.0), <Wrapped<f32>>::from(foo));
1511+
}
1512+
}
14361513
}
14371514
}

0 commit comments

Comments
 (0)