1#![cfg_attr(docsrs, feature(doc_cfg))]
4
5extern crate proc_macro;
6
7mod component;
8mod event;
9mod message;
10mod query_data;
11mod query_filter;
12mod world_query;
13
14use crate::{
15 component::map_entities, query_data::derive_query_data_impl,
16 query_filter::derive_query_filter_impl,
17};
18use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
19use proc_macro::TokenStream;
20use proc_macro2::{Ident, Span};
21use quote::{format_ident, quote, ToTokens};
22use syn::{
23 parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data,
24 DeriveInput, GenericParam, TypeParam,
25};
26
27enum BundleFieldKind {
28 Component,
29 Ignore,
30}
31
32const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
33const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
34const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
35
36#[derive(Debug)]
37struct BundleAttributes {
38 impl_from_components: bool,
39}
40
41impl Default for BundleAttributes {
42 fn default() -> Self {
43 Self {
44 impl_from_components: true,
45 }
46 }
47}
48
49#[proc_macro_derive(Bundle, attributes(bundle))]
51pub fn derive_bundle(input: TokenStream) -> TokenStream {
52 let ast = parse_macro_input!(input as DeriveInput);
53 let ecs_path = bevy_ecs_path();
54
55 let mut attributes = BundleAttributes::default();
56
57 for attr in &ast.attrs {
58 if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
59 let parsing = attr.parse_nested_meta(|meta| {
60 if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
61 attributes.impl_from_components = false;
62 return Ok(());
63 }
64
65 Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
66 });
67
68 if let Err(e) = parsing {
69 return e.into_compile_error().into();
70 }
71 }
72 }
73
74 let fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
75 Ok(fields) => fields,
76 Err(e) => return e.into_compile_error().into(),
77 };
78
79 let mut field_kinds = Vec::with_capacity(fields.len());
80
81 for field in fields {
82 let mut kind = BundleFieldKind::Component;
83
84 for attr in field
85 .attrs
86 .iter()
87 .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
88 {
89 if let Err(error) = attr.parse_nested_meta(|meta| {
90 if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
91 kind = BundleFieldKind::Ignore;
92 Ok(())
93 } else {
94 Err(meta.error(format!(
95 "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
96 )))
97 }
98 }) {
99 return error.into_compile_error().into();
100 }
101 }
102
103 field_kinds.push(kind);
104 }
105
106 let field_types = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
107
108 let mut active_field_types = Vec::new();
109 let mut active_field_members = Vec::new();
110 let mut active_field_locals = Vec::new();
111 let mut inactive_field_members = Vec::new();
112 for ((field_member, field_type), field_kind) in
113 fields.members().zip(field_types).zip(field_kinds)
114 {
115 let field_local = format_ident!("field_{}", field_member);
116
117 match field_kind {
118 BundleFieldKind::Component => {
119 active_field_types.push(field_type);
120 active_field_locals.push(field_local);
121 active_field_members.push(field_member);
122 }
123 BundleFieldKind::Ignore => inactive_field_members.push(field_member),
124 }
125 }
126 let generics = ast.generics;
127 let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
128 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
129 let struct_name = &ast.ident;
130
131 let bundle_impl = quote! {
132 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::ComponentsRegistrator,
139 ) -> impl Iterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
140 core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
141 }
142
143 fn get_component_ids(
144 components: &#ecs_path::component::Components,
145 ) -> impl Iterator<Item = Option<#ecs_path::component::ComponentId>> {
146 core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
147 }
148 }
149 };
150
151 let dynamic_bundle_impl = quote! {
152 impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
153 type Effect = ();
154 #[allow(unused_variables)]
155 #[inline]
156 unsafe fn get_components(
157 ptr: #ecs_path::ptr::MovingPtr<'_, Self>,
158 func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
159 ) {
160 use #ecs_path::__macro_exports::DebugCheckedUnwrap;
161
162 #ecs_path::ptr::deconstruct_moving_ptr!({
163 let #struct_name { #(#active_field_members: #active_field_locals,)* #(#inactive_field_members: _,)* } = ptr;
164 });
165 #(
166 <#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(
167 #active_field_locals,
168 func
169 );
170 )*
171 }
172
173 #[allow(unused_variables)]
174 #[inline]
175 unsafe fn apply_effect(
176 ptr: #ecs_path::ptr::MovingPtr<'_, core::mem::MaybeUninit<Self>>,
177 func: &mut #ecs_path::world::EntityWorldMut<'_>,
178 ) {
179 }
180 }
181 };
182
183 let from_components_impl = attributes.impl_from_components.then(|| quote! {
184 unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
187 #[allow(unused_variables, non_snake_case)]
188 unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
189 where
190 __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
191 {
192 Self {
193 #(#active_field_members: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
194 #(#inactive_field_members: ::core::default::Default::default(),)*
195 }
196 }
197 }
198 });
199 TokenStream::from(quote! {
200 #bundle_impl
201 #from_components_impl
202 #dynamic_bundle_impl
203 })
204}
205
206#[proc_macro_derive(MapEntities, attributes(entities))]
208pub fn derive_map_entities(input: TokenStream) -> TokenStream {
209 let ast = parse_macro_input!(input as DeriveInput);
210 let ecs_path = bevy_ecs_path();
211
212 let map_entities_impl = map_entities(
213 &ast.data,
214 &ecs_path,
215 Ident::new("self", Span::call_site()),
216 false,
217 false,
218 None,
219 );
220
221 let struct_name = &ast.ident;
222 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
223 TokenStream::from(quote! {
224 impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
225 fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
226 #map_entities_impl
227 }
228 }
229 })
230}
231
232#[proc_macro_derive(SystemParam, attributes(system_param))]
234pub fn derive_system_param(input: TokenStream) -> TokenStream {
235 let token_stream = input.clone();
236 let ast = parse_macro_input!(input as DeriveInput);
237
238 match derive_system_param_impl(token_stream, ast) {
239 Ok(t) => t,
240 Err(e) => e.into_compile_error().into(),
241 }
242}
243fn derive_system_param_impl(
244 token_stream: TokenStream,
245 ast: DeriveInput,
246) -> syn::Result<TokenStream> {
247 let fields = get_struct_fields(&ast.data, "derive(SystemParam)")?;
248 let path = bevy_ecs_path();
249
250 let field_locals = fields
251 .members()
252 .map(|m| format_ident!("field{}", m))
253 .collect::<Vec<_>>();
254 let field_members = fields.members().collect::<Vec<_>>();
255 let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
256
257 let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
258 let mut field_validation_messages = Vec::with_capacity(fields.len());
259 for attr in fields
260 .iter()
261 .map(|f| f.attrs.iter().find(|a| a.path().is_ident("system_param")))
262 {
263 let mut field_validation_message = None;
264 if let Some(attr) = attr {
265 attr.parse_nested_meta(|nested| {
266 if nested.path.is_ident("validation_message") {
267 field_validation_message = Some(nested.value()?.parse()?);
268 Ok(())
269 } else {
270 Err(nested.error("Unsupported attribute"))
271 }
272 })?;
273 }
274 field_validation_messages
275 .push(field_validation_message.unwrap_or_else(|| quote! { err.message }));
276 }
277
278 let generics = ast.generics;
279
280 let w = format_ident!("w");
282 let s = format_ident!("s");
283 for lt in generics.lifetimes() {
284 let ident = <.lifetime.ident;
285 if ident != &w && ident != &s {
286 return Err(syn::Error::new_spanned(
287 lt,
288 r#"invalid lifetime name: expected `'w` or `'s`
289 'w -- refers to data stored in the World.
290 's -- refers to data stored in the SystemParam's state.'"#,
291 ));
292 }
293 }
294
295 let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
296
297 let lifetimeless_generics: Vec<_> = generics
298 .params
299 .iter()
300 .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
301 .collect();
302
303 let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
304
305 let mut punctuated_generics = Punctuated::<_, Comma>::new();
306 punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
307 GenericParam::Type(g) => GenericParam::Type(TypeParam {
308 default: None,
309 ..g.clone()
310 }),
311 GenericParam::Const(g) => GenericParam::Const(ConstParam {
312 default: None,
313 ..g.clone()
314 }),
315 _ => unreachable!(),
316 }));
317
318 let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
319 punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
320 GenericParam::Type(g) => &g.ident,
321 GenericParam::Const(g) => &g.ident,
322 _ => unreachable!(),
323 }));
324
325 let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
326 .iter()
327 .map(|&g| match g.clone() {
328 GenericParam::Type(mut g) => {
329 g.bounds.clear();
330 GenericParam::Type(g)
331 }
332 g => g,
333 })
334 .collect();
335
336 let mut tuple_types: Vec<_> = field_types.iter().map(ToTokens::to_token_stream).collect();
337 let mut tuple_patterns: Vec<_> = field_locals.iter().map(ToTokens::to_token_stream).collect();
338
339 const LIMIT: usize = 16;
342 while tuple_types.len() > LIMIT {
343 let end = Vec::from_iter(tuple_types.drain(..LIMIT));
344 tuple_types.push(parse_quote!( (#(#end,)*) ));
345
346 let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
347 tuple_patterns.push(parse_quote!( (#(#end,)*) ));
348 }
349 let mut read_only_generics = generics.clone();
352 let read_only_where_clause = read_only_generics.make_where_clause();
353 for field_type in &field_types {
354 read_only_where_clause
355 .predicates
356 .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
357 }
358
359 let fields_alias =
360 ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
361
362 let struct_name = &ast.ident;
363 let state_struct_visibility = &ast.vis;
364 let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
365
366 let mut builder_name = None;
367 for meta in ast
368 .attrs
369 .iter()
370 .filter(|a| a.path().is_ident("system_param"))
371 {
372 meta.parse_nested_meta(|nested| {
373 if nested.path.is_ident("builder") {
374 builder_name = Some(format_ident!("{struct_name}Builder"));
375 Ok(())
376 } else {
377 Err(nested.error("Unsupported attribute"))
378 }
379 })?;
380 }
381
382 let builder = builder_name.map(|builder_name| {
383 let builder_type_parameters: Vec<Ident> = field_members.iter().map(|m| format_ident!("B{}", m)).collect();
384 let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
385 let builder_struct = quote! {
386 #[doc = #builder_doc_comment]
387 struct #builder_name<#(#builder_type_parameters,)*> {
388 #(#field_members: #builder_type_parameters,)*
389 }
390 };
391 let lifetimes: Vec<_> = generics.lifetimes().collect();
392 let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
393 let builder_impl = quote!{
394 unsafe impl<
396 #(#lifetimes,)*
397 #(#builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
398 #punctuated_generics
399 > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
400 #where_clause
401 {
402 fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
403 let #builder_name { #(#field_members: #field_locals,)* } = self;
404 #state_struct_name {
405 state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
406 }
407 }
408 }
409 };
410 (builder_struct, builder_impl)
411 });
412 let (builder_struct, builder_impl) = builder.unzip();
413
414 Ok(TokenStream::from(quote! {
415 const _: () = {
419 type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
421
422 #[doc(hidden)]
423 #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
424 #where_clause {
425 state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
426 }
427
428 unsafe impl<#punctuated_generics> #path::system::SystemParam for
429 #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
430 {
431 type State = #state_struct_name<#punctuated_generic_idents>;
432 type Item<'w, 's> = #struct_name #ty_generics;
433
434 fn init_state(world: &mut #path::world::World) -> Self::State {
435 #state_struct_name {
436 state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
437 }
438 }
439
440 fn init_access(state: &Self::State, system_meta: &mut #path::system::SystemMeta, component_access_set: &mut #path::query::FilteredAccessSet, world: &mut #path::world::World) {
441 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, component_access_set, world);
442 }
443
444 fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
445 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
446 }
447
448 fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
449 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
450 }
451
452 #[inline]
453 unsafe fn validate_param<'w, 's>(
454 state: &'s mut Self::State,
455 _system_meta: &#path::system::SystemMeta,
456 _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
457 ) -> Result<(), #path::system::SystemParamValidationError> {
458 let #state_struct_name { state: (#(#tuple_patterns,)*) } = state;
459 #(
460 <#field_types as #path::system::SystemParam>::validate_param(#field_locals, _system_meta, _world)
461 .map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_validation_messages, #field_validation_names))?;
462 )*
463 Result::Ok(())
464 }
465
466 #[inline]
467 unsafe fn get_param<'w, 's>(
468 state: &'s mut Self::State,
469 system_meta: &#path::system::SystemMeta,
470 world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
471 change_tick: #path::change_detection::Tick,
472 ) -> Self::Item<'w, 's> {
473 let (#(#tuple_patterns,)*) = <
474 (#(#tuple_types,)*) as #path::system::SystemParam
475 >::get_param(&mut state.state, system_meta, world, change_tick);
476 #struct_name {
477 #(#field_members: #field_locals,)*
478 }
479 }
480 }
481
482 unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
484
485 #builder_impl
486 };
487
488 #builder_struct
489 }))
490}
491
492#[proc_macro_derive(QueryData, attributes(query_data))]
494pub fn derive_query_data(input: TokenStream) -> TokenStream {
495 derive_query_data_impl(input)
496}
497
498#[proc_macro_derive(QueryFilter, attributes(query_filter))]
500pub fn derive_query_filter(input: TokenStream) -> TokenStream {
501 derive_query_filter_impl(input)
502}
503
504#[proc_macro_derive(ScheduleLabel)]
508pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
509 let input = parse_macro_input!(input as DeriveInput);
510 let mut trait_path = bevy_ecs_path();
511 trait_path.segments.push(format_ident!("schedule").into());
512 trait_path
513 .segments
514 .push(format_ident!("ScheduleLabel").into());
515 derive_label(input, "ScheduleLabel", &trait_path)
516}
517
518#[proc_macro_derive(SystemSet)]
522pub fn derive_system_set(input: TokenStream) -> TokenStream {
523 let input = parse_macro_input!(input as DeriveInput);
524 let mut trait_path = bevy_ecs_path();
525 trait_path.segments.push(format_ident!("schedule").into());
526 trait_path.segments.push(format_ident!("SystemSet").into());
527 derive_label(input, "SystemSet", &trait_path)
528}
529
530pub(crate) fn bevy_ecs_path() -> syn::Path {
531 BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"))
532}
533
534#[proc_macro_derive(Event, attributes(event))]
536pub fn derive_event(input: TokenStream) -> TokenStream {
537 event::derive_event(input)
538}
539
540#[proc_macro_derive(EntityEvent, attributes(entity_event, event_target))]
554pub fn derive_entity_event(input: TokenStream) -> TokenStream {
555 event::derive_entity_event(input)
556}
557
558#[proc_macro_derive(Message)]
560pub fn derive_message(input: TokenStream) -> TokenStream {
561 message::derive_message(input)
562}
563
564#[proc_macro_derive(Resource)]
566pub fn derive_resource(input: TokenStream) -> TokenStream {
567 component::derive_resource(input)
568}
569
570#[proc_macro_derive(
655 Component,
656 attributes(component, require, relationship, relationship_target, entities)
657)]
658pub fn derive_component(input: TokenStream) -> TokenStream {
659 component::derive_component(input)
660}
661
662#[proc_macro_derive(FromWorld, attributes(from_world))]
664pub fn derive_from_world(input: TokenStream) -> TokenStream {
665 let bevy_ecs_path = bevy_ecs_path();
666 let ast = parse_macro_input!(input as DeriveInput);
667 let name = ast.ident;
668 let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
669
670 let (fields, variant_ident) = match &ast.data {
671 Data::Struct(data) => (&data.fields, None),
672 Data::Enum(data) => {
673 match data.variants.iter().find(|variant| {
674 variant
675 .attrs
676 .iter()
677 .any(|attr| attr.path().is_ident("from_world"))
678 }) {
679 Some(variant) => (&variant.fields, Some(&variant.ident)),
680 None => {
681 return syn::Error::new(
682 Span::call_site(),
683 "No variant found with the `#[from_world]` attribute",
684 )
685 .into_compile_error()
686 .into();
687 }
688 }
689 }
690 Data::Union(_) => {
691 return syn::Error::new(
692 Span::call_site(),
693 "#[derive(FromWorld)]` does not support unions",
694 )
695 .into_compile_error()
696 .into();
697 }
698 };
699
700 let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
701 let members = fields.members();
702
703 let field_initializers = match variant_ident {
704 Some(variant_ident) => quote!( Self::#variant_ident {
705 #(#members: #field_init_expr),*
706 }),
707 None => quote!( Self {
708 #(#members: #field_init_expr),*
709 }),
710 };
711
712 TokenStream::from(quote! {
713 impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
714 fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
715 #field_initializers
716 }
717 }
718 })
719}