bevy_reflect: Remove ContainerAttributes::merge (#13303)

# Objective

Unblocks #11659.

Currently the `Reflect` derive macro has to go through a merge process
for each `#[reflect]`/`#[reflet_value]` attribute encountered on a
container type.

Not only is this a bit inefficient, but it also has a soft requirement
that we can compare attributes such that an error can be thrown on
duplicates, invalid states, etc.

While working on #11659 this proved to be challenging due to the fact
that `syn` types don't implement `PartialEq` or `Hash` without enabling
the `extra-traits` feature.

Ideally, we wouldn't have to enable another feature just to accommodate
this one use case.

## Solution

Removed `ContainerAttributes::merge`.

This was a fairly simple change as we could just have the parsing
functions take `&mut self` instead of returning `Self`.

## Testing

CI should build as there should be no user-facing change.
This commit is contained in:
Gino Valente 2024-05-09 11:17:54 -07:00 committed by GitHub
parent 42ba9dfaea
commit 705c144259
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 101 deletions

View file

@ -92,24 +92,6 @@ impl FromReflectAttrs {
.map(|lit| lit.value())
.unwrap_or(true)
}
/// Merges this [`FromReflectAttrs`] with another.
pub fn merge(&mut self, other: FromReflectAttrs) -> Result<(), syn::Error> {
if let Some(new) = other.auto_derive {
if let Some(existing) = &self.auto_derive {
if existing.value() != new.value() {
return Err(syn::Error::new(
new.span(),
format!("`{FROM_REFLECT_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.auto_derive = Some(new);
}
}
Ok(())
}
}
/// A collection of attributes used for deriving `TypePath` via the `Reflect` derive.
@ -133,24 +115,6 @@ impl TypePathAttrs {
.map(|lit| lit.value())
.unwrap_or(true)
}
/// Merges this [`TypePathAttrs`] with another.
pub fn merge(&mut self, other: TypePathAttrs) -> Result<(), syn::Error> {
if let Some(new) = other.auto_derive {
if let Some(existing) = &self.auto_derive {
if existing.value() != new.value() {
return Err(syn::Error::new(
new.span(),
format!("`{TYPE_PATH_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.auto_derive = Some(new);
}
}
Ok(())
}
}
/// A collection of traits that have been registered for a reflected type.
@ -231,14 +195,16 @@ impl ContainerAttributes {
///
/// # Example
/// - `Hash, Debug(custom_debug), MyTrait`
pub fn parse_terminated(input: ParseStream, trait_: ReflectTraitToImpl) -> syn::Result<Self> {
let mut this = Self::default();
pub fn parse_terminated(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
terminated_parser(Token![,], |stream| {
this.parse_container_attribute(stream, trait_)
self.parse_container_attribute(stream, trait_)
})(input)?;
Ok(this)
Ok(())
}
/// Parse the contents of a `#[reflect(...)]` attribute into a [`ContainerAttributes`] instance.
@ -246,8 +212,12 @@ impl ContainerAttributes {
/// # Example
/// - `#[reflect(Hash, Debug(custom_debug), MyTrait)]`
/// - `#[reflect(no_field_bounds)]`
pub fn parse_meta_list(meta: &MetaList, trait_: ReflectTraitToImpl) -> syn::Result<Self> {
meta.parse_args_with(|stream: ParseStream| Self::parse_terminated(stream, trait_))
pub fn parse_meta_list(
&mut self,
meta: &MetaList,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
meta.parse_args_with(|stream: ParseStream| self.parse_terminated(stream, trait_))
}
/// Parse a single container attribute.
@ -392,7 +362,7 @@ impl ContainerAttributes {
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let value = extract_bool(&pair.value, |lit| {
let extracted_bool = extract_bool(&pair.value, |lit| {
// Override `lit` if this is a `FromReflect` derive.
// This typically means a user is opting out of the default implementation
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
@ -401,7 +371,16 @@ impl ContainerAttributes {
.unwrap_or_else(|| lit.clone())
})?;
self.from_reflect_attrs.auto_derive = Some(value);
if let Some(existing) = &self.from_reflect_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{FROM_REFLECT_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.from_reflect_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
@ -416,7 +395,7 @@ impl ContainerAttributes {
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let value = extract_bool(&pair.value, |lit| {
let extracted_bool = extract_bool(&pair.value, |lit| {
// Override `lit` if this is a `FromReflect` derive.
// This typically means a user is opting out of the default implementation
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
@ -425,7 +404,16 @@ impl ContainerAttributes {
.unwrap_or_else(|| lit.clone())
})?;
self.type_path_attrs.auto_derive = Some(value);
if let Some(existing) = &self.type_path_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{TYPE_PATH_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.type_path_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
@ -530,50 +518,6 @@ impl ContainerAttributes {
pub fn no_field_bounds(&self) -> bool {
self.no_field_bounds
}
/// Merges the trait implementations of this [`ContainerAttributes`] with another one.
///
/// An error is returned if the two [`ContainerAttributes`] have conflicting implementations.
pub fn merge(&mut self, other: ContainerAttributes) -> Result<(), syn::Error> {
// Destructuring is used to help ensure that all fields are merged
let Self {
debug,
hash,
partial_eq,
from_reflect_attrs,
type_path_attrs,
custom_where,
no_field_bounds,
idents,
} = self;
debug.merge(other.debug)?;
hash.merge(other.hash)?;
partial_eq.merge(other.partial_eq)?;
from_reflect_attrs.merge(other.from_reflect_attrs)?;
type_path_attrs.merge(other.type_path_attrs)?;
Self::merge_custom_where(custom_where, other.custom_where);
*no_field_bounds |= other.no_field_bounds;
for ident in other.idents {
add_unique_ident(idents, ident)?;
}
Ok(())
}
fn merge_custom_where(this: &mut Option<WhereClause>, other: Option<WhereClause>) {
match (this, other) {
(Some(this), Some(other)) => {
this.predicates.extend(other.predicates);
}
(this @ None, Some(other)) => {
*this = Some(other);
}
_ => {}
}
}
}
/// Adds an identifier to a vector of identifiers if it is not already present.

View file

@ -183,7 +183,7 @@ impl<'a> ReflectDerive<'a> {
input: &'a DeriveInput,
provenance: ReflectProvenance,
) -> Result<Self, syn::Error> {
let mut traits = ContainerAttributes::default();
let mut container_attributes = ContainerAttributes::default();
// Should indicate whether `#[reflect_value]` was used.
let mut reflect_mode = None;
// Should indicate whether `#[type_path = "..."]` was used.
@ -205,9 +205,7 @@ impl<'a> ReflectDerive<'a> {
}
reflect_mode = Some(ReflectMode::Normal);
let new_traits =
ContainerAttributes::parse_meta_list(meta_list, provenance.trait_)?;
traits.merge(new_traits)?;
container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
}
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
@ -218,9 +216,7 @@ impl<'a> ReflectDerive<'a> {
}
reflect_mode = Some(ReflectMode::Value);
let new_traits =
ContainerAttributes::parse_meta_list(meta_list, provenance.trait_)?;
traits.merge(new_traits)?;
container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
}
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
@ -296,7 +292,7 @@ impl<'a> ReflectDerive<'a> {
generics: &input.generics,
};
let meta = ReflectMeta::new(type_path, traits);
let meta = ReflectMeta::new(type_path, container_attributes);
if provenance.source == ReflectImplSource::ImplRemoteType
&& meta.type_path_attrs().should_auto_derive()
@ -439,7 +435,7 @@ impl<'a> ReflectMeta<'a> {
Self { docs, ..self }
}
/// The registered reflect traits on this struct.
/// The registered reflect attributes on this struct.
pub fn attrs(&self) -> &ContainerAttributes {
&self.attrs
}

View file

@ -55,7 +55,11 @@ impl ReflectValueDef {
if input.peek(Paren) {
let content;
parenthesized!(content in input);
traits = Some(ContainerAttributes::parse_terminated(&content, trait_)?);
traits = Some({
let mut attrs = ContainerAttributes::default();
attrs.parse_terminated(&content, trait_)?;
attrs
});
}
Ok(ReflectValueDef {
attrs,