Skip to content

Commit 8c6bbfc

Browse files
committed
Add #[from_reflect(auto_derive = false)] attribute
1 parent 7396717 commit 8c6bbfc

File tree

5 files changed

+122
-12
lines changed

5 files changed

+122
-12
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use syn::parse::{Parse, ParseStream};
1212
use syn::punctuated::Punctuated;
1313
use syn::spanned::Spanned;
1414
use syn::token::Comma;
15-
use syn::{Meta, NestedMeta, Path};
15+
use syn::{Lit, Meta, NestedMeta, Path};
1616

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

27+
// Attributes for `FromReflect` derive
28+
const FROM_REFLECT_AUTO_DERIVE_ATTR: &str = "auto_derive";
29+
2730
// The error message to show when a trait/type is specified multiple times
2831
const CONFLICTING_TYPE_DATA_MESSAGE: &str = "conflicting type data registration";
2932

@@ -323,3 +326,59 @@ fn add_unique_ident(idents: &mut Vec<Ident>, ident: Ident) -> Result<(), syn::Er
323326
idents.push(ident);
324327
Ok(())
325328
}
329+
330+
/// A collection of attributes used for deriving `FromReflect`.
331+
#[derive(Clone)]
332+
pub(crate) struct FromReflectAttrs {
333+
auto_derive: bool,
334+
}
335+
336+
impl Default for FromReflectAttrs {
337+
fn default() -> Self {
338+
Self { auto_derive: true }
339+
}
340+
}
341+
342+
impl FromReflectAttrs {
343+
pub fn from_nested_metas(nested_metas: &Punctuated<NestedMeta, Comma>) -> syn::Result<Self> {
344+
let mut attrs = FromReflectAttrs::default();
345+
for nested_meta in nested_metas.iter() {
346+
if let NestedMeta::Meta(Meta::NameValue(pair)) = nested_meta {
347+
let ident = pair.path.get_ident().ok_or_else(|| {
348+
syn::Error::new(
349+
pair.path.span(),
350+
"expected ident key in `from_reflect` attribute",
351+
)
352+
})?;
353+
354+
let key = ident.to_string();
355+
match key.as_str() {
356+
FROM_REFLECT_AUTO_DERIVE_ATTR => match &pair.lit {
357+
Lit::Bool(auto_derive) => attrs.auto_derive = auto_derive.value,
358+
lit => return Err(syn::Error::new(lit.span(), "expected boolean value")),
359+
},
360+
unknown => {
361+
return Err(syn::Error::new(
362+
ident.span(),
363+
format!("unknown key in `from_reflect` attribute: `{}`", unknown),
364+
))
365+
}
366+
}
367+
}
368+
}
369+
370+
Ok(attrs)
371+
}
372+
373+
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
374+
pub fn should_auto_derive(&self) -> bool {
375+
self.auto_derive
376+
}
377+
}
378+
379+
impl Parse for FromReflectAttrs {
380+
fn parse(input: ParseStream) -> syn::Result<Self> {
381+
let result = Punctuated::<NestedMeta, Comma>::parse_terminated(input)?;
382+
FromReflectAttrs::from_nested_metas(&result)
383+
}
384+
}

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

