Skip to content

Commit 1edf1bd

Browse files
Fix #[derive(QueryData)] on empty structs (#23930)
# Objective - Allow `#[derive(QueryData)]` on structs with no fields. ## Solution - The derive fails for (non-unit) structs with no fields because the lifetime parameters of the generated structs are unused. Unit structs use a type alias instead of creating a new struct, avoiding the unused lifetime problem. Use type aliases for structs with no fields. ## Testing - Added regression test - Ran `cargo run -p ci -- compile`
1 parent 9c09a3b commit 1edf1bd

3 files changed

Lines changed: 72 additions & 6 deletions

File tree

crates/bevy_ecs/macros/src/query_data.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,21 @@ fn contiguous_item_struct(
6060
};
6161

6262
match fields {
63-
Fields::Named(_) => quote! {
63+
Fields::Named(_) if !fields.is_empty() => quote! {
6464
#derive_macro_call
6565
#item_attrs
6666
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state {
6767
#(#(#field_attrs)* #field_visibilities #field_members: <#field_types as #path::query::ContiguousQueryData>::Contiguous<'__w, '__s>,)*
6868
}
6969
},
70-
Fields::Unnamed(_) => quote! {
70+
Fields::Unnamed(_) if !fields.is_empty() => quote! {
7171
#derive_macro_call
7272
#item_attrs
7373
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state(
7474
#( #field_visibilities <#field_types as #path::query::ContiguousQueryData>::Contiguous<'__w, '__s>, )*
7575
) #user_where_clauses_with_world_and_state;
7676
},
77-
Fields::Unit => quote! {
77+
Fields::Unit | Fields::Named(_) | Fields::Unnamed(_) => quote! {
7878
#item_attrs
7979
#visibility type #item_struct_name #user_ty_generics_with_world_and_state = #struct_name #user_ty_generics;
8080
},

crates/bevy_ecs/macros/src/world_query.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ pub(crate) fn item_struct(
3030
};
3131

3232
match fields {
33-
Fields::Named(_) => quote! {
33+
Fields::Named(_) if !fields.is_empty() => quote! {
3434
#derive_macro_call
3535
#item_attrs
3636
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state {
3737
#(#(#field_attrs)* #field_visibilities #field_members: <#field_types as #path::query::QueryData>::Item<'__w, '__s>,)*
3838
}
3939
},
40-
Fields::Unnamed(_) => quote! {
40+
Fields::Unnamed(_) if !fields.is_empty() => quote! {
4141
#derive_macro_call
4242
#item_attrs
4343
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state(
4444
#( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w, '__s>, )*
4545
) #user_where_clauses_with_world_and_state;
4646
},
47-
Fields::Unit => quote! {
47+
Fields::Unit | Fields::Named(_) | Fields::Unnamed(_) => quote! {
4848
#item_attrs
4949
#visibility type #item_struct_name #user_ty_generics_with_world_and_state = #struct_name #user_ty_generics;
5050
},

crates/bevy_ecs/src/query/mod.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,4 +852,70 @@ mod tests {
852852
let _ = world.query::<QueryDataA>().contiguous_iter_mut(&mut world);
853853
let _ = world.query::<QueryDataB<D>>().contiguous_iter(&world);
854854
}
855+
856+
// regression test for https://github.com/bevyengine/bevy/pull/23930
857+
#[test]
858+
fn query_data_derive_empty() {
859+
#[derive(QueryData)]
860+
struct QueryDataA {}
861+
862+
#[derive(QueryData)]
863+
struct QueryDataB();
864+
865+
#[derive(QueryData)]
866+
#[query_data(mutable)]
867+
struct QueryDataC {}
868+
869+
#[derive(QueryData)]
870+
#[query_data(mutable)]
871+
struct QueryDataD();
872+
873+
#[derive(QueryData)]
874+
#[query_data(contiguous(immutable))]
875+
struct QueryDataE {}
876+
877+
#[derive(QueryData)]
878+
#[query_data(contiguous(immutable))]
879+
struct QueryDataF();
880+
881+
#[derive(QueryData)]
882+
#[query_data(mutable, contiguous(immutable))]
883+
struct QueryDataG {}
884+
885+
#[derive(QueryData)]
886+
#[query_data(mutable, contiguous(immutable))]
887+
struct QueryDataH();
888+
889+
#[derive(QueryData)]
890+
#[query_data(contiguous(mutable))]
891+
struct QueryDataI {}
892+
893+
#[derive(QueryData)]
894+
#[query_data(contiguous(mutable))]
895+
struct QueryDataJ();
896+
897+
#[derive(QueryData)]
898+
#[query_data(mutable, contiguous(mutable))]
899+
struct QueryDataK {}
900+
901+
#[derive(QueryData)]
902+
#[query_data(mutable, contiguous(mutable))]
903+
struct QueryDataL();
904+
905+
#[derive(QueryData)]
906+
#[query_data(contiguous(all))]
907+
struct QueryDataM {}
908+
909+
#[derive(QueryData)]
910+
#[query_data(contiguous(all))]
911+
struct QueryDataN();
912+
913+
#[derive(QueryData)]
914+
#[query_data(mutable, contiguous(all))]
915+
struct QueryDataO {}
916+
917+
#[derive(QueryData)]
918+
#[query_data(mutable, contiguous(all))]
919+
struct QueryDataP();
920+
}
855921
}

0 commit comments

Comments
 (0)