Skip to content

Commit be5ae02

Browse files
committed
Add #[from_reflect(auto_derive = false)] attribute
1 parent a3a2173 commit be5ae02

File tree

5 files changed

+127
-16
lines changed

5 files changed

+127
-16
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use proc_macro2::Ident;
1010
use quote::quote;
1111
use syn::parse::{Parse, ParseStream};
1212
use syn::punctuated::Punctuated;
13+
use syn::spanned::Spanned;
1314
use syn::token::Comma;
14-
use syn::{Meta, NestedMeta, Path};
15+
use syn::{Lit, Meta, NestedMeta, Path};
1516

1617
// The "special" trait idents that are used internally for reflection.
1718
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
@@ -23,6 +24,9 @@ const HASH_ATTR: &str = "Hash";
2324
// but useful to know exist nonetheless
2425
pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault";
2526

27+
// Attributes for `FromReflect` derive
28+
const FROM_REFLECT_AUTO_DERIVE_ATTR: &str = "auto_derive";
29+
2630
/// A marker for trait implementations registered via the `Reflect` derive macro.
2731
#[derive(Clone, Default)]
2832
pub(crate) enum TraitImpl {
@@ -245,3 +249,59 @@ impl Parse for ReflectTraits {
245249
Ok(ReflectTraits::from_nested_metas(&result))
246250
}
247251
}
252+
253+
/// A collection of attributes used for deriving `FromReflect`.
254+
#[derive(Clone)]
255+
pub(crate) struct FromReflectAttrs {
256+
auto_derive: bool,
257+
}
258+
259+
impl Default for FromReflectAttrs {
260+
fn default() -> Self {
261+
Self { auto_derive: true }
262+
}
263+
}
264+
265+
impl FromReflectAttrs {
266+
pub fn from_nested_metas(nested_metas: &Punctuated<NestedMeta, Comma>) -> syn::Result<Self> {
267+
let mut attrs = FromReflectAttrs::default();
268+
for nested_meta in nested_metas.iter() {
269+
if let NestedMeta::Meta(Meta::NameValue(pair)) = nested_meta {
270+
let ident = pair.path.get_ident().ok_or_else(|| {
271+
syn::Error::new(
272+
pair.path.span(),
273+
"expected ident key in `from_reflect` attribute",
274+
)
275+
})?;
276+
277+
let key = ident.to_string();
278+
match key.as_str() {
279+
FROM_REFLECT_AUTO_DERIVE_ATTR => match &pair.lit {
280+
Lit::Bool(auto_derive) => attrs.auto_derive = auto_derive.value,
281+
lit => return Err(syn::Error::new(lit.span(), "expected boolean value")),
282+
},
283+
unknown => {
284+
return Err(syn::Error::new(
285+
ident.span(),
286+
format!("unknown key in `from_reflect` attribute: `{}`", unknown),
287+
))
288+
}
289+
}
290+
}
291+
}
292+
293+
Ok(attrs)
294+
}
295+
296+
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
297+
pub fn should_auto_derive(&self) -> bool {
298+
self.auto_derive
299+
}
300+
}
301+
302+
impl Parse for FromReflectAttrs {
303+
fn parse(input: ParseStream) -> syn::Result<Self> {
304+
let result = Punctuated::<NestedMeta, Comma>::parse_terminated(input)?;
305+
FromReflectAttrs::from_nested_metas(&result)
306+
}
307+
}

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use crate::container_attributes::ReflectTraits;
1+
use crate::container_attributes::{FromReflectAttrs, ReflectTraits};
22
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
33
use crate::utility::members_to_serialization_denylist;
44
use bit_set::BitSet;
55
use quote::quote;
66