+24-4
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.
@@ -129,6 +133,7 @@ enum ReflectMode {
129133
impl<'a> ReflectDerive<'a> {
130134
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
131135
let mut traits = ReflectTraits::default();
136+
let mut from_reflect = FromReflectAttrs::default();
132137
// Should indicate whether `#[reflect_value]` was used
133138
let mut reflect_mode = None;
134139

@@ -161,6 +166,9 @@ impl<'a> ReflectDerive<'a> {
161166
let new_traits = ReflectTraits::from_nested_metas(&meta_list.nested)?;
162167
traits = traits.merge(new_traits)?;
163168
}
169+
Meta::List(meta_list) if meta_list.path.is_ident(FROM_REFLECT_ATTRIBUTE_NAME) => {
170+
from_reflect = FromReflectAttrs::from_nested_metas(&meta_list.nested)?;
171+
}
164172
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
165173
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
166174
return Err(syn::Error::new(
@@ -181,7 +189,7 @@ impl<'a> ReflectDerive<'a> {
181189
}
182190
}
183191

184-
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
192+
let meta = ReflectMeta::new(&input.ident, &input.generics, traits, from_reflect);
185193

186194
#[cfg(feature = "documentation")]
187195
let meta = meta.with_docs(doc);
@@ -278,9 +286,15 @@ impl<'a> ReflectDerive<'a> {
278286
}
279287

280288
impl<'a> ReflectMeta<'a> {
281-
pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self {
289+
pub fn new(
290+
type_name: &'a Ident,
291+
generics: &'a Generics,
292+
traits: ReflectTraits,
293+
from_reflect: FromReflectAttrs,
294+
) -> Self {
282295
Self {
283296
traits,
297+
from_reflect,
284298
type_name,
285299
generics,
286300
bevy_reflect_path: utility::get_bevy_reflect_path(),
@@ -300,6 +314,12 @@ impl<'a> ReflectMeta<'a> {
300314
&self.traits
301315
}
302316

317+
/// The `FromReflect` attributes on this type.
318+
#[allow(clippy::wrong_self_convention)]
319+
pub fn from_reflect(&self) -> &FromReflectAttrs {
320+
&self.from_reflect
321+
}
322+
303323
/// The name of this struct.
304324
pub fn type_name(&self) -> &'a Ident {
305325
self.type_name

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ use syn::{parse_macro_input, DeriveInput};
3737

3838
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
3939
pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
40+
pub(crate) static FROM_REFLECT_ATTRIBUTE_NAME: &str = "from_reflect";
4041

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

@@ -50,17 +51,36 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream {
5051
let (reflect_impls, from_reflect_impl) = match derive_data {
5152
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
5253
impls::impl_struct(&struct_data),
53-
from_reflect::impl_struct(&struct_data),
54+
if struct_data.meta().from_reflect().should_auto_derive() {
55+
Some(from_reflect::impl_struct(&struct_data))
56+
} else {
57+
None
58+
},
5459
),
5560
ReflectDerive::TupleStruct(struct_data) => (
5661
impls::impl_tuple_struct(&struct_data),
57-
from_reflect::impl_tuple_struct(&struct_data),
62+
if struct_data.meta().from_reflect().should_auto_derive() {
63+
Some(from_reflect::impl_tuple_struct(&struct_data))
64+
} else {
65+
None
66+
},
5867
),
5968
ReflectDerive::Enum(enum_data) => (
6069
impls::impl_enum(&enum_data),
61-
from_reflect::impl_enum(&enum_data),
70+
if enum_data.meta().from_reflect().should_auto_derive() {
71+
Some(from_reflect::impl_enum(&enum_data))
72+
} else {
73+
None
74+
},
75+
),
76+
ReflectDerive::Value(meta) => (
77+
impls::impl_value(&meta),
78+
if meta.from_reflect().should_auto_derive() {
79+
Some(from_reflect::impl_value(&meta))
80+
} else {
81+
None
82+
},
6283
),
63-
ReflectDerive::Value(meta) => (impls::impl_value(&meta), from_reflect::impl_value(&meta)),
6484
};
6585

6686
TokenStream::from(quote! {
@@ -114,6 +134,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
114134
&def.type_name,
115135
&def.generics,
116136
def.traits.unwrap_or_default(),
137+
def.from_reflect.unwrap_or_default(),
117138
);
118139

119140
#[cfg(feature = "documentation")]
@@ -171,7 +192,6 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
171192

172193
TokenStream::from(quote! {
173194
#impl_struct
174-
175195
#impl_from_struct
176196
})
177197
}
@@ -200,6 +220,7 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
200220
&def.type_name,
201221
&def.generics,
202222
def.traits.unwrap_or_default(),
223+
def.from_reflect.unwrap_or_default(),
203224
))
204225
.into()
205226
}

crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs

+3-1
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};
@@ -24,6 +24,7 @@ pub(crate) struct ReflectValueDef {
2424
pub type_name: Ident,
2525
pub generics: Generics,
2626
pub traits: Option<ReflectTraits>,
27+
pub from_reflect: Option<FromReflectAttrs>,
2728
}
2829

2930
impl Parse for ReflectValueDef {
@@ -53,6 +54,7 @@ impl Parse for ReflectValueDef {
5354
..generics
5455
},
5556
traits,
57+
from_reflect: None,
5658
})
5759
}
5860
}

examples/reflection/reflection.rs

+8
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)