Skip to content

Commit f64192c

Browse files
committed
Add #[reflect(from_reflect = false)] attribute
1 parent d2ca7dd commit f64192c

File tree

8 files changed

+158
-9
lines changed

8 files changed

+158
-9
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use syn::parse::{Parse, ParseStream};
1313
use syn::punctuated::Punctuated;
1414
use syn::spanned::Spanned;
1515
use syn::token::Comma;
16-
use syn::{Meta, Path};
16+
use syn::{LitBool, Meta, Path};
1717

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

28+
// Attributes for `FromReflect` implementation
29+
const FROM_REFLECT_ATTR: &str = "from_reflect";
30+
2831
// The error message to show when a trait/type is specified multiple times
2932
const CONFLICTING_TYPE_DATA_MESSAGE: &str = "conflicting type data registration";
3033

@@ -62,6 +65,40 @@ impl TraitImpl {
6265
}
6366
}
6467

68+
/// A collection of attributes used for deriving `FromReflect`.
69+
#[derive(Clone, Default)]
70+
pub(crate) struct FromReflectAttrs {
71+
auto_derive: Option<LitBool>,
72+
}
73+
74+
impl FromReflectAttrs {
75+
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
76+
pub fn should_auto_derive(&self) -> bool {
77+
self.auto_derive
78+
.as_ref()
79+
.map(|lit| lit.value())
80+
.unwrap_or(true)
81+
}
82+
83+
/// Merges this [`FromReflectAttrs`] with another.
84+
pub fn merge(&mut self, other: FromReflectAttrs) -> Result<(), syn::Error> {
85+
if let Some(new) = other.auto_derive {
86+
if let Some(existing) = &self.auto_derive {
87+
if existing.value() != new.value() {
88+
return Err(syn::Error::new(
89+
new.span(),
90+
format!("`from_reflect` already set to {}", existing.value()),
91+
));
92+
}
93+
} else {
94+
self.auto_derive = Some(new);
95+
}
96+
}
97+
98+
Ok(())
99+
}
100+
}
101+
65102
/// A collection of traits that have been registered for a reflected type.
66103
///
67104
/// This keeps track of a few traits that are utilized internally for reflection
@@ -128,6 +165,7 @@ pub(crate) struct ReflectTraits {
128165
debug: TraitImpl,
129166
hash: TraitImpl,
130167
partial_eq: TraitImpl,
168+
from_reflect: FromReflectAttrs,
131169
idents: Vec<Ident>,
132170
}
133171

@@ -201,7 +239,24 @@ impl ReflectTraits {
201239
Ok(())
202240
})?;
203241
}
204-
_ => {}
242+
Meta::NameValue(pair) => {
243+
if pair.path.is_ident(FROM_REFLECT_ATTR) {
244+
traits.from_reflect.auto_derive = match &pair.value {
245+
syn::Expr::Lit(syn::ExprLit {
246+
lit: syn::Lit::Bool(lit),
247+
..
248+
}) => Some(lit.clone()),
249+
_ => {
250+
return Err(syn::Error::new(
251+
pair.value.span(),
252+
"Expected a boolean value",
253+
))
254+
}
255+
};
256+
} else {
257+
return Err(syn::Error::new(pair.path.span(), "Unknown attribute"));
258+
}
259+
}
205260
}
206261
}
207262

@@ -219,6 +274,12 @@ impl ReflectTraits {
219274
&self.idents
220275
}
221276

277+
/// The `FromReflect` attributes on this type.
278+
#[allow(clippy::wrong_self_convention)]
279+
pub fn from_reflect(&self) -> &FromReflectAttrs {
280+
&self.from_reflect
281+
}
282+
222283
/// Returns the implementation of `Reflect::reflect_hash` as a `TokenStream`.
223284
///
224285
/// If `Hash` was not registered, returns `None`.
@@ -295,6 +356,7 @@ impl ReflectTraits {
295356
self.debug.merge(other.debug)?;
296357
self.hash.merge(other.hash)?;
297358
self.partial_eq.merge(other.partial_eq)?;
359+
self.from_reflect.merge(other.from_reflect)?;
298360
for ident in other.idents {
299361
add_unique_ident(&mut self.idents, ident)?;
300362
}

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

Lines changed: 7 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 crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
33
use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync};
44
use crate::type_path::parse_path_no_leading_colon;
@@ -376,6 +376,12 @@ impl<'a> ReflectMeta<'a> {
376376
&self.traits
377377
}
378378

