Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 92ded7a

Browse files
committed
update DefaultNoBound derive macro
1 parent a7ba55d commit 92ded7a

File tree

12 files changed

+215
-62
lines changed

12 files changed

+215
-62
lines changed

frame/support/procedural/src/default_no_bound.rs

Lines changed: 104 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,82 +15,131 @@
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
1717

18-
use syn::spanned::Spanned;
18+
use proc_macro2::Span;
19+
use quote::{quote, quote_spanned};
20+
use syn::{spanned::Spanned, Data, DeriveInput, Fields, Variant};
1921

20-
/// Derive Clone but do not bound any generic.
22+
/// Derive Default but do not bound any generic.
2123
pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
22-
let input: syn::DeriveInput = match syn::parse(input) {
23-
Ok(input) => input,
24-
Err(e) => return e.to_compile_error().into(),
25-
};
24+
let input = syn::parse_macro_input!(input as DeriveInput);
2625

2726
let name = &input.ident;
27+
2828
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
2929

3030
let impl_ = match input.data {
31-
syn::Data::Struct(struct_) => match struct_.fields {
32-
syn::Fields::Named(named) => {
33-
let fields = named.named.iter().map(|i| &i.ident).map(|i| {
34-
quote::quote_spanned!(i.span() =>
35-
#i: core::default::Default::default()
36-
)
31+
Data::Struct(struct_) => match struct_.fields {
32+
Fields::Named(named) => {
33+
let fields = named.named.iter().map(|field| &field.ident).map(|ident| {
34+
quote_spanned! {ident.span() =>
35+
#ident: core::default::Default::default()
36+
}
3737
});
3838

39-
quote::quote!( Self { #( #fields, )* } )
39+
quote!(Self { #( #fields, )* })
4040
},
41-
syn::Fields::Unnamed(unnamed) => {
42-
let fields =
43-
unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| {
44-
quote::quote_spanned!(i.span() =>
45-
core::default::Default::default()
46-
)
47-
});
41+
Fields::Unnamed(unnamed) => {
42+
let fields = unnamed.unnamed.iter().map(|field| {
43+
quote_spanned! {field.span()=>
44+
core::default::Default::default()
45+
}
46+
});
4847

49-
quote::quote!( Self ( #( #fields, )* ) )
48+
quote!(Self( #( #fields, )* ))
5049
},
51-
syn::Fields::Unit => {
52-
quote::quote!(Self)
50+
Fields::Unit => {
51+
quote!(Self)
5352
},
5453
},
55-
syn::Data::Enum(enum_) =>
56-
if let Some(first_variant) = enum_.variants.first() {
57-
let variant_ident = &first_variant.ident;
58-
match &first_variant.fields {
59-
syn::Fields::Named(named) => {
60-
let fields = named.named.iter().map(|i| &i.ident).map(|i| {
61-
quote::quote_spanned!(i.span() =>
62-
#i: core::default::Default::default()
63-
)
64-
});
54+
Data::Enum(enum_) => {
55+
if enum_.variants.is_empty() {
56+
quote!(Self)
57+
} else {
58+
let tagged_variants = enum_
59+
.variants
60+
.into_iter()
61+
.flat_map(|variant| {
62+
variant
63+
.attrs
64+
.iter()
65+
.filter_map::<(), _>(|attr| attr.path.is_ident("default").then_some(()))
66+
.collect::<Vec<_>>()
67+
.into_iter()
68+
.map(move |_| variant.clone())
69+
})
70+
.collect::<Vec<_>>();
6571

66-
quote::quote!( #name :: #ty_generics :: #variant_ident { #( #fields, )* } )
72+
match &*tagged_variants {
73+
[] => {
74+
return syn::Error::new(
75+
name.clone().span(),
76+
// writing this as a regular string breaks rustfmt for some reason
77+
r#"no default declared, make a variant default by placing `#[default]` above it"#,
78+
)
79+
.into_compile_error()
80+
.into();
6781
},
68-
syn::Fields::Unnamed(unnamed) => {
69-
let fields = unnamed
70-
.unnamed
71-
.iter()
72-
.enumerate()
73-
.map(|(i, _)| syn::Index::from(i))
74-
.map(|i| {
75-
quote::quote_spanned!(i.span() =>
76-
core::default::Default::default()
77-
)
78-
});
79-
80-
quote::quote!( #name :: #ty_generics :: #variant_ident ( #( #fields, )* ) )
82+
[default_variant] => {
83+
let variant_ident = &default_variant.ident;
84+
85+
let fully_qualified_variant_path = quote!(Self::#variant_ident);
86+
87+
match &default_variant.fields {
88+
Fields::Named(named) => {
89+
let fields =
90+
named.named.iter().map(|field| &field.ident).map(|ident| {
91+
quote_spanned! {ident.span()=>
92+
#ident: core::default::Default::default()
93+
}
94+
});
95+
96+
quote!(#fully_qualified_variant_path { #( #fields, )* })
97+
},
98+
Fields::Unnamed(unnamed) => {
99+
let fields = unnamed.unnamed.iter().map(|field| {
100+
quote_spanned! {field.span()=>
101+
core::default::Default::default()
102+
}
103+
});
104+
105+
quote!(#fully_qualified_variant_path( #( #fields, )* ))
106+
},
107+
Fields::Unit => fully_qualified_variant_path,
108+
}
109+
},
110+
[first, additional @ ..] => {
111+
let attr_spanned_err = |v: &Variant, msg| {
112+
syn::Error::new_spanned(
113+
v.attrs.iter().find(|a| a.path.is_ident("default")).unwrap(),
114+
msg,
115+
)
116+
};
117+
118+
let mut err =
119+
syn::Error::new(Span::call_site(), "multiple `#[default]` attributes");
120+
121+
err.combine(attr_spanned_err(first, "`#[default]` used here"));
122+
123+
err.extend(additional.into_iter().map(|variant| {
124+
attr_spanned_err(variant, "`#[default]` used again here")
125+
}));
126+
127+
err.into_compile_error().into()
81128
},
82-
syn::Fields::Unit => quote::quote!( #name :: #ty_generics :: #variant_ident ),
83129
}
84-
} else {
85-
quote::quote!(Self)
86-
},
87-
syn::Data::Union(_) => {
88-
let msg = "Union type not supported by `derive(CloneNoBound)`";
89-
return syn::Error::new(input.span(), msg).to_compile_error().into()
130+
}
131+
},
132+
Data::Union(union_) => {
133+
return syn::Error::new_spanned(
134+
union_.union_token,
135+
"Union type not supported by `derive(DefaultNoBound)`",
136+
)
137+
.to_compile_error()
138+
.into();
90139
},
91140
};
92141

93-
quote::quote!(
142+
quote!(
94143
const _: () = {
95144
impl #impl_generics core::default::Default for #name #ty_generics #where_clause {
96145
fn default() -> Self {

frame/support/procedural/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ pub fn derive_eq_no_bound(input: TokenStream) -> TokenStream {
582582
}
583583

584584
/// derive `Default` but do no bound any generic. Docs are at `frame_support::DefaultNoBound`.
585-
#[proc_macro_derive(DefaultNoBound)]
585+
#[proc_macro_derive(DefaultNoBound, attributes(default))]
586586
pub fn derive_default_no_bound(input: TokenStream) -> TokenStream {
587587
default_no_bound::derive_default_no_bound(input)
588588
}

frame/support/src/lib.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -621,14 +621,23 @@ pub use frame_support_procedural::DebugNoBound;
621621
/// # use frame_support::DefaultNoBound;
622622
/// # use core::default::Default;
623623
/// trait Config {
624-
/// type C: Default;
624+
/// type C: Default;
625625
/// }
626626
///
627627
/// // Foo implements [`Default`] because `C` bounds [`Default`].
628628
/// // Otherwise compilation will fail with an output telling `c` doesn't implement [`Default`].
629629
/// #[derive(DefaultNoBound)]
630630
/// struct Foo<T: Config> {
631-
/// c: T::C,
631+
/// c: T::C,
632+
/// }
633+
///
634+
/// // Also works with enums, by specifying the default with #[default]:
635+
/// #[derive(DefaultNoBound)]
636+
/// enum Bar<T: Config> {
637+
/// // Bar will implement Default as long as all of the types within Baz also implement default.
638+
/// #[default]
639+
/// Baz(T::C)
640+
/// Quxx,
632641
/// }
633642
/// ```
634643
pub use frame_support_procedural::DefaultNoBound;
@@ -685,7 +694,7 @@ pub use frame_support_procedural::crate_to_crate_version;
685694
#[macro_export]
686695
macro_rules! fail {
687696
( $y:expr ) => {{
688-
return Err($y.into())
697+
return Err($y.into());
689698
}};
690699
}
691700

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn main() {}

frame/support/test/tests/derive_no_bound.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,46 @@ fn test_struct_unnamed() {
110110
assert!(b != a_1);
111111
}
112112

113+
#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)]
114+
struct StructNoGenerics {
115+
field1: u32,
116+
field2: (),
117+
}
118+
119+
#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)]
120+
enum EnumNoGenerics {
121+
#[default]
122+
VariantUnnamed(u32, u64),
123+
VariantNamed {
124+
a: u32,
125+
b: u64,
126+
},
127+
VariantUnit,
128+
}
129+
113130
#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)]
114131
enum Enum<T: Config, U, V> {
132+
#[default]
115133
VariantUnnamed(u32, u64, T::C, core::marker::PhantomData<(U, V)>),
116-
VariantNamed { a: u32, b: u64, c: T::C, phantom: core::marker::PhantomData<(U, V)> },
134+
VariantNamed {
135+
a: u32,
136+
b: u64,
137+
c: T::C,
138+
phantom: core::marker::PhantomData<(U, V)>,
139+
},
117140
VariantUnit,
118141
VariantUnit2,
119142
}
120143

121144
// enum that will have a named default.
122145
#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)]
123146
enum Enum2<T: Config> {
124-
VariantNamed { a: u32, b: u64, c: T::C },
147+
#[default]
148+
VariantNamed {
149+
a: u32,
150+
b: u64,
151+
c: T::C,
152+
},
125153
VariantUnnamed(u32, u64, T::C),
126154
VariantUnit,
127155
VariantUnit2,
@@ -130,8 +158,13 @@ enum Enum2<T: Config> {
130158
// enum that will have a unit default.
131159
#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)]
132160
enum Enum3<T: Config> {
161+
#[default]
133162
VariantUnit,
134-
VariantNamed { a: u32, b: u64, c: T::C },
163+
VariantNamed {
164+
a: u32,
165+
b: u64,
166+
c: T::C,
167+
},
135168
VariantUnnamed(u32, u64, T::C),
136169
VariantUnit2,
137170
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait Config {
2+
type C;
3+
}
4+
5+
#[derive(frame_support::DefaultNoBound)]
6+
enum Foo<T: Config> {
7+
Bar(T::C),
8+
Baz,
9+
}
10+
11+
fn main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: no default declared, make a variant default by placing `#[default]` above it
2+
--> tests/derive_no_bound_ui/default_no_attribute.rs:6:6
3+
|
4+
6 | enum Foo<T: Config> {
5+
| ^^^
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait Config {
2+
type C;
3+
}
4+
5+
#[derive(frame_support::DefaultNoBound)]
6+
enum Foo<T: Config> {
7+
#[default]
8+
Bar(T::C),
9+
#[default]
10+
Baz,
11+
}
12+
13+
fn main() {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: multiple `#[default]` attributes
2+
--> tests/derive_no_bound_ui/default_too_many_attributes.rs:5:10
3+
|
4+
5 | #[derive(frame_support::DefaultNoBound)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this error originates in the derive macro `frame_support::DefaultNoBound` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
error: `#[default]` used here
10+
--> tests/derive_no_bound_ui/default_too_many_attributes.rs:7:2
11+
|
12+
7 | #[default]
13+
| ^^^^^^^^^^
14+
15+
error: `#[default]` used again here
16+
--> tests/derive_no_bound_ui/default_too_many_attributes.rs:9:2
17+
|
18+
9 | #[default]
19+
| ^^^^^^^^^^
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[derive(frame_support::DefaultNoBound)]
2+
union Foo {
3+
field1: u32,
4+
field2: (),
5+
}
6+
7+
fn main() {}

0 commit comments

Comments
 (0)