bevy_reflect_derive/
where_clause_options.rs

1use crate::derive_data::ReflectMeta;
2use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{punctuated::Punctuated, Token, Type, WhereClause};
6
7/// Options defining how to extend the `where` clause for reflection.
8pub(crate) struct WhereClauseOptions<'a, 'b> {
9    meta: &'a ReflectMeta<'b>,
10    active_fields: Box<[Type]>,
11}
12
13impl<'a, 'b> WhereClauseOptions<'a, 'b> {
14    pub fn new(meta: &'a ReflectMeta<'b>) -> Self {
15        Self {
16            meta,
17            active_fields: Box::new([]),
18        }
19    }
20
21    pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self {
22        Self {
23            meta,
24            active_fields,
25        }
26    }
27
28    /// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
29    ///
30    /// The default bounds added are as follows:
31    /// - `Self` has the bounds `Any + Send + Sync`
32    /// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
33    /// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
34    ///   or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
35    ///
36    /// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
37    ///
38    /// # Example
39    ///
40    /// ```ignore (bevy_reflect is not accessible from this crate)
41    /// #[derive(Reflect)]
42    /// struct Foo<T, U> {
43    ///   a: T,
44    ///   #[reflect(ignore)]
45    ///   b: U
46    /// }
47    /// ```
48    ///
49    /// Generates the following where clause:
50    ///
51    /// ```ignore (bevy_reflect is not accessible from this crate)
52    /// where
53    ///   // `Self` bounds:
54    ///   Self: Any + Send + Sync,
55    ///   // Type parameter bounds:
56    ///   T: TypePath,
57    ///   U: TypePath,
58    ///   // Field bounds
59    ///   T: FromReflect + TypePath,
60    /// ```
61    ///
62    /// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
63    ///
64    /// ```ignore (bevy_reflect is not accessible from this crate)
65    /// where
66    ///   // `Self` bounds:
67    ///   Self: Any + Send + Sync,
68    ///   // Type parameter bounds:
69    ///   T: TypePath,
70    ///   U: TypePath,
71    ///   // Field bounds
72    ///   T: FromReflect + TypePath,
73    ///   // Custom bounds
74    ///   T: MyTrait,
75    /// ```
76    ///
77    /// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
78    ///
79    /// ```ignore (bevy_reflect is not accessible from this crate)
80    /// where
81    ///   // `Self` bounds:
82    ///   Self: Any + Send + Sync,
83    ///   // Type parameter bounds:
84    ///   T: TypePath,
85    ///   U: TypePath,
86    ///   // Custom bounds
87    ///   T: MyTrait,
88    /// ```
89    pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
90        // We would normally just use `Self`, but that won't work for generating things like assertion functions
91        // and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
92        let this = self.meta.type_path().true_type();
93
94        let required_bounds = self.required_bounds();
95
96        // Maintain existing where clause, if any.
97        let mut generic_where_clause = if let Some(where_clause) = where_clause {
98            let predicates = where_clause.predicates.iter();
99            quote! {where #this: #required_bounds, #(#predicates,)*}
100        } else {
101            quote!(where #this: #required_bounds,)
102        };
103
104        // Add additional reflection trait bounds
105        let predicates = self.predicates();
106        generic_where_clause.extend(quote! {
107            #predicates
108        });
109
110        generic_where_clause
111    }
112
113    /// Returns an iterator the where clause predicates to extended the where clause with.
114    fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {
115        let mut predicates = Punctuated::new();
116
117        if let Some(type_param_predicates) = self.type_param_predicates() {
118            predicates.extend(type_param_predicates);
119        }
120
121        if let Some(field_predicates) = self.active_field_predicates() {
122            predicates.extend(field_predicates);
123        }
124
125        if let Some(custom_where) = self.meta.attrs().custom_where() {
126            predicates.push(custom_where.predicates.to_token_stream());
127        }
128
129        predicates
130    }
131
132    /// Returns an iterator over the where clause predicates for the type parameters
133    /// if they require one.
134    fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
135        self.type_path_bound().map(|type_path_bound| {
136            self.meta
137                .type_path()
138                .generics()
139                .type_params()
140                .map(move |param| {
141                    let ident = &param.ident;
142
143                    quote!(#ident : #type_path_bound)
144                })
145        })
146    }
147
148    /// Returns an iterator over the where clause predicates for the active fields.
149    fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
150        if self.meta.attrs().no_field_bounds() {
151            None
152        } else {
153            let bevy_reflect_path = self.meta.bevy_reflect_path();
154            let reflect_bound = self.reflect_bound();
155
156            // `TypePath` is always required for active fields since they are used to
157            // construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
158            // Likewise, `GetTypeRegistration` is always required for active fields since
159            // they are used to register the type's dependencies.
160            Some(self.active_fields.iter().map(move |ty| {
161                quote!(
162                    #ty : #reflect_bound
163                        + #bevy_reflect_path::TypePath
164                        // Needed for `Typed` impls
165                        + #bevy_reflect_path::MaybeTyped
166                        // Needed for `GetTypeRegistration` impls
167                        + #bevy_reflect_path::__macro_exports::RegisterForReflection
168                )
169            }))
170        }
171    }
172
173    /// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.
174    fn reflect_bound(&self) -> TokenStream {
175        let bevy_reflect_path = self.meta.bevy_reflect_path();
176
177        if self.meta.from_reflect().should_auto_derive() {
178            quote!(#bevy_reflect_path::FromReflect)
179        } else {
180            quote!(#bevy_reflect_path::PartialReflect)
181        }
182    }
183
184    /// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.
185    fn type_path_bound(&self) -> Option<TokenStream> {
186        if self.meta.type_path_attrs().should_auto_derive() {
187            let bevy_reflect_path = self.meta.bevy_reflect_path();
188            Some(quote!(#bevy_reflect_path::TypePath))
189        } else {
190            None
191        }
192    }
193
194    /// The minimum required bounds for a type to be reflected.
195    fn required_bounds(&self) -> TokenStream {
196        quote!(#FQAny + #FQSend + #FQSync)
197    }
198}