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 = ¶m.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}