7-
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
7+
use crate::{
8+
utility, FROM_REFLECT_ATTRIBUTE_NAME, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME,
9+
};
810
use syn::punctuated::Punctuated;
911
use syn::spanned::Spanned;
1012
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant};
@@ -33,6 +35,8 @@ pub(crate) enum ReflectDerive<'a> {
3335
pub(crate) struct ReflectMeta<'a> {
3436
/// The registered traits for this type.
3537
traits: ReflectTraits,
38+
/// The `FromReflect` attributes for this type.
39+
from_reflect: FromReflectAttrs,
3640
/// The name of this type.
3741
type_name: &'a Ident,
3842
/// The generics defined on this type.
@@ -111,6 +115,7 @@ pub(crate) enum EnumVariantFields<'a> {
111115
impl<'a> ReflectDerive<'a> {
112116
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
113117
let mut traits = ReflectTraits::default();
118+
let mut from_reflect = FromReflectAttrs::default();
114119
// Should indicate whether `#[reflect_value]` was used
115120
let mut force_reflect_value = false;
116121

@@ -123,6 +128,9 @@ impl<'a> ReflectDerive<'a> {
123128
force_reflect_value = true;
124129
traits = ReflectTraits::from_nested_metas(&meta_list.nested);
125130
}
131+
Meta::List(meta_list) if meta_list.path.is_ident(FROM_REFLECT_ATTRIBUTE_NAME) => {
132+
from_reflect = FromReflectAttrs::from_nested_metas(&meta_list.nested)?;
133+
}
126134
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
127135
force_reflect_value = true;
128136
}
@@ -134,13 +142,14 @@ impl<'a> ReflectDerive<'a> {
134142
&input.ident,
135143
&input.generics,
136144
traits,
145+
from_reflect,
137146
)));
138147
}
139148

