Path: blob/main/crates/bevy_reflect/derive/src/where_clause_options.rs
6599 views
use crate::derive_data::ReflectMeta;1use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};2use indexmap::IndexSet;3use proc_macro2::{TokenStream, TokenTree};4use quote::{quote, ToTokens};5use syn::{punctuated::Punctuated, Ident, Token, Type, WhereClause};67/// Options defining how to extend the `where` clause for reflection.8pub(crate) struct WhereClauseOptions<'a, 'b> {9meta: &'a ReflectMeta<'b>,10active_types: IndexSet<Type>,11}1213impl<'a, 'b> WhereClauseOptions<'a, 'b> {14pub fn new(meta: &'a ReflectMeta<'b>) -> Self {15Self {16meta,17active_types: IndexSet::new(),18}19}2021pub fn new_with_types(meta: &'a ReflectMeta<'b>, active_types: IndexSet<Type>) -> Self {22Self { meta, active_types }23}2425pub fn meta(&self) -> &'a ReflectMeta<'b> {26self.meta27}2829/// Extends the `where` clause for a type with additional bounds needed for the reflection30/// impls.31///32/// The default bounds added are as follows:33/// - `Self` has:34/// - `Any + Send + Sync` bounds, if generic over types35/// - An `Any` bound, if generic over lifetimes but not types36/// - No bounds, if generic over neither types nor lifetimes37/// - Any given bounds in a `where` clause on the type38/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is39/// present40/// - Active fields with non-generic types have the bounds `TypePath`, either `PartialReflect`41/// if `#[reflect(from_reflect = false)]` is present or `FromReflect` otherwise,42/// `MaybeTyped`, and `RegisterForReflection` (or no bounds at all if43/// `#[reflect(no_field_bounds)]` is present)44///45/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are46/// added as well.47///48/// # Example49///50/// ```ignore (bevy_reflect is not accessible from this crate)51/// #[derive(Reflect)]52/// struct Foo<T, U> {53/// a: T,54/// #[reflect(ignore)]55/// b: U56/// }57/// ```58///59/// Generates the following where clause:60///61/// ```ignore (bevy_reflect is not accessible from this crate)62/// where63/// // `Self` bounds:64/// Foo<T, U>: Any + Send + Sync,65/// // Type parameter bounds:66/// T: TypePath,67/// U: TypePath,68/// // Active non-generic field bounds69/// T: FromReflect + TypePath + MaybeTyped + RegisterForReflection,70///71/// ```72///73/// If we add various things to the type:74///75/// ```ignore (bevy_reflect is not accessible from this crate)76/// #[derive(Reflect)]77/// #[reflect(where T: MyTrait)]78/// #[reflect(no_field_bounds)]79/// struct Foo<T, U>80/// where T: Clone81/// {82/// a: T,83/// #[reflect(ignore)]84/// b: U85/// }86/// ```87///88/// It will instead generate the following where clause:89///90/// ```ignore (bevy_reflect is not accessible from this crate)91/// where92/// // `Self` bounds:93/// Foo<T, U>: Any + Send + Sync,94/// // Given bounds:95/// T: Clone,96/// // Type parameter bounds:97/// T: TypePath,98/// U: TypePath,99/// // No active non-generic field bounds100/// // Custom bounds101/// T: MyTrait,102/// ```103pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {104let mut generic_where_clause = quote! { where };105106// Bounds on `Self`. We would normally just use `Self`, but that won't work for generating107// things like assertion functions and trait impls for a type's reference (e.g. `impl108// FromArg for &MyType`).109let generics = self.meta.type_path().generics();110if generics.type_params().next().is_some() {111// Generic over types? We need `Any + Send + Sync`.112let this = self.meta.type_path().true_type();113generic_where_clause.extend(quote! { #this: #FQAny + #FQSend + #FQSync, });114} else if generics.lifetimes().next().is_some() {115// Generic only over lifetimes? We need `'static`.116let this = self.meta.type_path().true_type();117generic_where_clause.extend(quote! { #this: 'static, });118}119120// Maintain existing where clause bounds, if any.121if let Some(where_clause) = where_clause {122let predicates = where_clause.predicates.iter();123generic_where_clause.extend(quote! { #(#predicates,)* });124}125126// Add additional reflection trait bounds.127let predicates = self.predicates();128generic_where_clause.extend(quote! {129#predicates130});131132generic_where_clause133}134135/// Returns an iterator the where clause predicates to extended the where clause with.136fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {137let mut predicates = Punctuated::new();138139if let Some(type_param_predicates) = self.type_param_predicates() {140predicates.extend(type_param_predicates);141}142143if let Some(field_predicates) = self.active_field_predicates() {144predicates.extend(field_predicates);145}146147if let Some(custom_where) = self.meta.attrs().custom_where() {148predicates.push(custom_where.predicates.to_token_stream());149}150151predicates152}153154/// Returns an iterator over the where clause predicates for the type parameters155/// if they require one.156fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {157self.type_path_bound().map(|type_path_bound| {158self.meta159.type_path()160.generics()161.type_params()162.map(move |param| {163let ident = ¶m.ident;164165quote!(#ident : #type_path_bound)166})167})168}169170/// Returns an iterator over the where clause predicates for the active fields.171fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {172if self.meta.attrs().no_field_bounds() {173None174} else {175let bevy_reflect_path = self.meta.bevy_reflect_path();176let reflect_bound = self.reflect_bound();177178// Get the identifiers of all type parameters.179let type_param_idents = self180.meta181.type_path()182.generics()183.type_params()184.map(|type_param| type_param.ident.clone())185.collect::<Vec<Ident>>();186187// Do any of the identifiers in `idents` appear in `token_stream`?188fn is_any_ident_in_token_stream(idents: &[Ident], token_stream: TokenStream) -> bool {189for token_tree in token_stream {190match token_tree {191TokenTree::Ident(ident) => {192if idents.contains(&ident) {193return true;194}195}196TokenTree::Group(group) => {197if is_any_ident_in_token_stream(idents, group.stream()) {198return true;199}200}201TokenTree::Punct(_) | TokenTree::Literal(_) => {}202}203}204false205}206207Some(self.active_types.iter().filter_map(move |ty| {208// Field type bounds are only required if `ty` is generic. How to determine that?209// Search `ty`s token stream for identifiers that match the identifiers from the210// function's type params. E.g. if `T` and `U` are the type param identifiers and211// `ty` is `Vec<[T; 4]>` then the `T` identifiers match. This is a bit hacky, but212// it works.213let is_generic =214is_any_ident_in_token_stream(&type_param_idents, ty.to_token_stream());215216is_generic.then(|| {217quote!(218#ty: #reflect_bound219// Needed to construct `NamedField` and `UnnamedField` instances for220// the `Typed` impl.221+ #bevy_reflect_path::TypePath222// Needed for `Typed` impls223+ #bevy_reflect_path::MaybeTyped224// Needed for registering type dependencies in the225// `GetTypeRegistration` impl.226+ #bevy_reflect_path::__macro_exports::RegisterForReflection227)228})229}))230}231}232233/// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.234fn reflect_bound(&self) -> TokenStream {235let bevy_reflect_path = self.meta.bevy_reflect_path();236237if self.meta.from_reflect().should_auto_derive() {238quote!(#bevy_reflect_path::FromReflect)239} else {240quote!(#bevy_reflect_path::PartialReflect)241}242}243244/// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.245fn type_path_bound(&self) -> Option<TokenStream> {246if self.meta.type_path_attrs().should_auto_derive() {247let bevy_reflect_path = self.meta.bevy_reflect_path();248Some(quote!(#bevy_reflect_path::TypePath))249} else {250None251}252}253}254255256