bevy/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs
Gino Valente 9e30aa7c92
bevy_reflect_derive: Clean up attribute logic (#11777)
# Objective

The code in `bevy_reflect_derive` could use some cleanup.

## Solution

Took some of the changes in #11659 to create a dedicated PR for cleaning
up the field and container attribute logic.

#### Updated Naming

I renamed `ReflectTraits` and `ReflectFieldAttr` to
`ContainerAttributes` and `FieldAttributes`, respectively. I think these
are clearer.

#### Updated Parsing

##### Readability

The parsing logic wasn't too bad before, but it was getting difficult to
read. There was some duplicated logic between `Meta::List` and
`Meta::Path` attributes. Additionally, all the logic was kept inside a
large method.

To simply things, I replaced the nested meta parsing with `ParseStream`
parsing. In my opinion, this is easier to follow since it breaks up the
large match statement into a small set of single-line if statements,
where each if-block contains a single call to the appropriate attribute
parsing method.

##### Flexibility

On top of the added simplicity, this also makes our attribute parsing
much more flexible. It allows us to more elegantly handle custom where
clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more
non-standard attribute syntax (e.g. #11659).

##### Errors

This also allows us to automatically provide certain errors when
parsing. For example, since we can use `stream.lookahead1()`, we get
errors like the following for free:

```
error: expected one of: `ignore`, `skip_serializing`, `default`
    --> crates/bevy_reflect/src/lib.rs:1988:23
     |
1988 |             #[reflect(foo)]
     |                       ^^^
```

---

## Changelog

> [!note]
> All changes are internal to `bevy_reflect_derive` and should not
affect the public API[^1].

- Renamed `ReflectTraits` to `ContainerAttributes`
  - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs`
- Renamed `ReflectFieldAttr` to `FieldAttributes`
- Updated parsing logic for field/container attribute parsing
  - Now uses a `ParseStream` directly instead of nested meta parsing
- General code cleanup of the field/container attribute modules for
`bevy_reflect_derive`


[^1]: Does not include errors, which may look slightly different.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00

50 lines
2.0 KiB
Rust

//! Contains code related specifically to Bevy's type registration.
use crate::derive_data::ReflectMeta;
use crate::serialization::SerializationDataDef;
use crate::utility::WhereClauseOptions;
use quote::quote;
/// Creates the `GetTypeRegistration` impl for the given type data.
#[allow(clippy::too_many_arguments)]
pub(crate) fn impl_get_type_registration(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
serialization_data: Option<&SerializationDataDef>,
) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let registration_data = meta.attrs().idents();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let from_reflect_data = if meta.from_reflect().should_auto_derive() {
Some(quote! {
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<Self>::from_type());
})
} else {
None
};
let serialization_data = serialization_data.map(|data| {
let serialization_data = data.as_serialization_data(bevy_reflect_path);
quote! {
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#serialization_data);
}
});
quote! {
#[allow(unused_mut)]
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<Self>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<Self>::from_type());
#from_reflect_data
#serialization_data
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<Self>::from_type());)*
registration
}
}
}
}