140149
return match &input.data {
141150
Data::Struct(data) => {
142151
let fields = Self::collect_struct_fields(&data.fields)?;
143-
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
152+
let meta = ReflectMeta::new(&input.ident, &input.generics, traits, from_reflect);
144153
let reflect_struct = ReflectStruct {
145154
meta,
146155
serialization_denylist: members_to_serialization_denylist(
@@ -157,7 +166,7 @@ impl<'a> ReflectDerive<'a> {
157166
}
158167
Data::Enum(data) => {
159168
let variants = Self::collect_enum_variants(&data.variants)?;
160-
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
169+
let meta = ReflectMeta::new(&input.ident, &input.generics, traits, from_reflect);
161170

162171
let reflect_enum = ReflectEnum { meta, variants };
163172
Ok(Self::Enum(reflect_enum))
@@ -220,9 +229,15 @@ impl<'a> ReflectDerive<'a> {
220229
}
221230

222231
impl<'a> ReflectMeta<'a> {
223-
pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self {
232+
pub fn new(
233+
type_name: &'a Ident,
234+
generics: &'a Generics,
235+
traits: ReflectTraits,
236+
from_reflect: FromReflectAttrs,
237+
) -> Self {
224238
Self {
225239
traits,
240+
from_reflect,
226241
type_name,
227242
generics,
228243
bevy_reflect_path: utility::get_bevy_reflect_path(),
@@ -234,6 +249,12 @@ impl<'a> ReflectMeta<'a> {
234249
&self.traits
235250
}
236251

252+
/// The `FromReflect` attributes on this type.
253+
#[allow(clippy::wrong_self_convention)]
254+
pub fn from_reflect(&self) -> &FromReflectAttrs {
255+
&self.from_reflect
256+
}
257+
237258
/// The name of this struct.
238259
pub fn type_name(&self) -> &'a Ident {
239260
self.type_name

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ use syn::{parse_macro_input, DeriveInput};
3535

3636
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
3737
pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
38+
pub(crate) static FROM_REFLECT_ATTRIBUTE_NAME: &str = "from_reflect";
3839

39-
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))]
40+
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, from_reflect, module))]
4041
pub fn derive_reflect(input: TokenStream) -> TokenStream {
4142
let ast = parse_macro_input!(input as DeriveInput);
4243

@@ -48,17 +49,36 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream {
4849
let (reflect_impls, from_reflect_impl) = match derive_data {
4950
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
5051
impls::impl_struct(&struct_data),
51-
from_reflect::impl_struct(&struct_data),
52+
if struct_data.meta().from_reflect().should_auto_derive() {
53+
Some(from_reflect::impl_struct(&struct_data))
54+
} else {
55+
None
56+
},
5257
),
5358
ReflectDerive::TupleStruct(struct_data) => (
5459
impls::impl_tuple_struct(&struct_data),
55-
from_reflect::impl_tuple_struct(&struct_data),
60+
if struct_data.meta().from_reflect().should_auto_derive() {
61+
Some(from_reflect::impl_tuple_struct(&struct_data))
62+
} else {
63+
None
64+
},
5665
),
5766
ReflectDerive::Enum(enum_data) => (
5867
impls::impl_enum(&enum_data),
59-
from_reflect::impl_enum(&enum_data),
68+
if enum_data.meta().from_reflect().should_auto_derive() {
69+
Some(from_reflect::impl_enum(&enum_data))
70+
} else {
71+
None
72+
},
73+
),
74+
ReflectDerive::Value(meta) => (
75+
impls::impl_value(&meta),
76+
if meta.from_reflect().should_auto_derive() {
77+
Some(from_reflect::impl_value(&meta))
78+
} else {
79+
None
80+
},
6081
),
61-
ReflectDerive::Value(meta) => (impls::impl_value(&meta), from_reflect::impl_value(&meta)),
6282
};
6383

6484
TokenStream::from(quote! {
@@ -112,6 +132,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
112132
&def.type_name,
113133
&def.generics,
114134
def.traits.unwrap_or_default(),
135+
def.from_reflect.unwrap_or_default(),
115136
);
116137
let reflect_impls = impls::impl_value(&meta);
117138
let from_reflect_impl = from_reflect::impl_value(&meta);
@@ -160,13 +181,11 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
160181

161182
match derive_data {
162183
ReflectDerive::Struct(struct_data) => {
163-
let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into();
164-
let impl_from_struct: proc_macro2::TokenStream =
165-
from_reflect::impl_struct(&struct_data).into();
184+
let impl_struct = impls::impl_struct(&struct_data);
185+
let impl_from_struct = from_reflect::impl_struct(&struct_data);
166186

167187
TokenStream::from(quote! {
168188
#impl_struct
169-
170189
#impl_from_struct
171190
})
172191
}
@@ -195,6 +214,7 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
195214
&def.type_name,
196215
&def.generics,
197216
def.traits.unwrap_or_default(),
217+
def.from_reflect.unwrap_or_default(),
198218
))
199219
.into()
200220
}

crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::container_attributes::ReflectTraits;
1+
use crate::container_attributes::{FromReflectAttrs, ReflectTraits};
22
use proc_macro2::Ident;
33
use syn::parse::{Parse, ParseStream};
44
use syn::token::{Paren, Where};
@@ -22,6 +22,7 @@ pub(crate) struct ReflectValueDef {
2222
pub type_name: Ident,
2323
pub generics: Generics,
2424
pub traits: Option<ReflectTraits>,
25+
pub from_reflect: Option<FromReflectAttrs>,
2526
}
2627

2728
impl Parse for ReflectValueDef {
@@ -49,6 +50,7 @@ impl Parse for ReflectValueDef {
4950
..generics
5051
},
5152
traits,
53+
from_reflect: None,
5254
})
5355
}
5456
}

examples/reflection/reflection.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ fn main() {
2525
/// Deriving `Reflect` implements the relevant reflection traits. In this case, it implements the
2626
/// `Reflect` trait and the `Struct` trait `derive(Reflect)` assumes that all fields also implement
2727
/// Reflect.
28+
///
29+
/// All fields in a reflected item will need to be `Reflect` as well. You can opt a field out of
30+
/// reflection by using the `#[reflect(ignore)]` attribute.
31+
/// If you choose to ignore a field, you need to let the automatically-derived `FromReflect` implementation
32+
/// how to handle the field.
33+
/// To do this, you can either define a `#[reflect(default = "...")]` attribute on the ignored field, or
34+
/// opt-out of `FromReflect`'s auto-derive using the `#[from_reflect(auto_derive = false)]` attribute.
2835
#[derive(Reflect)]
36+
#[from_reflect(auto_derive = false)]
2937
pub struct Foo {
3038
a: usize,
3139
nested: Bar,

0 commit comments

Comments
 (0)