From c03ada53f7b57f014d5f9387effd403919d0a596 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Aug 2022 19:50:05 -0700 Subject: [PATCH 01/14] Added alias support --- .../src/container_attributes.rs | 99 ++++++- .../bevy_reflect_derive/src/derive_data.rs | 22 +- .../bevy_reflect_derive/src/registration.rs | 28 +- crates/bevy_reflect/src/lib.rs | 63 ++++ crates/bevy_reflect/src/serde/de.rs | 7 +- crates/bevy_reflect/src/type_registry.rs | 274 +++++++++++++++++- 6 files changed, 461 insertions(+), 32 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 99513ed3acbd4..52d6ad5e50ee9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -10,8 +10,9 @@ use proc_macro2::Ident; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::token::Comma; -use syn::{Meta, NestedMeta, Path}; +use syn::{Lit, LitStr, Meta, NestedMeta, Path}; // The "special" trait idents that are used internally for reflection. // Received via attributes like `#[reflect(PartialEq, Hash, ...)]` @@ -23,6 +24,10 @@ const HASH_ATTR: &str = "Hash"; // but useful to know exist nonetheless pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault"; +// Other container attribute idents +const ALIAS_ATTR: &str = "alias"; +const DEPRECATED_ALIAS_ATTR: &str = "deprecated_alias"; + /// A marker for trait implementations registered via the `Reflect` derive macro. #[derive(Clone, Default)] pub(crate) enum TraitImpl { @@ -103,11 +108,13 @@ pub(crate) struct ReflectTraits { hash: TraitImpl, partial_eq: TraitImpl, idents: Vec, + aliases: Vec, + deprecated_aliases: Vec, } impl ReflectTraits { /// Create a new [`ReflectTraits`] instance from a set of nested metas. - pub fn from_nested_metas(nested_metas: &Punctuated) -> Self { + pub fn from_nested_metas(nested_metas: &Punctuated) -> syn::Result { let mut traits = ReflectTraits::default(); for nested_meta in nested_metas.iter() { match nested_meta { @@ -151,11 +158,87 @@ impl ReflectTraits { } } } + // Handles `#[reflect( alias = "Foo" )]` + NestedMeta::Meta(Meta::NameValue(pair)) => { + let ident = pair + .path + .get_ident() + .ok_or_else(|| syn::Error::new(pair.span(), "not a valid path"))?; + + let attr_name = ident.to_string(); + match (attr_name.as_str(), &pair.lit) { + (ALIAS_ATTR, Lit::Str(alias)) => { + traits.aliases.push(alias.clone()); + } + (DEPRECATED_ALIAS_ATTR, Lit::Str(alias)) => { + traits.deprecated_aliases.push(alias.clone()); + } + (DEPRECATED_ALIAS_ATTR | ALIAS_ATTR, lit) => { + return Err(syn::Error::new(lit.span(), "expected a string literal")); + } + (_, _) => { + return Err(syn::Error::new( + ident.span(), + format_args!( + "unknown attribute `{}`, expected one of {:?}", + attr_name, + [ALIAS_ATTR, DEPRECATED_ALIAS_ATTR] + ), + )); + } + } + } _ => {} } } - traits + Ok(traits) + } + + pub fn combine(&mut self, other: Self) { + if matches!(self.debug, TraitImpl::NotImplemented) { + self.debug = other.debug; + } + if matches!(self.hash, TraitImpl::NotImplemented) { + self.hash = other.hash; + } + if matches!(self.partial_eq, TraitImpl::NotImplemented) { + self.partial_eq = other.partial_eq; + } + + for ident in other.idents { + if self.idents.contains(&ident) { + continue; + } + + self.idents.push(ident); + } + + for alias in other.aliases { + let value = alias.value(); + if self + .aliases + .iter() + .any(|other_alias| value == other_alias.value()) + { + continue; + } + + self.aliases.push(alias); + } + + for alias in other.deprecated_aliases { + let value = alias.value(); + if self + .deprecated_aliases + .iter() + .any(|other_alias| value == other_alias.value()) + { + continue; + } + + self.deprecated_aliases.push(alias); + } } /// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`) @@ -169,6 +252,14 @@ impl ReflectTraits { &self.idents } + pub fn aliases(&self) -> &[LitStr] { + &self.aliases + } + + pub fn deprecated_aliases(&self) -> &[LitStr] { + &self.deprecated_aliases + } + /// Returns the implementation of `Reflect::reflect_hash` as a `TokenStream`. /// /// If `Hash` was not registered, returns `None`. @@ -242,6 +333,6 @@ impl ReflectTraits { impl Parse for ReflectTraits { fn parse(input: ParseStream) -> syn::Result { let result = Punctuated::::parse_terminated(input)?; - Ok(ReflectTraits::from_nested_metas(&result)) + ReflectTraits::from_nested_metas(&result) } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 422809d6db804..9f04c582bcc94 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -119,10 +119,19 @@ impl<'a> ReflectDerive<'a> { if let Some(ident) = meta_list.path.get_ident() { if ident == REFLECT_ATTRIBUTE_NAME { - traits = ReflectTraits::from_nested_metas(&meta_list.nested); + if force_reflect_value { + force_reflect_value = false; + traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; + } else { + traits.combine(ReflectTraits::from_nested_metas(&meta_list.nested)?); + } } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - force_reflect_value = true; - traits = ReflectTraits::from_nested_metas(&meta_list.nested); + if !force_reflect_value { + force_reflect_value = true; + traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; + } else { + traits.combine(ReflectTraits::from_nested_metas(&meta_list.nested)?); + } } } } @@ -242,12 +251,7 @@ impl<'a> ReflectMeta<'a> { /// Returns the `GetTypeRegistration` impl as a `TokenStream`. pub fn get_type_registration(&self) -> proc_macro2::TokenStream { - crate::registration::impl_get_type_registration( - self.type_name, - &self.bevy_reflect_path, - self.traits.idents(), - self.generics, - ) + crate::registration::impl_get_type_registration(self) } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 7b13f7d352ef8..1cf8fb9aef5b0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,17 +1,19 @@ //! Contains code related specifically to Bevy's type registration. -use proc_macro2::Ident; +use crate::ReflectMeta; use quote::quote; -use syn::{Generics, Path}; /// Creates the `GetTypeRegistration` impl for the given type data. -pub(crate) fn impl_get_type_registration( - type_name: &Ident, - bevy_reflect_path: &Path, - registration_data: &[Ident], - generics: &Generics, -) -> proc_macro2::TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); +pub(crate) fn impl_get_type_registration(meta: &ReflectMeta) -> proc_macro2::TokenStream { + let type_name = meta.type_name(); + let bevy_reflect_path = meta.bevy_reflect_path(); + + let registration_data = meta.traits().idents(); + + let aliases = meta.traits().aliases(); + let deprecated_aliases = meta.traits().deprecated_aliases(); + + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); quote! { #[allow(unused_mut)] impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { @@ -21,6 +23,14 @@ pub(crate) fn impl_get_type_registration( #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* registration } + + fn aliases() -> &'static [&'static str] { + &[#(#aliases),*] + } + + fn deprecated_aliases() -> &'static [&'static str] { + &[#(#deprecated_aliases),*] + } } } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index ef9e05ccd3cff..c6f0e67e9e6ca 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -436,6 +436,69 @@ mod tests { assert_eq!(new_foo, expected_new_foo); } + // FIXME: This test doesn't actually test anything since the current deserializer doesn't rely on + // the type name being correct and registered. + #[test] + fn reflect_alias() { + #[derive(Reflect, FromReflect)] + #[reflect(alias = "SomeAlias")] + #[reflect(alias = "SomeOtherAlias")] + #[reflect(deprecated_alias = "SomeOldAlias")] + struct Foo { + x: u32, + } + + let mut registry = TypeRegistry::default(); + registry.register::(); + registry.register::(); + + let deserialize_foo = |serialized: &str| { + let mut deserializer = Deserializer::from_str(serialized).unwrap(); + let reflect_deserializer = ReflectDeserializer::new(®istry); + let value = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + ::from_reflect(value.as_ref()).unwrap() + }; + + let serialized = r#"{ + "type": "SomeAlias", + "struct": { + "x": { + "type": "u32", + "value": 123, + } + } + }"#; + let dynamic_struct = deserialize_foo(serialized); + let expected: Box = Box::new(Foo { x: 123 }); + assert!(expected.reflect_partial_eq(&dynamic_struct).unwrap()); + + let serialized = r#"{ + "type": "SomeOtherAlias", + "struct": { + "x": { + "type": "u32", + "value": 123, + } + } + }"#; + let dynamic_struct = deserialize_foo(serialized); + let expected: Box = Box::new(Foo { x: 123 }); + assert!(expected.reflect_partial_eq(&dynamic_struct).unwrap()); + + let serialized = r#"{ + "type": "SomeOldAlias", + "struct": { + "x": { + "type": "u32", + "value": 123, + } + } + }"#; + let dynamic_struct = deserialize_foo(serialized); + let expected: Box = Box::new(Foo { x: 123 }); + assert!(expected.reflect_partial_eq(&dynamic_struct).unwrap()); + } + #[test] fn reflect_serialize() { #[derive(Reflect)] diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index ad24185de15f3..9c3f66106ee41 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -217,8 +217,11 @@ impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { let type_name = type_name .take() .ok_or_else(|| de::Error::missing_field(type_fields::TYPE))?; - let registration = - self.registry.get_with_name(&type_name).ok_or_else(|| { + let registration = self + .registry + .get_with_name(&type_name) + .or_else(|| self.registry.get_with_alias(&type_name)) + .ok_or_else(|| { de::Error::custom(format_args!( "No registration found for {}", type_name diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 74865abeb0c91..7176587013000 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,9 +1,11 @@ use crate::{serde::Serializable, Reflect, TypeInfo, Typed}; use bevy_ptr::{Ptr, PtrMut}; +use bevy_utils::tracing::warn; use bevy_utils::{HashMap, HashSet}; use downcast_rs::{impl_downcast, Downcast}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::Deserialize; +use std::borrow::Cow; use std::{any::TypeId, fmt::Debug, sync::Arc}; /// A registry of reflected types. @@ -11,6 +13,7 @@ pub struct TypeRegistry { registrations: HashMap, short_name_to_id: HashMap, full_name_to_id: HashMap, + alias_to_id: HashMap, AliasData>, ambiguous_names: HashSet, } @@ -33,6 +36,35 @@ impl Debug for TypeRegistryArc { /// This trait is automatically implemented for types which derive [`Reflect`]. pub trait GetTypeRegistration { fn get_type_registration() -> TypeRegistration; + + /// Returns the static set of aliases that can be used to refer to this type. + /// + /// Note that these are the _default_ aliases— used specifically for type registration. + /// For a given [registry], the actual set of aliases for a registered type may differ from the + /// ones listed here. + /// + /// If you need the list of aliases for this type, please use [`TypeRegistration::aliases`]. + /// + /// [registry]: TypeRegistry + fn aliases() -> &'static [&'static str] { + &[] + } + + /// Returns the static set of _deprecated_ aliases that can be used to refer to this type. + /// + /// For the list of _current_ aliases, try using [`aliases`] instead. + /// + /// Note that, like [`aliases`], this is the _default_ set— used specifically for type registration. + /// For a given [registry], the actual set of deprecated aliases for a registered type may differ from the + /// ones listed here. + /// + /// If you need the list of aliases for this type, please use [`TypeRegistration::deprecated_aliases`]. + /// + /// [`aliases`]: GetTypeRegistration::aliases + /// [registry]: TypeRegistry + fn deprecated_aliases() -> &'static [&'static str] { + &[] + } } impl Default for TypeRegistry { @@ -48,6 +80,7 @@ impl TypeRegistry { registrations: Default::default(), short_name_to_id: Default::default(), full_name_to_id: Default::default(), + alias_to_id: Default::default(), ambiguous_names: Default::default(), } } @@ -87,6 +120,8 @@ impl TypeRegistry { /// Registers the type described by `registration`. pub fn add_registration(&mut self, registration: TypeRegistration) { + let type_id = registration.type_id(); + let type_name = registration.type_name(); let short_name = registration.short_name.to_string(); if self.short_name_to_id.contains_key(&short_name) || self.ambiguous_names.contains(&short_name) @@ -95,13 +130,18 @@ impl TypeRegistry { self.short_name_to_id.remove(&short_name); self.ambiguous_names.insert(short_name); } else { - self.short_name_to_id - .insert(short_name, registration.type_id()); + self.short_name_to_id.insert(short_name, type_id); } - self.full_name_to_id - .insert(registration.type_name().to_string(), registration.type_id()); - self.registrations - .insert(registration.type_id(), registration); + + for alias in registration.aliases { + self.register_alias_internal(Cow::Borrowed(alias), type_name, type_id, false, true); + } + for alias in registration.deprecated_aliases { + self.register_alias_internal(Cow::Borrowed(alias), type_name, type_id, true, true); + } + + self.full_name_to_id.insert(type_name.to_string(), type_id); + self.registrations.insert(type_id, registration); } /// Registers the type data `D` for type `T`. @@ -172,6 +212,36 @@ impl TypeRegistry { .and_then(move |id| self.get_mut(id)) } + /// Returns a reference to the [`TypeRegistration`] of the type with the + /// given alias. + /// + /// If no type with the given alias has been registered, returns `None`. + pub fn get_with_alias(&self, alias: &str) -> Option<&TypeRegistration> { + let alias_data = self.alias_to_id.get(alias)?; + let registration = self.get(alias_data.type_id)?; + + if alias_data.is_deprecated { + Self::warn_alias_deprecation(alias, registration); + } + + Some(registration) + } + + /// Returns a mutable reference to the [`TypeRegistration`] of the type with + /// the given alias. + /// + /// If no type with the given alias has been registered, returns `None`. + pub fn get_with_alias_mut(&mut self, alias: &str) -> Option<&mut TypeRegistration> { + let alias_data = *self.alias_to_id.get(alias)?; + let registration = self.get_mut(alias_data.type_id)?; + + if alias_data.is_deprecated { + Self::warn_alias_deprecation(alias, registration); + } + + Some(registration) + } + /// Returns a reference to the [`TypeRegistration`] of the type with /// the given short name. /// @@ -239,6 +309,124 @@ impl TypeRegistry { pub fn iter_mut(&mut self) -> impl Iterator { self.registrations.values_mut() } + + /// Register an alias for the given type, `T`. + /// + /// This will implicitly overwrite existing usages of the given alias + /// and print a warning to the console if it does so. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). + /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_alias`](Self::overwrite_alias). + pub fn register_alias(&mut self, alias: impl Into>) { + let registration = self.get(TypeId::of::()).unwrap_or_else(|| { + panic!( + "cannot register an alias to non-existent type `{}`", + std::any::type_name::() + ) + }); + self.register_alias_internal( + alias.into(), + registration.type_name(), + registration.type_id(), + false, + true, + ); + } + + /// Attempts to register an alias for the given type, `T`, if it isn't already in use. + /// + /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or + /// [`overwrite_alias`](Self::overwrite_alias). + pub fn try_register_alias(&mut self, alias: impl Into>) -> bool { + let registration = self.get(TypeId::of::()).unwrap_or_else(|| { + panic!( + "cannot register an alias to non-existent type `{}`", + std::any::type_name::() + ) + }); + + let alias = alias.into(); + + if self.alias_to_id.contains_key(&alias) { + false + } else { + self.register_alias_internal( + alias, + registration.type_name(), + registration.type_id(), + false, + false, + ); + + true + } + } + + /// Register an alias for the given type, `T`, explicitly overwriting existing aliases. + /// + /// Unlike, [`register_alias`](Self::register_alias), this does not print a warning when overwriting existing aliases. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). + pub fn overwrite_alias(&mut self, alias: impl Into>) { + let registration = self.get(TypeId::of::()).unwrap_or_else(|| { + panic!( + "cannot register an alias to non-existent type `{}`", + std::any::type_name::() + ) + }); + + self.register_alias_internal( + alias.into(), + registration.type_name(), + registration.type_id(), + false, + false, + ); + } + + /// Registers an alias for the given type. + fn register_alias_internal( + &mut self, + alias: Cow<'static, str>, + type_name: &'static str, + type_id: TypeId, + is_deprecated: bool, + should_warn: bool, + ) { + let existing = self.alias_to_id.insert( + alias.clone(), + AliasData { + type_id, + is_deprecated, + }, + ); + + if !should_warn { + return; + } + + if let Some(existing) = existing.and_then(|existing| self.get(existing.type_id)) { + warn!( + "overwrote alias `{alias}` — was assigned to type `{}` ({:?}), but is now assigned to type `{}` ({:?})", + existing.type_name(), + existing.type_id(), + type_name, + type_id + ); + } + } + + /// Prints a warning stating that the given alias has been deprecated for the given registration. + fn warn_alias_deprecation(alias: &str, registration: &TypeRegistration) { + warn!( + "the alias `{}` has been deprecated for the type `{}` ({:?}). Consider using the full type name or \ + one of the current aliases: {:?}", + alias, + registration.type_name(), + registration.type_id(), + registration.aliases(), + ); + } } impl TypeRegistryArc { @@ -253,6 +441,12 @@ impl TypeRegistryArc { } } +#[derive(Copy, Clone)] +struct AliasData { + pub type_id: TypeId, + pub is_deprecated: bool, +} + /// A record of data about a type. /// /// This contains the [`TypeInfo`] of the type, as well as its [short name]. @@ -270,6 +464,8 @@ pub struct TypeRegistration { short_name: String, data: HashMap>, type_info: &'static TypeInfo, + aliases: &'static [&'static str], + deprecated_aliases: &'static [&'static str], } impl TypeRegistration { @@ -314,12 +510,14 @@ impl TypeRegistration { } /// Creates type registration information for `T`. - pub fn of() -> Self { + pub fn of() -> Self { let type_name = std::any::type_name::(); Self { data: HashMap::default(), short_name: bevy_utils::get_short_name(type_name), type_info: T::type_info(), + aliases: T::aliases(), + deprecated_aliases: T::deprecated_aliases(), } } @@ -336,6 +534,20 @@ impl TypeRegistration { pub fn type_name(&self) -> &'static str { self.type_info.type_name() } + + /// Returns the default set of aliases for the type. + /// + /// For the set of _deprecated_ aliases, try [`deprecated_aliases`](Self::deprecated_aliases). + pub fn aliases(&self) -> &'static [&'static str] { + self.aliases + } + + /// Returns the default set of _deprecated_ aliases for the type. + /// + /// For the set of _current_ aliases, try [`aliases`](Self::aliases). + pub fn deprecated_aliases(&self) -> &'static [&'static str] { + self.deprecated_aliases + } } impl Clone for TypeRegistration { @@ -349,6 +561,8 @@ impl Clone for TypeRegistration { data, short_name: self.short_name.clone(), type_info: self.type_info, + aliases: self.aliases, + deprecated_aliases: self.deprecated_aliases, } } } @@ -522,9 +736,10 @@ impl FromType for ReflectFromPtr { #[cfg(test)] mod test { + use std::any::TypeId; use std::ptr::NonNull; - use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration}; + use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration, TypeRegistry}; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::HashMap; @@ -575,6 +790,49 @@ mod test { } } + #[test] + fn should_register_new_alias() { + #[derive(Reflect)] + struct Foo; + #[derive(Reflect)] + struct Bar; + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register::(); + + registry.register_alias::("my_alias"); + assert_eq!( + TypeId::of::(), + registry.get_with_alias("my_alias").unwrap().type_id() + ); + + registry.register_alias::("my_alias"); + assert_eq!( + TypeId::of::(), + registry.get_with_alias("my_alias").unwrap().type_id() + ); + } + + #[test] + fn should_not_register_existing_alias() { + #[derive(Reflect)] + struct Foo; + #[derive(Reflect)] + struct Bar; + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register::(); + + registry.register_alias::("my_alias"); + registry.try_register_alias::("my_alias"); + assert_eq!( + TypeId::of::(), + registry.get_with_alias("my_alias").unwrap().type_id() + ); + } + #[test] fn test_property_type_registration() { assert_eq!( From 7973bdf2d8bb91bd4b3e013ce93acb4ec6d070fe Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Aug 2022 20:24:45 -0700 Subject: [PATCH 02/14] Allow mutation of TypeRegistration aliases --- crates/bevy_reflect/src/type_registry.rs | 127 +++++++++++++---------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 7176587013000..7ebca613c3557 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -133,11 +133,11 @@ impl TypeRegistry { self.short_name_to_id.insert(short_name, type_id); } - for alias in registration.aliases { - self.register_alias_internal(Cow::Borrowed(alias), type_name, type_id, false, true); + for alias in ®istration.aliases { + self.register_alias_internal(alias.clone(), type_name, type_id, false, true); } - for alias in registration.deprecated_aliases { - self.register_alias_internal(Cow::Borrowed(alias), type_name, type_id, true, true); + for alias in ®istration.deprecated_aliases { + self.register_alias_internal(alias.clone(), type_name, type_id, true, true); } self.full_name_to_id.insert(type_name.to_string(), type_id); @@ -318,19 +318,20 @@ impl TypeRegistry { /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_alias`](Self::overwrite_alias). pub fn register_alias(&mut self, alias: impl Into>) { - let registration = self.get(TypeId::of::()).unwrap_or_else(|| { + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "cannot register an alias to non-existent type `{}`", std::any::type_name::() ) }); - self.register_alias_internal( - alias.into(), - registration.type_name(), - registration.type_id(), - false, - true, - ); + + let alias = alias.into(); + registration.aliases.insert(alias.clone()); + + let type_name = registration.type_name(); + let type_id = registration.type_id(); + + self.register_alias_internal(alias, type_name, type_id, false, true); } /// Attempts to register an alias for the given type, `T`, if it isn't already in use. @@ -338,25 +339,24 @@ impl TypeRegistry { /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or /// [`overwrite_alias`](Self::overwrite_alias). pub fn try_register_alias(&mut self, alias: impl Into>) -> bool { - let registration = self.get(TypeId::of::()).unwrap_or_else(|| { - panic!( - "cannot register an alias to non-existent type `{}`", - std::any::type_name::() - ) - }); - let alias = alias.into(); if self.alias_to_id.contains_key(&alias) { false } else { - self.register_alias_internal( - alias, - registration.type_name(), - registration.type_id(), - false, - false, - ); + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { + panic!( + "cannot register an alias to non-existent type `{}`", + std::any::type_name::() + ) + }); + + registration.aliases.insert(alias.clone()); + + let type_name = registration.type_name(); + let type_id = registration.type_id(); + + self.register_alias_internal(alias, type_name, type_id, false, false); true } @@ -368,20 +368,20 @@ impl TypeRegistry { /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). pub fn overwrite_alias(&mut self, alias: impl Into>) { - let registration = self.get(TypeId::of::()).unwrap_or_else(|| { + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "cannot register an alias to non-existent type `{}`", std::any::type_name::() ) }); - self.register_alias_internal( - alias.into(), - registration.type_name(), - registration.type_id(), - false, - false, - ); + let alias = alias.into(); + registration.aliases.insert(alias.clone()); + + let type_name = registration.type_name(); + let type_id = registration.type_id(); + + self.register_alias_internal(alias, type_name, type_id, false, false); } /// Registers an alias for the given type. @@ -405,7 +405,8 @@ impl TypeRegistry { return; } - if let Some(existing) = existing.and_then(|existing| self.get(existing.type_id)) { + if let Some(existing) = existing.and_then(|existing| self.get_mut(existing.type_id)) { + existing.aliases.remove(&alias); warn!( "overwrote alias `{alias}` — was assigned to type `{}` ({:?}), but is now assigned to type `{}` ({:?})", existing.type_name(), @@ -464,8 +465,8 @@ pub struct TypeRegistration { short_name: String, data: HashMap>, type_info: &'static TypeInfo, - aliases: &'static [&'static str], - deprecated_aliases: &'static [&'static str], + aliases: HashSet>, + deprecated_aliases: HashSet>, } impl TypeRegistration { @@ -516,8 +517,12 @@ impl TypeRegistration { data: HashMap::default(), short_name: bevy_utils::get_short_name(type_name), type_info: T::type_info(), - aliases: T::aliases(), - deprecated_aliases: T::deprecated_aliases(), + aliases: HashSet::from_iter(T::aliases().iter().map(|&alias| Cow::Borrowed(alias))), + deprecated_aliases: HashSet::from_iter( + T::deprecated_aliases() + .iter() + .map(|&alias| Cow::Borrowed(alias)), + ), } } @@ -538,15 +543,15 @@ impl TypeRegistration { /// Returns the default set of aliases for the type. /// /// For the set of _deprecated_ aliases, try [`deprecated_aliases`](Self::deprecated_aliases). - pub fn aliases(&self) -> &'static [&'static str] { - self.aliases + pub fn aliases(&self) -> &HashSet> { + &self.aliases } /// Returns the default set of _deprecated_ aliases for the type. /// /// For the set of _current_ aliases, try [`aliases`](Self::aliases). - pub fn deprecated_aliases(&self) -> &'static [&'static str] { - self.deprecated_aliases + pub fn deprecated_aliases(&self) -> &HashSet> { + &self.deprecated_aliases } } @@ -561,8 +566,8 @@ impl Clone for TypeRegistration { data, short_name: self.short_name.clone(), type_info: self.type_info, - aliases: self.aliases, - deprecated_aliases: self.deprecated_aliases, + aliases: self.aliases.clone(), + deprecated_aliases: self.deprecated_aliases.clone(), } } } @@ -802,16 +807,20 @@ mod test { registry.register::(); registry.register_alias::("my_alias"); - assert_eq!( - TypeId::of::(), - registry.get_with_alias("my_alias").unwrap().type_id() - ); + let registration = registry.get_with_alias("my_alias").unwrap(); + assert_eq!(TypeId::of::(), registration.type_id()); + assert!(registration.aliases().contains("my_alias")); registry.register_alias::("my_alias"); - assert_eq!( - TypeId::of::(), - registry.get_with_alias("my_alias").unwrap().type_id() - ); + let registration = registry.get_with_alias("my_alias").unwrap(); + assert_eq!(TypeId::of::(), registration.type_id()); + assert!(registration.aliases().contains("my_alias")); + + // Confirm that the registrations' aliases have been updated + let foo_registration = registry.get(TypeId::of::()).unwrap(); + let bar_registration = registry.get(TypeId::of::()).unwrap(); + assert!(!foo_registration.aliases().contains("my_alias")); + assert!(bar_registration.aliases().contains("my_alias")); } #[test] @@ -827,10 +836,14 @@ mod test { registry.register_alias::("my_alias"); registry.try_register_alias::("my_alias"); - assert_eq!( - TypeId::of::(), - registry.get_with_alias("my_alias").unwrap().type_id() - ); + let registration = registry.get_with_alias("my_alias").unwrap(); + assert_eq!(TypeId::of::(), registration.type_id()); + + // Confirm that the registrations' aliases have been updated + let foo_registration = registry.get(TypeId::of::()).unwrap(); + let bar_registration = registry.get(TypeId::of::()).unwrap(); + assert!(foo_registration.aliases().contains("my_alias")); + assert!(!bar_registration.aliases().contains("my_alias")); } #[test] From 68b7ceaad2f5cb454b530feb7c846e35f0b50dd2 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Aug 2022 20:32:04 -0700 Subject: [PATCH 03/14] Make alias methods consistent with existing methods --- crates/bevy_reflect/src/type_registry.rs | 37 +++++++++++++++++------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 7ebca613c3557..d391b0d6c18ee 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -317,15 +317,16 @@ impl TypeRegistry { /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_alias`](Self::overwrite_alias). - pub fn register_alias(&mut self, alias: impl Into>) { + pub fn register_alias(&mut self, alias: impl Into>) { + let alias = alias.into(); + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( - "cannot register an alias to non-existent type `{}`", - std::any::type_name::() + "attempted to call `TypeRegistry::register_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", + T = std::any::type_name::(), ) }); - let alias = alias.into(); registration.aliases.insert(alias.clone()); let type_name = registration.type_name(); @@ -338,7 +339,10 @@ impl TypeRegistry { /// /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or /// [`overwrite_alias`](Self::overwrite_alias). - pub fn try_register_alias(&mut self, alias: impl Into>) -> bool { + pub fn try_register_alias( + &mut self, + alias: impl Into>, + ) -> bool { let alias = alias.into(); if self.alias_to_id.contains_key(&alias) { @@ -346,8 +350,8 @@ impl TypeRegistry { } else { let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( - "cannot register an alias to non-existent type `{}`", - std::any::type_name::() + "attempted to call `TypeRegistry::try_register_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", + T = std::any::type_name::(), ) }); @@ -367,15 +371,16 @@ impl TypeRegistry { /// Unlike, [`register_alias`](Self::register_alias), this does not print a warning when overwriting existing aliases. /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). - pub fn overwrite_alias(&mut self, alias: impl Into>) { + pub fn overwrite_alias(&mut self, alias: impl Into>) { + let alias = alias.into(); + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( - "cannot register an alias to non-existent type `{}`", - std::any::type_name::() + "attempted to call `TypeRegistry::overwrite_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", + T = std::any::type_name::(), ) }); - let alias = alias.into(); registration.aliases.insert(alias.clone()); let type_name = registration.type_name(); @@ -846,6 +851,16 @@ mod test { assert!(!bar_registration.aliases().contains("my_alias")); } + #[test] + #[should_panic(expected = "attempted to call `TypeRegistry::register_alias` for type")] + fn register_alias_should_panic_if_no_registration() { + #[derive(Reflect)] + struct Foo; + + let mut registry = TypeRegistry::empty(); + registry.register_alias::("my_alias"); + } + #[test] fn test_property_type_registration() { assert_eq!( From 34a41dada49ca894f699ef0272d338d2ec487160 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Aug 2022 21:43:32 -0700 Subject: [PATCH 04/14] Added compile error for aliased generic types --- .../src/container_attributes.rs | 25 ++++++++++++------- .../bevy_reflect_derive/src/derive_data.rs | 16 +++++++++--- .../bevy_reflect_derive/src/reflect_value.rs | 10 +++++--- .../bevy_reflect_derive/src/utility.rs | 13 +++++++++- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 52d6ad5e50ee9..4a0e0a99daabc 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -8,7 +8,6 @@ use crate::utility; use proc_macro2::Ident; use quote::quote; -use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; @@ -114,7 +113,10 @@ pub(crate) struct ReflectTraits { impl ReflectTraits { /// Create a new [`ReflectTraits`] instance from a set of nested metas. - pub fn from_nested_metas(nested_metas: &Punctuated) -> syn::Result { + pub fn from_nested_metas( + nested_metas: &Punctuated, + is_generic: bool, + ) -> syn::Result { let mut traits = ReflectTraits::default(); for nested_meta in nested_metas.iter() { match nested_meta { @@ -165,15 +167,27 @@ impl ReflectTraits { .get_ident() .ok_or_else(|| syn::Error::new(pair.span(), "not a valid path"))?; + // Closure that handles defining an alias on a generic type + let try_handle_generic_alias = || -> syn::Result<()> { + if is_generic { + Err(syn::Error::new(ident.span(), "cannot specify non-generic aliases on generic types. Consider using `TypeRegistry::register_alias` instead")) + } else { + Ok(()) + } + }; + let attr_name = ident.to_string(); match (attr_name.as_str(), &pair.lit) { (ALIAS_ATTR, Lit::Str(alias)) => { + try_handle_generic_alias()?; traits.aliases.push(alias.clone()); } (DEPRECATED_ALIAS_ATTR, Lit::Str(alias)) => { + try_handle_generic_alias()?; traits.deprecated_aliases.push(alias.clone()); } (DEPRECATED_ALIAS_ATTR | ALIAS_ATTR, lit) => { + try_handle_generic_alias()?; return Err(syn::Error::new(lit.span(), "expected a string literal")); } (_, _) => { @@ -329,10 +343,3 @@ impl ReflectTraits { } } } - -impl Parse for ReflectTraits { - fn parse(input: ParseStream) -> syn::Result { - let result = Punctuated::::parse_terminated(input)?; - ReflectTraits::from_nested_metas(&result) - } -} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 9f04c582bcc94..ca48cf0c8cfa3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -110,6 +110,8 @@ impl<'a> ReflectDerive<'a> { // Should indicate whether `#[reflect_value]` was used let mut force_reflect_value = false; + let is_generic = utility::is_generic(&input.generics, false); + for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { let meta_list = if let Meta::List(meta_list) = attribute { meta_list @@ -121,16 +123,22 @@ impl<'a> ReflectDerive<'a> { if ident == REFLECT_ATTRIBUTE_NAME { if force_reflect_value { force_reflect_value = false; - traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; + traits = ReflectTraits::from_nested_metas(&meta_list.nested, is_generic)?; } else { - traits.combine(ReflectTraits::from_nested_metas(&meta_list.nested)?); + traits.combine(ReflectTraits::from_nested_metas( + &meta_list.nested, + is_generic, + )?); } } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { if !force_reflect_value { force_reflect_value = true; - traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; + traits = ReflectTraits::from_nested_metas(&meta_list.nested, is_generic)?; } else { - traits.combine(ReflectTraits::from_nested_metas(&meta_list.nested)?); + traits.combine(ReflectTraits::from_nested_metas( + &meta_list.nested, + is_generic, + )?); } } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index ec54b99a6f404..23a93823b3127 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,8 +1,9 @@ use crate::container_attributes::ReflectTraits; +use crate::utility; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; -use syn::token::{Paren, Where}; -use syn::{parenthesized, Generics}; +use syn::token::{Comma, Paren, Where}; +use syn::{parenthesized, Generics, NestedMeta}; /// A struct used to define a simple reflected value type (such as primitives). /// @@ -28,6 +29,8 @@ impl Parse for ReflectValueDef { fn parse(input: ParseStream) -> syn::Result { let type_ident = input.parse::()?; let generics = input.parse::()?; + let is_generic = utility::is_generic(&generics, false); + let mut lookahead = input.lookahead1(); let mut where_clause = None; if lookahead.peek(Where) { @@ -39,7 +42,8 @@ impl Parse for ReflectValueDef { if lookahead.peek(Paren) { let content; parenthesized!(content in input); - traits = Some(content.parse::()?); + let meta = content.parse_terminated::(NestedMeta::parse)?; + traits = Some(ReflectTraits::from_nested_metas(&meta, is_generic)?); } Ok(ReflectValueDef { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index f8894689a3858..ff494641fdcd4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,7 +2,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; -use syn::{Member, Path}; +use syn::{Generics, Member, Path}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -96,3 +96,14 @@ impl ResultSifter { } } } + +/// Returns true if the given [`Generics`] contains generic information. +/// +/// If `include_lifetimes` is false, this does not count lifetime arguments. +pub(crate) fn is_generic(generics: &Generics, include_lifetimes: bool) -> bool { + if include_lifetimes { + generics.params.len() - generics.lifetimes().count() > 0 + } else { + !generics.params.is_empty() + } +} From f2fe100d8c4aa6a4887bf6abf8b4c5c4752e09b9 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:07:02 -0700 Subject: [PATCH 05/14] Apply suggestions from code review Co-authored-by: Afonso Lage --- crates/bevy_reflect/src/type_registry.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index d391b0d6c18ee..627b9d801d3c8 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -39,7 +39,7 @@ pub trait GetTypeRegistration { /// Returns the static set of aliases that can be used to refer to this type. /// - /// Note that these are the _default_ aliases— used specifically for type registration. + /// Note that these are the _default_ aliases used specifically for type registration. /// For a given [registry], the actual set of aliases for a registered type may differ from the /// ones listed here. /// @@ -54,7 +54,7 @@ pub trait GetTypeRegistration { /// /// For the list of _current_ aliases, try using [`aliases`] instead. /// - /// Note that, like [`aliases`], this is the _default_ set— used specifically for type registration. + /// Note that, like [`aliases`], this is the _default_ set used specifically for type registration. /// For a given [registry], the actual set of deprecated aliases for a registered type may differ from the /// ones listed here. /// @@ -425,8 +425,8 @@ impl TypeRegistry { /// Prints a warning stating that the given alias has been deprecated for the given registration. fn warn_alias_deprecation(alias: &str, registration: &TypeRegistration) { warn!( - "the alias `{}` has been deprecated for the type `{}` ({:?}). Consider using the full type name or \ - one of the current aliases: {:?}", + "the alias `{}` has been deprecated for the type `{}` ({:?}) and may be removed in the future. \ + Consider using the full type name or one of the current aliases: {:?}", alias, registration.type_name(), registration.type_id(), From 62916d78123a321c261715e2cc5407e9bef3978b Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 29 Aug 2022 23:13:40 -0700 Subject: [PATCH 06/14] Update crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- .../bevy_reflect_derive/src/container_attributes.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 4a0e0a99daabc..91933ae700e0f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -221,11 +221,9 @@ impl ReflectTraits { } for ident in other.idents { - if self.idents.contains(&ident) { - continue; + if !self.idents.contains(&ident) { + self.idents.push(ident); } - - self.idents.push(ident); } for alias in other.aliases { From e0aabe1b75b90f1ac7e2fcb73818e6295224083b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 29 Aug 2022 23:23:51 -0700 Subject: [PATCH 07/14] Small refactor --- .../bevy_reflect_derive/src/container_attributes.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 91933ae700e0f..a074e468ac32e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -231,12 +231,10 @@ impl ReflectTraits { if self .aliases .iter() - .any(|other_alias| value == other_alias.value()) + .all(|other_alias| value != other_alias.value()) { - continue; + self.aliases.push(alias); } - - self.aliases.push(alias); } for alias in other.deprecated_aliases { @@ -244,12 +242,10 @@ impl ReflectTraits { if self .deprecated_aliases .iter() - .any(|other_alias| value == other_alias.value()) + .all(|other_alias| value != other_alias.value()) { - continue; + self.deprecated_aliases.push(alias); } - - self.deprecated_aliases.push(alias); } } From 1f8e866ff20ed6a2906efab901316e33b0b69165 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 00:01:31 -0700 Subject: [PATCH 08/14] Refactor alias registration methods --- crates/bevy_reflect/src/type_registry.rs | 203 ++++++++++++++--------- 1 file changed, 127 insertions(+), 76 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 627b9d801d3c8..b149e13919dbe 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -317,53 +317,28 @@ impl TypeRegistry { /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_alias`](Self::overwrite_alias). - pub fn register_alias(&mut self, alias: impl Into>) { - let alias = alias.into(); - - let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { - panic!( - "attempted to call `TypeRegistry::register_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", - T = std::any::type_name::(), - ) - }); - - registration.aliases.insert(alias.clone()); - - let type_name = registration.type_name(); - let type_id = registration.type_id(); - - self.register_alias_internal(alias, type_name, type_id, false, true); + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + pub fn register_alias( + &mut self, + alias: impl Into>, + ) -> Option { + let registerer = AliasRegisterer::implicit_overwrite(self, "TypeRegistry::register_alias"); + registerer.register::(alias, false) } /// Attempts to register an alias for the given type, `T`, if it isn't already in use. /// /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or /// [`overwrite_alias`](Self::overwrite_alias). + /// + /// If the given alias is already in use, then the [`TypeId`] of that type is returned. pub fn try_register_alias( &mut self, alias: impl Into>, - ) -> bool { - let alias = alias.into(); - - if self.alias_to_id.contains_key(&alias) { - false - } else { - let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { - panic!( - "attempted to call `TypeRegistry::try_register_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", - T = std::any::type_name::(), - ) - }); - - registration.aliases.insert(alias.clone()); - - let type_name = registration.type_name(); - let type_id = registration.type_id(); - - self.register_alias_internal(alias, type_name, type_id, false, false); - - true - } + ) -> Option { + let registerer = AliasRegisterer::no_overwrite(self, "TypeRegistry::try_register_alias"); + registerer.register::(alias, false) } /// Register an alias for the given type, `T`, explicitly overwriting existing aliases. @@ -371,22 +346,14 @@ impl TypeRegistry { /// Unlike, [`register_alias`](Self::register_alias), this does not print a warning when overwriting existing aliases. /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). - pub fn overwrite_alias(&mut self, alias: impl Into>) { - let alias = alias.into(); - - let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { - panic!( - "attempted to call `TypeRegistry::overwrite_alias` for type `{T}` with alias `{alias}` without registering `{T}` first", - T = std::any::type_name::(), - ) - }); - - registration.aliases.insert(alias.clone()); - - let type_name = registration.type_name(); - let type_id = registration.type_id(); - - self.register_alias_internal(alias, type_name, type_id, false, false); + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + pub fn overwrite_alias( + &mut self, + alias: impl Into>, + ) -> Option { + let registerer = AliasRegisterer::explicit_overwrite(self, "TypeRegistry::overwrite_alias"); + registerer.register::(alias, false) } /// Registers an alias for the given type. @@ -397,7 +364,7 @@ impl TypeRegistry { type_id: TypeId, is_deprecated: bool, should_warn: bool, - ) { + ) -> Option { let existing = self.alias_to_id.insert( alias.clone(), AliasData { @@ -406,19 +373,21 @@ impl TypeRegistry { }, ); - if !should_warn { - return; - } - if let Some(existing) = existing.and_then(|existing| self.get_mut(existing.type_id)) { existing.aliases.remove(&alias); - warn!( - "overwrote alias `{alias}` — was assigned to type `{}` ({:?}), but is now assigned to type `{}` ({:?})", - existing.type_name(), - existing.type_id(), - type_name, - type_id - ); + if should_warn { + warn!( + "overwrote alias `{alias}` — was assigned to type `{}` ({:?}), but is now assigned to type `{}` ({:?})", + existing.type_name(), + existing.type_id(), + type_name, + type_id + ); + } + + Some(existing.type_id()) + } else { + None } } @@ -447,12 +416,6 @@ impl TypeRegistryArc { } } -#[derive(Copy, Clone)] -struct AliasData { - pub type_id: TypeId, - pub is_deprecated: bool, -} - /// A record of data about a type. /// /// This contains the [`TypeInfo`] of the type, as well as its [short name]. @@ -577,6 +540,88 @@ impl Clone for TypeRegistration { } } +#[derive(Copy, Clone)] +struct AliasData { + pub type_id: TypeId, + pub is_deprecated: bool, +} + +/// A simple helper struct for registering type aliases. +struct AliasRegisterer<'a> { + registry: &'a mut TypeRegistry, + func_name: &'static str, + allow_overwrite: bool, + should_warn: bool, +} + +impl<'a> AliasRegisterer<'a> { + /// Configure the registerer to register aliases with an implicit overwrite (produces a warning). + fn implicit_overwrite(registry: &'a mut TypeRegistry, func_name: &'static str) -> Self { + Self { + registry, + func_name, + allow_overwrite: true, + should_warn: true, + } + } + + /// Configure the registerer to register aliases with an explicit overwrite (does not produce a warning). + fn explicit_overwrite(registry: &'a mut TypeRegistry, func_name: &'static str) -> Self { + Self { + registry, + func_name, + allow_overwrite: true, + should_warn: false, + } + } + + /// Configure the registerer to register aliases as long as they are not already in use. + fn no_overwrite(registry: &'a mut TypeRegistry, func_name: &'static str) -> Self { + Self { + registry, + func_name, + allow_overwrite: false, + should_warn: false, + } + } + + /// Register the given alias for type, `T`. + fn register( + self, + alias: impl Into>, + is_deprecated: bool, + ) -> Option { + let Self { + registry, + func_name, + allow_overwrite, + should_warn, + } = self; + + let alias = alias.into(); + + if !allow_overwrite { + if let Some(data) = registry.alias_to_id.get(&alias) { + return Some(data.type_id); + } + } + + let registration = registry.get_mut(TypeId::of::()).unwrap_or_else(|| { + panic!( + "attempted to call `{func_name}` for type `{T}` with alias `{alias}` without registering `{T}` first", + T = std::any::type_name::(), + ) + }); + + registration.aliases.insert(alias.clone()); + + let type_name = registration.type_name(); + let type_id = registration.type_id(); + + registry.register_alias_internal(alias, type_name, type_id, is_deprecated, should_warn) + } +} + /// A trait for types generated by the [`#[reflect_trait]`][0] attribute macro. /// /// [0]: crate::reflect_trait @@ -745,7 +790,7 @@ impl FromType for ReflectFromPtr { } #[cfg(test)] -mod test { +mod tests { use std::any::TypeId; use std::ptr::NonNull; @@ -811,12 +856,16 @@ mod test { registry.register::(); registry.register::(); - registry.register_alias::("my_alias"); + let previous = registry.register_alias::("my_alias"); + assert_eq!(None, previous); + let registration = registry.get_with_alias("my_alias").unwrap(); assert_eq!(TypeId::of::(), registration.type_id()); assert!(registration.aliases().contains("my_alias")); - registry.register_alias::("my_alias"); + let previous = registry.register_alias::("my_alias"); + assert_eq!(Some(TypeId::of::()), previous); + let registration = registry.get_with_alias("my_alias").unwrap(); assert_eq!(TypeId::of::(), registration.type_id()); assert!(registration.aliases().contains("my_alias")); @@ -840,7 +889,9 @@ mod test { registry.register::(); registry.register_alias::("my_alias"); - registry.try_register_alias::("my_alias"); + let current = registry.try_register_alias::("my_alias"); + assert_eq!(Some(TypeId::of::()), current); + let registration = registry.get_with_alias("my_alias").unwrap(); assert_eq!(TypeId::of::(), registration.type_id()); From 0cef3b4cea1c983de97a19f12f6d957aff882820 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 00:14:39 -0700 Subject: [PATCH 09/14] Add deprecated alias registration methods --- crates/bevy_reflect/src/type_registry.rs | 103 ++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index b149e13919dbe..e969940f00f40 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -327,6 +327,26 @@ impl TypeRegistry { registerer.register::(alias, false) } + /// Register a _deprecated_ alias for the given type, `T`. + /// + /// To register an alias that isn't marked as deprecated, use [`register_alias`](Self::register_alias). + /// + /// This will implicitly overwrite existing usages of the given alias + /// and print a warning to the console if it does so. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). + /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + pub fn register_deprecated_alias( + &mut self, + alias: impl Into>, + ) -> Option { + let registerer = + AliasRegisterer::implicit_overwrite(self, "TypeRegistry::register_deprecated_alias"); + registerer.register::(alias, true) + } + /// Attempts to register an alias for the given type, `T`, if it isn't already in use. /// /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or @@ -341,6 +361,23 @@ impl TypeRegistry { registerer.register::(alias, false) } + /// Attempts to register a _deprecated_ alias for the given type, `T`, if it isn't already in use. + /// + /// To try and register an alias that isn't marked as deprecated, use [`try_register_alias`](Self::try_register_alias). + /// + /// To register the alias whether or not it exists, try using either [`register_deprecated_alias`](Self::register_deprecated_alias) or + /// [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). + /// + /// If the given alias is already in use, then the [`TypeId`] of that type is returned. + pub fn try_register_deprecated_alias( + &mut self, + alias: impl Into>, + ) -> Option { + let registerer = + AliasRegisterer::no_overwrite(self, "TypeRegistry::try_register_deprecated_alias"); + registerer.register::(alias, true) + } + /// Register an alias for the given type, `T`, explicitly overwriting existing aliases. /// /// Unlike, [`register_alias`](Self::register_alias), this does not print a warning when overwriting existing aliases. @@ -356,6 +393,24 @@ impl TypeRegistry { registerer.register::(alias, false) } + /// Register a _deprecated_ alias for the given type, `T`, explicitly overwriting existing aliases. + /// + /// To register an alias that isn't marked as deprecated, use [`overwrite_alias`](Self::overwrite_alias). + /// + /// Unlike, [`register_deprecated_alias`](Self::register_deprecated_alias), this does not print a warning when overwriting existing aliases. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + pub fn overwrite_deprecated_alias( + &mut self, + alias: impl Into>, + ) -> Option { + let registerer = + AliasRegisterer::explicit_overwrite(self, "TypeRegistry::overwrite_deprecated_alias"); + registerer.register::(alias, true) + } + /// Registers an alias for the given type. fn register_alias_internal( &mut self, @@ -375,6 +430,8 @@ impl TypeRegistry { if let Some(existing) = existing.and_then(|existing| self.get_mut(existing.type_id)) { existing.aliases.remove(&alias); + existing.deprecated_aliases.remove(&alias); + if should_warn { warn!( "overwrote alias `{alias}` — was assigned to type `{}` ({:?}), but is now assigned to type `{}` ({:?})", @@ -613,7 +670,11 @@ impl<'a> AliasRegisterer<'a> { ) }); - registration.aliases.insert(alias.clone()); + if is_deprecated { + registration.deprecated_aliases.insert(alias.clone()); + } else { + registration.aliases.insert(alias.clone()); + } let type_name = registration.type_name(); let type_id = registration.type_id(); @@ -845,6 +906,46 @@ mod tests { } } + #[test] + fn should_register_deprecated_alias() { + #[derive(Reflect)] + struct Foo; + #[derive(Reflect)] + struct Bar; + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register::(); + + let previous = registry.register_deprecated_alias::("my_deprecated_alias"); + assert_eq!(None, previous); + + let registration = registry.get_with_alias("my_deprecated_alias").unwrap(); + assert_eq!(TypeId::of::(), registration.type_id()); + assert!(registration + .deprecated_aliases() + .contains("my_deprecated_alias")); + + let previous = registry.register_deprecated_alias::("my_deprecated_alias"); + assert_eq!(Some(TypeId::of::()), previous); + + let registration = registry.get_with_alias("my_deprecated_alias").unwrap(); + assert_eq!(TypeId::of::(), registration.type_id()); + assert!(registration + .deprecated_aliases() + .contains("my_deprecated_alias")); + + // Confirm that the registrations' aliases have been updated + let foo_registration = registry.get(TypeId::of::()).unwrap(); + let bar_registration = registry.get(TypeId::of::()).unwrap(); + assert!(!foo_registration + .deprecated_aliases() + .contains("my_deprecated_alias")); + assert!(bar_registration + .deprecated_aliases() + .contains("my_deprecated_alias")); + } + #[test] fn should_register_new_alias() { #[derive(Reflect)] From 22b5e03377b1082f0ded7382d4c96c0873440fe1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 00:18:30 -0700 Subject: [PATCH 10/14] Add test for generic aliases --- crates/bevy_reflect/src/type_registry.rs | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index e969940f00f40..0cef4766cbcb8 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -946,6 +946,36 @@ mod tests { .contains("my_deprecated_alias")); } + #[test] + fn should_register_alias_for_generic() { + #[derive(Reflect)] + struct Foo(T); + + let mut registry = TypeRegistry::empty(); + registry.register::>(); + registry.register::>(); + + let previous = registry.register_alias::>("my_alias"); + assert_eq!(None, previous); + + let registration = registry.get_with_alias("my_alias").unwrap(); + assert_eq!(TypeId::of::>(), registration.type_id()); + assert!(registration.aliases().contains("my_alias")); + + let previous = registry.register_alias::>("my_alias"); + assert_eq!(Some(TypeId::of::>()), previous); + + let registration = registry.get_with_alias("my_alias").unwrap(); + assert_eq!(TypeId::of::>(), registration.type_id()); + assert!(registration.aliases().contains("my_alias")); + + // Confirm that the registrations' aliases have been updated + let foo_registration = registry.get(TypeId::of::>()).unwrap(); + let bar_registration = registry.get(TypeId::of::>()).unwrap(); + assert!(!foo_registration.aliases().contains("my_alias")); + assert!(bar_registration.aliases().contains("my_alias")); + } + #[test] fn should_register_new_alias() { #[derive(Reflect)] From ac1b222a6b33dae127991411565862403966fd9c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 00:22:34 -0700 Subject: [PATCH 11/14] Clean up doc comments --- crates/bevy_reflect/src/type_registry.rs | 27 ++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 0cef4766cbcb8..4a5bfd25aac89 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -316,7 +316,8 @@ impl TypeRegistry { /// and print a warning to the console if it does so. /// /// To register the alias only if it isn't already in use, try using [`try_register_alias`](Self::try_register_alias). - /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_alias`](Self::overwrite_alias). + /// Otherwise, to explicitly overwrite existing aliases without the warning, + /// try using [`overwrite_alias`](Self::overwrite_alias). /// /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. pub fn register_alias( @@ -331,11 +332,12 @@ impl TypeRegistry { /// /// To register an alias that isn't marked as deprecated, use [`register_alias`](Self::register_alias). /// - /// This will implicitly overwrite existing usages of the given alias - /// and print a warning to the console if it does so. + /// This will implicitly overwrite existing usages of the given alias and print a warning to the console if it does so. /// - /// To register the alias only if it isn't already in use, try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). - /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). + /// To register the alias only if it isn't already in use, + /// try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). + /// Otherwise, to explicitly overwrite existing aliases without the warning, + /// try using [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). /// /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. pub fn register_deprecated_alias( @@ -349,8 +351,8 @@ impl TypeRegistry { /// Attempts to register an alias for the given type, `T`, if it isn't already in use. /// - /// To register the alias whether or not it exists, try using either [`register_alias`](Self::register_alias) or - /// [`overwrite_alias`](Self::overwrite_alias). + /// To register the alias whether or not it exists, + /// try using either [`register_alias`](Self::register_alias) or [`overwrite_alias`](Self::overwrite_alias). /// /// If the given alias is already in use, then the [`TypeId`] of that type is returned. pub fn try_register_alias( @@ -365,8 +367,9 @@ impl TypeRegistry { /// /// To try and register an alias that isn't marked as deprecated, use [`try_register_alias`](Self::try_register_alias). /// - /// To register the alias whether or not it exists, try using either [`register_deprecated_alias`](Self::register_deprecated_alias) or - /// [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). + /// To register the alias whether or not it exists, + /// try using either [`register_deprecated_alias`](Self::register_deprecated_alias) + /// or [`overwrite_deprecated_alias`](Self::overwrite_deprecated_alias). /// /// If the given alias is already in use, then the [`TypeId`] of that type is returned. pub fn try_register_deprecated_alias( @@ -397,9 +400,11 @@ impl TypeRegistry { /// /// To register an alias that isn't marked as deprecated, use [`overwrite_alias`](Self::overwrite_alias). /// - /// Unlike, [`register_deprecated_alias`](Self::register_deprecated_alias), this does not print a warning when overwriting existing aliases. + /// Unlike, [`register_deprecated_alias`](Self::register_deprecated_alias), + /// this does not print a warning when overwriting existing aliases. /// - /// To register the alias only if it isn't already in use, try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). + /// To register the alias only if it isn't already in use, + /// try using [`try_register_deprecated_alias`](Self::try_register_deprecated_alias). /// /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. pub fn overwrite_deprecated_alias( From e8fbf7d666657cec73dba3b4a9221251e4d4a51e Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 00:40:01 -0700 Subject: [PATCH 12/14] Add warning to deserializer --- crates/bevy_reflect/src/serde/de.rs | 6 +++++- crates/bevy_reflect/src/type_registry.rs | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 9c3f66106ee41..953461859452a 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -145,7 +145,11 @@ impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { while let Some(key) = map.next_key::()? { match key.as_str() { type_fields::TYPE => { - type_name = Some(map.next_value()?); + let key = map.next_value::()?; + + self.registry.warn_on_alias_deprecation(&key); + + type_name = Some(key); } type_fields::MAP => { let _type_name = type_name diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 4a5bfd25aac89..8e7f0351d2384 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -221,7 +221,7 @@ impl TypeRegistry { let registration = self.get(alias_data.type_id)?; if alias_data.is_deprecated { - Self::warn_alias_deprecation(alias, registration); + Self::warn_alias_deprecation_internal(alias, registration); } Some(registration) @@ -236,7 +236,7 @@ impl TypeRegistry { let registration = self.get_mut(alias_data.type_id)?; if alias_data.is_deprecated { - Self::warn_alias_deprecation(alias, registration); + Self::warn_alias_deprecation_internal(alias, registration); } Some(registration) @@ -453,8 +453,19 @@ impl TypeRegistry { } } + /// If the given alias exists in the registry, prints a warning if it's deprecated. + pub(crate) fn warn_on_alias_deprecation(&self, alias: &str) { + if let Some(data) = self.alias_to_id.get(alias) { + if data.is_deprecated { + if let Some(registration) = self.get(data.type_id) { + Self::warn_alias_deprecation_internal(alias, registration); + } + } + } + } + /// Prints a warning stating that the given alias has been deprecated for the given registration. - fn warn_alias_deprecation(alias: &str, registration: &TypeRegistration) { + fn warn_alias_deprecation_internal(alias: &str, registration: &TypeRegistration) { warn!( "the alias `{}` has been deprecated for the type `{}` ({:?}) and may be removed in the future. \ Consider using the full type name or one of the current aliases: {:?}", From b6c6ead09fd6a6c292092e0f51f98e81d293b83a Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Aug 2022 01:01:16 -0700 Subject: [PATCH 13/14] Add alias registration methods to App --- crates/bevy_app/src/app.rs | 150 +++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 7d6c027da0cdf..fa48b6ef86e2c 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -12,6 +12,7 @@ use bevy_ecs::{ world::World, }; use bevy_utils::{tracing::debug, HashMap}; +use std::borrow::Cow; use std::fmt::Debug; #[cfg(feature = "trace")] @@ -923,6 +924,155 @@ impl App { self } + /// Register an alias for the given type, `T`, in the [`TypeRegistry`] resource. + /// + /// This will implicitly overwrite existing usages of the given alias and print a warning to the console if it does so. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_type_alias`]. + /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_type_alias`]. + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + /// + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`try_register_type_alias`]: Self::try_register_type_alias + /// [`overwrite_type_alias`]: Self::overwrite_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn register_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().register_alias::(alias); + } + self + } + + /// Register a _deprecated_ alias for the given type, `T`, in the [`TypeRegistry`] resource. + /// + /// To register an alias that isn't marked as deprecated, use [`register_type_alias`]. + /// + /// This will implicitly overwrite existing usages of the given alias and print a warning to the console if it does so. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_type_alias`]. + /// Otherwise, to explicitly overwrite existing aliases without the warning, try using [`overwrite_type_alias`]. + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + /// + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`register_type_alias`]: Self::register_type_alias + /// [`try_register_type_alias`]: Self::try_register_type_alias + /// [`overwrite_type_alias`]: Self::overwrite_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn register_deprecated_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().register_deprecated_alias::(alias); + } + self + } + + /// Attempts to register an alias for the given type, `T`, in the [`TypeRegistry`] resource if it isn't already in use. + /// + /// To register the alias whether or not it exists, try using either [`register_type_alias`] or [`overwrite_type_alias`]. + /// + /// If the given alias is already in use, then the [`TypeId`] of that type is returned. + /// + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`register_type_alias`]: Self::register_type_alias + /// [`overwrite_type_alias`]: Self::overwrite_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn try_register_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().try_register_alias::(alias); + } + self + } + + /// Attempts to register a _deprecated_ alias for the given type, `T`, in the [`TypeRegistry`] resource if it isn't already in use. + /// + /// To try and register an alias that isn't marked as deprecated, use [`try_register_type_alias`]. + /// + /// To register the alias whether or not it exists, try using either [`register_deprecated_type_alias`] or [`overwrite_deprecated_type_alias`]. + /// + /// If the given alias is already in use, then the [`TypeId`] of that type is returned. + /// + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`try_register_type_alias`]: Self::try_register_type_alias + /// [`register_deprecated_type_alias`]: Self::register_deprecated_type_alias + /// [`overwrite_deprecated_type_alias`]: Self::overwrite_deprecated_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn try_register_deprecated_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().try_register_deprecated_alias::(alias); + } + self + } + + /// Register an alias for the given type, `T`, in the [`TypeRegistry`] resource, explicitly overwriting existing aliases. + /// + /// Unlike, [`register_type_alias`], this does not print a warning when overwriting existing aliases. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_type_alias`]. + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`register_type_alias`]: Self::register_type_alias + /// [`try_register_type_alias`]: Self::try_register_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn overwrite_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().overwrite_alias::(alias); + } + self + } + + /// Register a _deprecated_ alias for the given type, `T`, in the [`TypeRegistry`] resource, explicitly overwriting existing aliases. + /// + /// To register an alias that isn't marked as deprecated, use [`overwrite_type_alias`]. + /// + /// Unlike, [`register_type_alias`], this does not print a warning when overwriting existing aliases. + /// + /// To register the alias only if it isn't already in use, try using [`try_register_type_alias`]. + /// + /// If an alias was overwritten, then the [`TypeId`] of the previous type is returned. + /// [`TypeRegistry`]: bevy_reflect::TypeRegistry + /// [`overwrite_type_alias`]: Self::overwrite_type_alias + /// [`register_type_alias`]: Self::register_type_alias + /// [`try_register_type_alias`]: Self::try_register_type_alias + /// [`TypeId`]: std::any::TypeId + #[cfg(feature = "bevy_reflect")] + pub fn overwrite_deprecated_type_alias( + &mut self, + alias: impl Into>, + ) -> &mut Self { + { + let registry = self.world.resource_mut::(); + registry.write().overwrite_deprecated_alias::(alias); + } + self + } + /// Adds an [`App`] as a child of the current one. /// /// The provided function `f` is called by the [`update`](Self::update) method. The [`World`] From 8390c902f660e67a7b98ed579e56430fc4759a6e Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Wed, 31 Aug 2022 00:09:51 -0700 Subject: [PATCH 14/14] Update crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs Co-authored-by: Afonso Lage --- .../bevy_reflect_derive/src/container_attributes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index a074e468ac32e..d111095a133c0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -170,7 +170,7 @@ impl ReflectTraits { // Closure that handles defining an alias on a generic type let try_handle_generic_alias = || -> syn::Result<()> { if is_generic { - Err(syn::Error::new(ident.span(), "cannot specify non-generic aliases on generic types. Consider using `TypeRegistry::register_alias` instead")) + Err(syn::Error::new(ident.span(), "alias attributes cannot be used on generic types. Consider using `TypeRegistry::register_alias` instead")) } else { Ok(()) }