bevy_ecs_macros/
lib.rs

1// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
2#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5extern crate proc_macro;
6
7mod component;
8mod query_data;
9mod query_filter;
10mod states;
11mod world_query;
12
13use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl};
14use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
15use proc_macro::TokenStream;
16use proc_macro2::Span;
17use proc_macro2::TokenStream as TokenStream2;
18use quote::{format_ident, quote};
19use syn::{
20    parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
21    ConstParam, DeriveInput, GenericParam, Ident, Index, TypeParam,
22};
23
24enum BundleFieldKind {
25    Component,
26    Ignore,
27}
28
29const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
30const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
31
32#[proc_macro_derive(Bundle, attributes(bundle))]
33pub fn derive_bundle(input: TokenStream) -> TokenStream {
34    let ast = parse_macro_input!(input as DeriveInput);
35    let ecs_path = bevy_ecs_path();
36
37    let named_fields = match get_struct_fields(&ast.data) {
38        Ok(fields) => fields,
39        Err(e) => return e.into_compile_error().into(),
40    };
41
42    let mut field_kind = Vec::with_capacity(named_fields.len());
43
44    for field in named_fields {
45        for attr in field
46            .attrs
47            .iter()
48            .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
49        {
50            if let Err(error) = attr.parse_nested_meta(|meta| {
51                if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
52                    field_kind.push(BundleFieldKind::Ignore);
53                    Ok(())
54                } else {
55                    Err(meta.error(format!(
56                        "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
57                    )))
58                }
59            }) {
60                return error.into_compile_error().into();
61            }
62        }
63
64        field_kind.push(BundleFieldKind::Component);
65    }
66
67    let field = named_fields
68        .iter()
69        .map(|field| field.ident.as_ref())
70        .collect::<Vec<_>>();
71
72    let field_type = named_fields
73        .iter()
74        .map(|field| &field.ty)
75        .collect::<Vec<_>>();
76
77    let mut field_component_ids = Vec::new();
78    let mut field_get_component_ids = Vec::new();
79    let mut field_get_components = Vec::new();
80    let mut field_from_components = Vec::new();
81    let mut field_required_components = Vec::new();
82    for (((i, field_type), field_kind), field) in field_type
83        .iter()
84        .enumerate()
85        .zip(field_kind.iter())
86        .zip(field.iter())
87    {
88        match field_kind {
89            BundleFieldKind::Component => {
90                field_component_ids.push(quote! {
91                <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids);
92                });
93                field_required_components.push(quote! {
94                    <#field_type as #ecs_path::bundle::Bundle>::register_required_components(components, storages, required_components);
95                });
96                field_get_component_ids.push(quote! {
97                    <#field_type as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);
98                });
99                match field {
100                    Some(field) => {
101                        field_get_components.push(quote! {
102                            self.#field.get_components(&mut *func);
103                        });
104                        field_from_components.push(quote! {
105                            #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
106                        });
107                    }
108                    None => {
109                        let index = Index::from(i);
110                        field_get_components.push(quote! {
111                            self.#index.get_components(&mut *func);
112                        });
113                        field_from_components.push(quote! {
114                            #index: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
115                        });
116                    }
117                }
118            }
119
120            BundleFieldKind::Ignore => {
121                field_from_components.push(quote! {
122                    #field: ::core::default::Default::default(),
123                });
124            }
125        }
126    }
127    let generics = ast.generics;
128    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
129    let struct_name = &ast.ident;
130
131    TokenStream::from(quote! {
132        // SAFETY:
133        // - ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
134        // - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass
135        //   the correct `StorageType` into the callback.
136        unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
137            fn component_ids(
138                components: &mut #ecs_path::component::Components,
139                storages: &mut #ecs_path::storage::Storages,
140                ids: &mut impl FnMut(#ecs_path::component::ComponentId)
141            ){
142                #(#field_component_ids)*
143            }
144
145            fn get_component_ids(
146                components: &#ecs_path::component::Components,
147                ids: &mut impl FnMut(Option<#ecs_path::component::ComponentId>)
148            ){
149                #(#field_get_component_ids)*
150            }
151
152            #[allow(unused_variables, non_snake_case)]
153            unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
154            where
155                __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
156            {
157                Self{
158                    #(#field_from_components)*
159                }
160            }
161
162            fn register_required_components(
163                components: &mut #ecs_path::component::Components,
164                storages: &mut #ecs_path::storage::Storages,
165                required_components: &mut #ecs_path::component::RequiredComponents
166            ){
167                #(#field_required_components)*
168            }
169        }
170
171        impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
172            #[allow(unused_variables)]
173            #[inline]
174            fn get_components(
175                self,
176                func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
177            ) {
178                #(#field_get_components)*
179            }
180        }
181    })
182}
183
184fn derive_visit_entities_base(
185    input: TokenStream,
186    trait_name: TokenStream2,
187    gen_methods: impl FnOnce(Vec<TokenStream2>) -> TokenStream2,
188) -> TokenStream {
189    let ast = parse_macro_input!(input as DeriveInput);
190    let ecs_path = bevy_ecs_path();
191
192    let named_fields = match get_struct_fields(&ast.data) {
193        Ok(fields) => fields,
194        Err(e) => return e.into_compile_error().into(),
195    };
196
197    let field = named_fields
198        .iter()
199        .filter_map(|field| {
200            if let Some(attr) = field
201                .attrs
202                .iter()
203                .find(|a| a.path().is_ident("visit_entities"))
204            {
205                let ignore = attr.parse_nested_meta(|meta| {
206                    if meta.path.is_ident("ignore") {
207                        Ok(())
208                    } else {
209                        Err(meta.error("Invalid visit_entities attribute. Use `ignore`"))
210                    }
211                });
212                return match ignore {
213                    Ok(()) => None,
214                    Err(e) => Some(Err(e)),
215                };
216            }
217            Some(Ok(field))
218        })
219        .map(|res| res.map(|field| field.ident.as_ref()))
220        .collect::<Result<Vec<_>, _>>();
221
222    let field = match field {
223        Ok(field) => field,
224        Err(e) => return e.into_compile_error().into(),
225    };
226
227    if field.is_empty() {
228        return syn::Error::new(
229            ast.span(),
230            format!("Invalid `{}` type: at least one field", trait_name),
231        )
232        .into_compile_error()
233        .into();
234    }
235
236    let field_access = field
237        .iter()
238        .enumerate()
239        .map(|(n, f)| {
240            if let Some(ident) = f {
241                quote! {
242                    self.#ident
243                }
244            } else {
245                let idx = Index::from(n);
246                quote! {
247                    self.#idx
248                }
249            }
250        })
251        .collect::<Vec<_>>();
252
253    let methods = gen_methods(field_access);
254
255    let generics = ast.generics;
256    let (impl_generics, ty_generics, _) = generics.split_for_impl();
257    let struct_name = &ast.ident;
258
259    TokenStream::from(quote! {
260        impl #impl_generics #ecs_path::entity:: #trait_name for #struct_name #ty_generics {
261            #methods
262        }
263    })
264}
265
266#[proc_macro_derive(VisitEntitiesMut, attributes(visit_entities))]
267pub fn derive_visit_entities_mut(input: TokenStream) -> TokenStream {
268    derive_visit_entities_base(input, quote! { VisitEntitiesMut }, |field| {
269        quote! {
270            fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
271                #(#field.visit_entities_mut(&mut f);)*
272            }
273        }
274    })
275}
276
277#[proc_macro_derive(VisitEntities, attributes(visit_entities))]
278pub fn derive_visit_entities(input: TokenStream) -> TokenStream {
279    derive_visit_entities_base(input, quote! { VisitEntities }, |field| {
280        quote! {
281            fn visit_entities<F: FnMut(Entity)>(&self, mut f: F) {
282                #(#field.visit_entities(&mut f);)*
283            }
284        }
285    })
286}
287
288fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
289    (0..count)
290        .map(|i| Ident::new(&fmt_string(i), Span::call_site()))
291        .collect::<Vec<Ident>>()
292}
293
294#[proc_macro]
295pub fn impl_param_set(_input: TokenStream) -> TokenStream {
296    let mut tokens = TokenStream::new();
297    let max_params = 8;
298    let params = get_idents(|i| format!("P{i}"), max_params);
299    let metas = get_idents(|i| format!("m{i}"), max_params);
300    let mut param_fn_muts = Vec::new();
301    for (i, param) in params.iter().enumerate() {
302        let fn_name = Ident::new(&format!("p{i}"), Span::call_site());
303        let index = Index::from(i);
304        let ordinal = match i {
305            1 => "1st".to_owned(),
306            2 => "2nd".to_owned(),
307            3 => "3rd".to_owned(),
308            x => format!("{x}th"),
309        };
310        let comment =
311            format!("Gets exclusive access to the {ordinal} parameter in this [`ParamSet`].");
312        param_fn_muts.push(quote! {
313            #[doc = #comment]
314            /// No other parameters may be accessed while this one is active.
315            pub fn #fn_name<'a>(&'a mut self) -> SystemParamItem<'a, 'a, #param> {
316                // SAFETY: systems run without conflicts with other systems.
317                // Conflicting params in ParamSet are not accessible at the same time
318                // ParamSets are guaranteed to not conflict with other SystemParams
319                unsafe {
320                    #param::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick)
321                }
322            }
323        });
324    }
325
326    for param_count in 1..=max_params {
327        let param = &params[0..param_count];
328        let meta = &metas[0..param_count];
329        let param_fn_mut = &param_fn_muts[0..param_count];
330        tokens.extend(TokenStream::from(quote! {
331            // SAFETY: All parameters are constrained to ReadOnlySystemParam, so World is only read
332            unsafe impl<'w, 's, #(#param,)*> ReadOnlySystemParam for ParamSet<'w, 's, (#(#param,)*)>
333            where #(#param: ReadOnlySystemParam,)*
334            { }
335
336            // SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
337            // with any prior access, a panic will occur.
338            unsafe impl<'_w, '_s, #(#param: SystemParam,)*> SystemParam for ParamSet<'_w, '_s, (#(#param,)*)>
339            {
340                type State = (#(#param::State,)*);
341                type Item<'w, 's> = ParamSet<'w, 's, (#(#param,)*)>;
342
343                // Note: We allow non snake case so the compiler don't complain about the creation of non_snake_case variables
344                #[allow(non_snake_case)]
345                fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
346                    #(
347                        // Pretend to add each param to the system alone, see if it conflicts
348                        let mut #meta = system_meta.clone();
349                        #meta.component_access_set.clear();
350                        #meta.archetype_component_access.clear();
351                        #param::init_state(world, &mut #meta);
352                        // The variable is being defined with non_snake_case here
353                        let #param = #param::init_state(world, &mut system_meta.clone());
354                    )*
355                    // Make the ParamSet non-send if any of its parameters are non-send.
356                    if false #(|| !#meta.is_send())* {
357                        system_meta.set_non_send();
358                    }
359                    #(
360                        system_meta
361                            .component_access_set
362                            .extend(#meta.component_access_set);
363                        system_meta
364                            .archetype_component_access
365                            .extend(&#meta.archetype_component_access);
366                    )*
367                    (#(#param,)*)
368                }
369
370                unsafe fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) {
371                    // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
372                    unsafe { <(#(#param,)*) as SystemParam>::new_archetype(state, archetype, system_meta); }
373                }
374
375                fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
376                    <(#(#param,)*) as SystemParam>::apply(state, system_meta, world);
377                }
378
379                fn queue(state: &mut Self::State, system_meta: &SystemMeta, mut world: DeferredWorld) {
380                    <(#(#param,)*) as SystemParam>::queue(state, system_meta, world.reborrow());
381                }
382
383                #[inline]
384                unsafe fn validate_param<'w, 's>(
385                    state: &'s Self::State,
386                    system_meta: &SystemMeta,
387                    world: UnsafeWorldCell<'w>,
388                ) -> bool {
389                    <(#(#param,)*) as SystemParam>::validate_param(state, system_meta, world)
390                }
391
392                #[inline]
393                unsafe fn get_param<'w, 's>(
394                    state: &'s mut Self::State,
395                    system_meta: &SystemMeta,
396                    world: UnsafeWorldCell<'w>,
397                    change_tick: Tick,
398                ) -> Self::Item<'w, 's> {
399                    ParamSet {
400                        param_states: state,
401                        system_meta: system_meta.clone(),
402                        world,
403                        change_tick,
404                    }
405                }
406            }
407
408            impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)>
409            {
410                #(#param_fn_mut)*
411            }
412        }));
413    }
414
415    tokens
416}
417
418/// Implement `SystemParam` to use a struct as a parameter in a system
419#[proc_macro_derive(SystemParam, attributes(system_param))]
420pub fn derive_system_param(input: TokenStream) -> TokenStream {
421    let token_stream = input.clone();
422    let ast = parse_macro_input!(input as DeriveInput);
423    let syn::Data::Struct(syn::DataStruct {
424        fields: field_definitions,
425        ..
426    }) = ast.data
427    else {
428        return syn::Error::new(
429            ast.span(),
430            "Invalid `SystemParam` type: expected a `struct`",
431        )
432        .into_compile_error()
433        .into();
434    };
435    let path = bevy_ecs_path();
436
437    let mut field_locals = Vec::new();
438    let mut fields = Vec::new();
439    let mut field_types = Vec::new();
440    for (i, field) in field_definitions.iter().enumerate() {
441        field_locals.push(format_ident!("f{i}"));
442        let i = Index::from(i);
443        fields.push(
444            field
445                .ident
446                .as_ref()
447                .map(|f| quote! { #f })
448                .unwrap_or_else(|| quote! { #i }),
449        );
450        field_types.push(&field.ty);
451    }
452
453    let generics = ast.generics;
454
455    // Emit an error if there's any unrecognized lifetime names.
456    for lt in generics.lifetimes() {
457        let ident = &lt.lifetime.ident;
458        let w = format_ident!("w");
459        let s = format_ident!("s");
460        if ident != &w && ident != &s {
461            return syn::Error::new_spanned(
462                lt,
463                r#"invalid lifetime name: expected `'w` or `'s`
464 'w -- refers to data stored in the World.
465 's -- refers to data stored in the SystemParam's state.'"#,
466            )
467            .into_compile_error()
468            .into();
469        }
470    }
471
472    let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
473
474    let lifetimeless_generics: Vec<_> = generics
475        .params
476        .iter()
477        .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
478        .collect();
479
480    let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
481
482    let mut punctuated_generics = Punctuated::<_, Comma>::new();
483    punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
484        GenericParam::Type(g) => GenericParam::Type(TypeParam {
485            default: None,
486            ..g.clone()
487        }),
488        GenericParam::Const(g) => GenericParam::Const(ConstParam {
489            default: None,
490            ..g.clone()
491        }),
492        _ => unreachable!(),
493    }));
494
495    let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
496    punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
497        GenericParam::Type(g) => &g.ident,
498        GenericParam::Const(g) => &g.ident,
499        _ => unreachable!(),
500    }));
501
502    let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
503        .iter()
504        .map(|&g| match g.clone() {
505            GenericParam::Type(mut g) => {
506                g.bounds.clear();
507                GenericParam::Type(g)
508            }
509            g => g,
510        })
511        .collect();
512
513    let mut tuple_types: Vec<_> = field_types.iter().map(|x| quote! { #x }).collect();
514    let mut tuple_patterns: Vec<_> = field_locals.iter().map(|x| quote! { #x }).collect();
515
516    // If the number of fields exceeds the 16-parameter limit,
517    // fold the fields into tuples of tuples until we are below the limit.
518    const LIMIT: usize = 16;
519    while tuple_types.len() > LIMIT {
520        let end = Vec::from_iter(tuple_types.drain(..LIMIT));
521        tuple_types.push(parse_quote!( (#(#end,)*) ));
522
523        let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
524        tuple_patterns.push(parse_quote!( (#(#end,)*) ));
525    }
526
527    // Create a where clause for the `ReadOnlySystemParam` impl.
528    // Ensure that each field implements `ReadOnlySystemParam`.
529    let mut read_only_generics = generics.clone();
530    let read_only_where_clause = read_only_generics.make_where_clause();
531    for field_type in &field_types {
532        read_only_where_clause
533            .predicates
534            .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
535    }
536
537    let fields_alias =
538        ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
539
540    let struct_name = &ast.ident;
541    let state_struct_visibility = &ast.vis;
542    let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
543
544    let mut builder_name = None;
545    for meta in ast
546        .attrs
547        .iter()
548        .filter(|a| a.path().is_ident("system_param"))
549    {
550        if let Err(e) = meta.parse_nested_meta(|nested| {
551            if nested.path.is_ident("builder") {
552                builder_name = Some(format_ident!("{struct_name}Builder"));
553                Ok(())
554            } else {
555                Err(nested.error("Unsupported attribute"))
556            }
557        }) {
558            return e.into_compile_error().into();
559        }
560    }
561
562    let builder = builder_name.map(|builder_name| {
563        let builder_type_parameters: Vec<_> = (0..fields.len()).map(|i| format_ident!("B{i}")).collect();
564        let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
565        let builder_struct = quote! {
566            #[doc = #builder_doc_comment]
567            struct #builder_name<#(#builder_type_parameters,)*> {
568                #(#fields: #builder_type_parameters,)*
569            }
570        };
571        let lifetimes: Vec<_> = generics.lifetimes().collect();
572        let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
573        let builder_impl = quote!{
574            // SAFETY: This delegates to the `SystemParamBuilder` for tuples.
575            unsafe impl<
576                #(#lifetimes,)*
577                #(#builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
578                #punctuated_generics
579            > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
580                #where_clause
581            {
582                fn build(self, world: &mut #path::world::World, meta: &mut #path::system::SystemMeta) -> <#generic_struct as #path::system::SystemParam>::State {
583                    let #builder_name { #(#fields: #field_locals,)* } = self;
584                    #state_struct_name {
585                        state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world, meta)
586                    }
587                }
588            }
589        };
590        (builder_struct, builder_impl)
591    });
592    let (builder_struct, builder_impl) = builder.unzip();
593
594    TokenStream::from(quote! {
595        // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
596        // The struct can still be accessed via SystemParam::State, e.g. EventReaderState can be accessed via
597        // <EventReader<'static, 'static, T> as SystemParam>::State
598        const _: () = {
599            // Allows rebinding the lifetimes of each field type.
600            type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
601
602            #[doc(hidden)]
603            #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
604            #where_clause {
605                state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
606            }
607
608            unsafe impl<#punctuated_generics> #path::system::SystemParam for
609                #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
610            {
611                type State = #state_struct_name<#punctuated_generic_idents>;
612                type Item<'w, 's> = #struct_name #ty_generics;
613
614                fn init_state(world: &mut #path::world::World, system_meta: &mut #path::system::SystemMeta) -> Self::State {
615                    #state_struct_name {
616                        state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world, system_meta),
617                    }
618                }
619
620                unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
621                    // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
622                    unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta) }
623                }
624
625                fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
626                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
627                }
628
629                fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
630                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
631                }
632
633                #[inline]
634                unsafe fn validate_param<'w, 's>(
635                    state: &'s Self::State,
636                    system_meta: &#path::system::SystemMeta,
637                    world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
638                ) -> bool {
639                    <(#(#tuple_types,)*) as #path::system::SystemParam>::validate_param(&state.state, system_meta, world)
640                }
641
642                #[inline]
643                unsafe fn get_param<'w, 's>(
644                    state: &'s mut Self::State,
645                    system_meta: &#path::system::SystemMeta,
646                    world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
647                    change_tick: #path::component::Tick,
648                ) -> Self::Item<'w, 's> {
649                    let (#(#tuple_patterns,)*) = <
650                        (#(#tuple_types,)*) as #path::system::SystemParam
651                    >::get_param(&mut state.state, system_meta, world, change_tick);
652                    #struct_name {
653                        #(#fields: #field_locals,)*
654                    }
655                }
656            }
657
658            // Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World`
659            unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
660
661            #builder_impl
662        };
663
664        #builder_struct
665    })
666}
667
668/// Implement `QueryData` to use a struct as a data parameter in a query
669#[proc_macro_derive(QueryData, attributes(query_data))]
670pub fn derive_query_data(input: TokenStream) -> TokenStream {
671    derive_query_data_impl(input)
672}
673
674/// Implement `QueryFilter` to use a struct as a filter parameter in a query
675#[proc_macro_derive(QueryFilter, attributes(query_filter))]
676pub fn derive_query_filter(input: TokenStream) -> TokenStream {
677    derive_query_filter_impl(input)
678}
679
680/// Derive macro generating an impl of the trait `ScheduleLabel`.
681///
682/// This does not work for unions.
683#[proc_macro_derive(ScheduleLabel)]
684pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
685    let input = parse_macro_input!(input as DeriveInput);
686    let mut trait_path = bevy_ecs_path();
687    trait_path.segments.push(format_ident!("schedule").into());
688    let mut dyn_eq_path = trait_path.clone();
689    trait_path
690        .segments
691        .push(format_ident!("ScheduleLabel").into());
692    dyn_eq_path.segments.push(format_ident!("DynEq").into());
693    derive_label(input, "ScheduleLabel", &trait_path, &dyn_eq_path)
694}
695
696/// Derive macro generating an impl of the trait `SystemSet`.
697///
698/// This does not work for unions.
699#[proc_macro_derive(SystemSet)]
700pub fn derive_system_set(input: TokenStream) -> TokenStream {
701    let input = parse_macro_input!(input as DeriveInput);
702    let mut trait_path = bevy_ecs_path();
703    trait_path.segments.push(format_ident!("schedule").into());
704    let mut dyn_eq_path = trait_path.clone();
705    trait_path.segments.push(format_ident!("SystemSet").into());
706    dyn_eq_path.segments.push(format_ident!("DynEq").into());
707    derive_label(input, "SystemSet", &trait_path, &dyn_eq_path)
708}
709
710pub(crate) fn bevy_ecs_path() -> syn::Path {
711    BevyManifest::default().get_path("bevy_ecs")
712}
713
714#[proc_macro_derive(Event)]
715pub fn derive_event(input: TokenStream) -> TokenStream {
716    component::derive_event(input)
717}
718
719#[proc_macro_derive(Resource)]
720pub fn derive_resource(input: TokenStream) -> TokenStream {
721    component::derive_resource(input)
722}
723
724#[proc_macro_derive(Component, attributes(component, require))]
725pub fn derive_component(input: TokenStream) -> TokenStream {
726    component::derive_component(input)
727}
728
729#[proc_macro_derive(States)]
730pub fn derive_states(input: TokenStream) -> TokenStream {
731    states::derive_states(input)
732}
733
734#[proc_macro_derive(SubStates, attributes(source))]
735pub fn derive_substates(input: TokenStream) -> TokenStream {
736    states::derive_substates(input)
737}