379+
/// The `FromReflect` attributes on this type.
380+
#[allow(clippy::wrong_self_convention)]
381+
pub fn from_reflect(&self) -> &FromReflectAttrs {
382+
self.traits.from_reflect()
383+
}
384+
379385
/// The name of this struct.
380386
pub fn type_path(&self) -> &ReflectTypePath<'a> {
381387
&self.type_path

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name";
111111
/// If planning to serialize this type using the reflection serializers,
112112
/// then the `Serialize` and `Deserialize` traits will need to be implemented and registered as well.
113113
///
114+
/// ## `#[reflect(from_reflect = false)]`
115+
///
116+
/// This attribute will opt-out of the default `FromReflect` implementation.
117+
///
118+
/// This is useful for when a type can't or shouldn't implement `FromReflect`,
119+
/// or if a manual implementation is desired.
120+
///
114121
/// # Field Attributes
115122
///
116123
/// Along with the container attributes, this macro comes with some attributes that may be applied
@@ -147,17 +154,36 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream {
147154
let (reflect_impls, from_reflect_impl) = match derive_data {
148155
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
149156
impls::impl_struct(&struct_data),
150-
from_reflect::impl_struct(&struct_data),
157+
if struct_data.meta().from_reflect().should_auto_derive() {
158+
Some(from_reflect::impl_struct(&struct_data))
159+
} else {
160+
None
161+
},
151162
),
152163
ReflectDerive::TupleStruct(struct_data) => (
153164
impls::impl_tuple_struct(&struct_data),
154-
from_reflect::impl_tuple_struct(&struct_data),
165+
if struct_data.meta().from_reflect().should_auto_derive() {
166+
Some(from_reflect::impl_tuple_struct(&struct_data))
167+
} else {
168+
None
169+
},
155170
),
156171
ReflectDerive::Enum(enum_data) => (
157172
impls::impl_enum(&enum_data),
158-
from_reflect::impl_enum(&enum_data),
173+
if enum_data.meta().from_reflect().should_auto_derive() {
174+
Some(from_reflect::impl_enum(&enum_data))
175+
} else {
176+
None
177+
},
178+
),
179+
ReflectDerive::Value(meta) => (
180+
impls::impl_value(&meta),
181+
if meta.from_reflect().should_auto_derive() {
182+
Some(from_reflect::impl_value(&meta))
183+
} else {
184+
None
185+
},
159186
),
160-
ReflectDerive::Value(meta) => (impls::impl_value(&meta), from_reflect::impl_value(&meta)),
161187
};
162188

163189
TokenStream::from(quote! {
@@ -418,7 +444,6 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
418444

419445
TokenStream::from(quote! {
420446
#impl_struct
421-
422447
#impl_from_struct
423448
})
424449
}

crates/bevy_reflect/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@
202202
//! And since the [`FromReflect::from_reflect`] method takes the data by reference,
203203
//! this can be used to effectively clone data (to an extent).
204204
//!
205-
//! It is automatically implemented when [deriving `Reflect`] on a type.
205+
//! It is automatically implemented when [deriving `Reflect`] on a type unless opted out of
206+
//! using `#[reflect(from_reflect = false)]` on the item.
206207
//!
207208
//! ```
208209
//! # use bevy_reflect::{Reflect, FromReflect};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use bevy_reflect::Reflect;
2+
3+
// Reason: Cannot have conflicting `from_reflect` attributes
4+
#[derive(Reflect)]
5+
#[reflect(from_reflect = false)]
6+
#[reflect(from_reflect = true)]
7+
struct Foo {
8+
value: String,
9+
}
10+
11+
// Reason: Cannot have conflicting `from_reflect` attributes
12+
#[derive(Reflect)]
13+
#[reflect(from_reflect = true)]
14+
#[reflect(from_reflect = false)]
15+
struct Bar {
16+
value: String,
17+
}
18+
19+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: `from_reflect` already set to false
2+
--> tests/reflect_derive/from_reflect.fail.rs:6:26
3+
|
4+
6 | #[reflect(from_reflect = true)]
5+
| ^^^^
6+
7+
error: `from_reflect` already set to true
8+
--> tests/reflect_derive/from_reflect.fail.rs:14:26
9+
|
10+
14 | #[reflect(from_reflect = false)]
11+
| ^^^^^
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use bevy_reflect::Reflect;
2+
3+
#[derive(Reflect)]
4+
#[reflect(from_reflect = false)]
5+
#[reflect(from_reflect = false)]
6+
struct Foo {
7+
value: String,
8+
}
9+
10+
#[derive(Reflect)]
11+
#[reflect(from_reflect = true)]
12+
#[reflect(from_reflect = true)]
13+
struct Bar {
14+
value: String,
15+
}
16+
17+
fn main() {}

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 `#[reflect(from_reflect = false)]` attribute.
2835
#[derive(Reflect)]
36+
#[reflect(from_reflect = false)]
2937
pub struct Foo {
3038
a: usize,
3139
nested: Bar,

0 commit comments

Comments
 (0)