@@ -3,7 +3,6 @@ use proc_macro2::{Ident, Span};
3
3
use quote:: { quote, ToTokens } ;
4
4
use syn:: {
5
5
parse:: { Parse , ParseStream } ,
6
- parse_quote,
7
6
punctuated:: Punctuated ,
8
7
Attribute , Data , DataStruct , DeriveInput , Field , Fields , GenericArgument , GenericParam ,
9
8
ImplGenerics , Lifetime , LifetimeDef , Path , PathArguments , ReturnType , Token , Type ,
@@ -15,12 +14,12 @@ use crate::bevy_ecs_path;
15
14
#[ derive( Default ) ]
16
15
struct FetchStructAttributes {
17
16
pub mutable : bool ,
18
- pub read_only_derive_args : Punctuated < syn:: NestedMeta , syn:: token:: Comma > ,
17
+ pub derive_args : Punctuated < syn:: NestedMeta , syn:: token:: Comma > ,
19
18
}
20
19
21
20
pub static FILTER_ATTRIBUTE_NAME : & str = "filter" ;
22
21
static MUTABLE_ATTRIBUTE_NAME : & str = "mutable" ;
23
- static READ_ONLY_DERIVE_ATTRIBUTE_NAME : & str = "read_only_derive " ;
22
+ static DERIVE_ATTRIBUTE_NAME : & str = "derive " ;
24
23
25
24
mod field_attr_keywords {
26
25
syn:: custom_keyword!( ignore) ;
@@ -52,15 +51,15 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
52
51
MUTABLE_ATTRIBUTE_NAME
53
52
) ;
54
53
}
55
- } else if ident. map_or ( false , |ident| ident == READ_ONLY_DERIVE_ATTRIBUTE_NAME ) {
54
+ } else if ident. map_or ( false , |ident| ident == DERIVE_ATTRIBUTE_NAME ) {
56
55
if let syn:: Meta :: List ( meta_list) = meta {
57
56
fetch_struct_attributes
58
- . read_only_derive_args
57
+ . derive_args
59
58
. extend ( meta_list. nested . iter ( ) . cloned ( ) ) ;
60
59
} else {
61
60
panic ! (
62
61
"Expected a structured list within the `{}` attribute" ,
63
- READ_ONLY_DERIVE_ATTRIBUTE_NAME
62
+ DERIVE_ATTRIBUTE_NAME
64
63
) ;
65
64
}
66
65
} else {
@@ -77,7 +76,8 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
77
76
78
77
let FetchImplTokens {
79
78
struct_name,
80
- struct_name_read_only,
79
+ item_struct_name,
80
+ read_only_item_struct_name,
81
81
fetch_struct_name,
82
82
state_struct_name,
83
83
read_only_fetch_struct_name,
@@ -109,16 +109,10 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
109
109
panic ! ( "Expected a struct with a lifetime" ) ;
110
110
}
111
111
112
- let read_only_derive_macro_call = if fetch_struct_attributes. read_only_derive_args . is_empty ( ) {
112
+ let derive_macro_call = if fetch_struct_attributes. derive_args . is_empty ( ) {
113
113
quote ! { }
114
114
} else {
115
- if !fetch_struct_attributes. mutable {
116
- panic ! (
117
- "Attribute `{}` can only be be used for a struct marked with the `{}` attribute" ,
118
- READ_ONLY_DERIVE_ATTRIBUTE_NAME , MUTABLE_ATTRIBUTE_NAME
119
- ) ;
120
- }
121
- let derive_args = & fetch_struct_attributes. read_only_derive_args ;
115
+ let derive_args = & fetch_struct_attributes. derive_args ;
122
116
quote ! { #[ derive( #derive_args) ] }
123
117
} ;
124
118
@@ -137,11 +131,11 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
137
131
138
132
let struct_read_only_declaration = if fetch_struct_attributes. mutable {
139
133
quote ! {
140
- // TODO: it would be great to be able to dedup this by just deriving `Fetch ` again
134
+ // TODO: it would be great to be able to dedup this by just deriving `WorldQuery ` again
141
135
// without the `mutable` attribute, but we'd need a way to avoid creating a redundant
142
136
// `State` struct.
143
- #read_only_derive_macro_call
144
- struct #struct_name_read_only #impl_generics #where_clause {
137
+ #derive_macro_call
138
+ struct #read_only_item_struct_name #impl_generics #where_clause {
145
139
#( #( #field_attrs) * #field_visibilities #field_idents: <<#query_types as #path:: query:: WorldQuery >:: ReadOnlyFetch as #path:: query:: Fetch <#world_lifetime, #world_lifetime>>:: Item , ) *
146
140
#( #( #ignored_field_attrs) * #ignored_field_visibilities #ignored_field_idents: #ignored_field_types, ) *
147
141
}
@@ -152,7 +146,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
152
146
}
153
147
154
148
impl #fetch_impl_generics #path:: query:: Fetch <#fetch_trait_punctuated_lifetimes> for #read_only_fetch_struct_name #fetch_ty_generics #where_clause {
155
- type Item = #struct_name_read_only #ty_generics;
149
+ type Item = #read_only_item_struct_name #ty_generics;
156
150
type State = #state_struct_name #fetch_ty_generics;
157
151
158
152
unsafe fn init( _world: & #path:: world:: World , state: & Self :: State , _last_change_tick: u32 , _change_tick: u32 ) -> Self {
@@ -179,7 +173,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
179
173
/// SAFETY: we call `table_fetch` for each member that implements `Fetch`.
180
174
#[ inline]
181
175
unsafe fn table_fetch( & mut self , _table_row: usize ) -> Self :: Item {
182
- #struct_name_read_only {
176
+ Self :: Item {
183
177
#( #field_idents: self . #field_idents. table_fetch( _table_row) , ) *
184
178
#( #ignored_field_idents: Default :: default ( ) , ) *
185
179
}
@@ -188,22 +182,18 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
188
182
/// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`.
189
183
#[ inline]
190
184
unsafe fn archetype_fetch( & mut self , _archetype_index: usize ) -> Self :: Item {
191
- #struct_name_read_only {
185
+ Self :: Item {
192
186
#( #field_idents: self . #field_idents. archetype_fetch( _archetype_index) , ) *
193
187
#( #ignored_field_idents: Default :: default ( ) , ) *
194
188
}
195
189
}
196
190
}
197
191
198
- impl #impl_generics #path:: query:: WorldQuery for #struct_name_read_only #ty_generics #where_clause {
192
+ impl #impl_generics #path:: query:: WorldQuery for #read_only_item_struct_name #ty_generics #where_clause {
199
193
type Fetch = #read_only_fetch_struct_name #ty_generics;
200
194
type State = #state_struct_name #ty_generics;
201
195
type ReadOnlyFetch = #read_only_fetch_struct_name #ty_generics;
202
196
}
203
-
204
- impl #impl_generics #path:: query:: FetchedItem for #struct_name_read_only #ty_generics #where_clause {
205
- type Query = Self ;
206
- }
207
197
}
208
198
} else {
209
199
quote ! {
@@ -223,6 +213,12 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
223
213
} ;
224
214
225
215
let tokens = TokenStream :: from ( quote ! {
216
+ #derive_macro_call
217
+ struct #item_struct_name #impl_generics #where_clause {
218
+ #( #( #field_attrs) * #field_visibilities #field_idents: <<#query_types as #path:: query:: WorldQuery >:: Fetch as #path:: query:: Fetch <#world_lifetime, #world_lifetime>>:: Item , ) *
219
+ #( #( #ignored_field_attrs) * #ignored_field_visibilities #ignored_field_idents: #ignored_field_types, ) *
220
+ }
221
+
226
222
struct #fetch_struct_name #impl_generics #where_clause {
227
223
#( #field_idents: <#query_types as #path:: query:: WorldQuery >:: Fetch , ) *
228
224
#( #ignored_field_idents: #ignored_field_types, ) *
@@ -234,11 +230,11 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
234
230
}
235
231
236
232
impl #fetch_impl_generics #path:: query:: Fetch <#fetch_trait_punctuated_lifetimes> for #fetch_struct_name #fetch_ty_generics #where_clause {
237
- type Item = #struct_name #ty_generics;
233
+ type Item = #item_struct_name #ty_generics;
238
234
type State = #state_struct_name #fetch_ty_generics;
239
235
240
236
unsafe fn init( _world: & #path:: world:: World , state: & Self :: State , _last_change_tick: u32 , _change_tick: u32 ) -> Self {
241
- #fetch_struct_name {
237
+ Self {
242
238
#( #field_idents: <#fetch_init_types as #path:: query:: WorldQuery >:: Fetch :: init( _world, & state. #field_idents, _last_change_tick, _change_tick) , ) *
243
239
#( #ignored_field_idents: Default :: default ( ) , ) *
244
240
}
@@ -261,7 +257,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
261
257
/// SAFETY: we call `table_fetch` for each member that implements `Fetch`.
262
258
#[ inline]
263
259
unsafe fn table_fetch( & mut self , _table_row: usize ) -> Self :: Item {
264
- #struct_name {
260
+ Self :: Item {
265
261
#( #field_idents: self . #field_idents. table_fetch( _table_row) , ) *
266
262
#( #ignored_field_idents: Default :: default ( ) , ) *
267
263
}
@@ -270,7 +266,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
270
266
/// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`.
271
267
#[ inline]
272
268
unsafe fn archetype_fetch( & mut self , _archetype_index: usize ) -> Self :: Item {
273
- #struct_name {
269
+ Self :: Item {
274
270
#( #field_idents: self . #field_idents. archetype_fetch( _archetype_index) , ) *
275
271
#( #ignored_field_idents: Default :: default ( ) , ) *
276
272
}
@@ -311,20 +307,28 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
311
307
type ReadOnlyFetch = #read_only_fetch_struct_name #ty_generics;
312
308
}
313
309
314
- impl #impl_generics #path:: query:: FetchedItem for #struct_name #ty_generics #where_clause {
315
- type Query = Self ;
316
- }
317
-
318
310
/// SAFETY: each item in the struct is read only
319
311
unsafe impl #impl_generics #path:: query:: ReadOnlyFetch for #read_only_fetch_struct_name #ty_generics #where_clause { }
312
+
313
+ // The original struct will most likely be left unused. As we don't want our users having
314
+ // to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed
315
+ // workaround.
316
+ #[ allow( dead_code) ]
317
+ const _: ( ) = {
318
+ fn dead_code_workaround #impl_generics ( q: #struct_name #ty_generics) #where_clause {
319
+ #( q. #field_idents; ) *
320
+ #( q. #ignored_field_idents; ) *
321
+ }
322
+ } ;
320
323
} ) ;
321
324
tokens
322
325
}
323
326
324
327
pub fn derive_world_query_filter_impl ( ast : DeriveInput ) -> TokenStream {
325
328
let FetchImplTokens {
326
329
struct_name,
327
- struct_name_read_only : _,
330
+ item_struct_name : _,
331
+ read_only_item_struct_name : _,
328
332
fetch_struct_name,
329
333
state_struct_name,
330
334
read_only_fetch_struct_name : _,
@@ -446,11 +450,12 @@ pub fn derive_world_query_filter_impl(ast: DeriveInput) -> TokenStream {
446
450
tokens
447
451
}
448
452
449
- // This struct is used to share common tokens between `Fetch` and `FilterFetch` implementations.
453
+ // This struct is used to share common tokens between query and filter implementations.
450
454
struct FetchImplTokens < ' a > {
451
455
struct_name : Ident ,
452
- // Equals `struct_name` if `has_mutable_attr` is false.
453
- struct_name_read_only : Ident ,
456
+ item_struct_name : Ident ,
457
+ // Equals `item_struct_name` if `has_mutable_attr` is false.
458
+ read_only_item_struct_name : Ident ,
454
459
fetch_struct_name : Ident ,
455
460
state_struct_name : Ident ,
456
461
read_only_fetch_struct_name : Ident ,
@@ -505,10 +510,11 @@ fn fetch_impl_tokens<'a>(
505
510
let ( impl_generics, ty_generics, where_clause) = ast. generics . split_for_impl ( ) ;
506
511
507
512
let struct_name = ast. ident . clone ( ) ;
508
- let struct_name_read_only = if has_mutable_attr {
509
- Ident :: new ( & format ! ( "{}ReadOnly" , struct_name) , Span :: call_site ( ) )
513
+ let item_struct_name = Ident :: new ( & format ! ( "{}Item" , struct_name) , Span :: call_site ( ) ) ;
514
+ let read_only_item_struct_name = if has_mutable_attr {
515
+ Ident :: new ( & format ! ( "{}ReadOnlyItem" , struct_name) , Span :: call_site ( ) )
510
516
} else {
511
- ast . ident . clone ( )
517
+ item_struct_name . clone ( )
512
518
} ;
513
519
let fetch_struct_name = Ident :: new ( & format ! ( "{}Fetch" , struct_name) , Span :: call_site ( ) ) ;
514
520
let state_struct_name = Ident :: new ( & format ! ( "{}State" , struct_name) , Span :: call_site ( ) ) ;
@@ -569,7 +575,8 @@ fn fetch_impl_tokens<'a>(
569
575
570
576
FetchImplTokens {
571
577
struct_name,
572
- struct_name_read_only,
578
+ item_struct_name,
579
+ read_only_item_struct_name,
573
580
fetch_struct_name,
574
581
state_struct_name,
575
582
read_only_fetch_struct_name,
@@ -595,14 +602,13 @@ fn fetch_impl_tokens<'a>(
595
602
}
596
603
597
604
struct WorldQueryFieldInfo {
598
- /// We convert `Mut<T>` to `&mut T` (because this is the type that implements `WorldQuery`)
599
- /// and store it here.
605
+ /// The original field type.
600
606
query_type : Type ,
601
607
/// The same as `query_type` but with `'fetch` lifetime.
602
608
fetch_init_type : Type ,
603
609
/// Has `#[fetch(ignore)]` or `#[filter_fetch(ignore)]` attribute.
604
610
is_ignored : bool ,
605
- /// All field attributes except for `fetch` or `filter_fetch` .
611
+ /// All field attributes except for `world_query` ones .
606
612
attrs : Vec < Attribute > ,
607
613
}
608
614
@@ -612,8 +618,6 @@ fn read_world_query_field_info(
612
618
world_lifetime : & Lifetime ,
613
619
fetch_lifetime : & Lifetime ,
614
620
) -> WorldQueryFieldInfo {
615
- let path = bevy_ecs_path ( ) ;
616
-
617
621
let is_ignored = field
618
622
. attrs
619
623
. iter ( )
@@ -649,8 +653,7 @@ fn read_world_query_field_info(
649
653
} )
650
654
. collect ( ) ;
651
655
652
- let ty = & field. ty ;
653
- let query_type: Type = parse_quote ! ( <#ty as #path:: query:: FetchedItem >:: Query ) ;
656
+ let query_type: Type = field. ty . clone ( ) ;
654
657
let mut fetch_init_type: Type = query_type. clone ( ) ;
655
658
656
659
replace_lifetime_for_type ( & mut fetch_init_type, world_lifetime, fetch_lifetime) ;
0 commit comments