From 8bc1f5a3f5eff2db838c94e9f8df6a8ae85732a0 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Thu, 29 Sep 2022 01:30:04 +0100 Subject: [PATCH 1/9] implemented #[bundle(ignore)] --- crates/bevy_ecs/macros/src/lib.rs | 74 ++++++++++++++++++++++++++----- crates/bevy_ecs/src/bundle.rs | 14 +++++- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index ed4391d7e0041..ba6033d50baa6 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -12,8 +12,10 @@ use syn::{ parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, + spanned::Spanned, token::Comma, - DeriveInput, Field, GenericParam, Ident, Index, LitInt, Result, Token, TypeParam, + DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, Result, + Token, TypeParam, }; struct AllTuples { @@ -80,7 +82,16 @@ pub fn all_tuples(input: TokenStream) -> TokenStream { }) } -#[proc_macro_derive(Bundle)] +#[derive(Eq, PartialEq)] +enum BundleFieldKind { + Component, + Ignore, +} + +static BUNDLE_ATTRIBUTE_NAME: &str = "bundle"; +static BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore"; + +#[proc_macro_derive(Bundle, attributes(bundle))] pub fn derive_bundle(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let ecs_path = bevy_ecs_path(); @@ -90,6 +101,29 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { Err(e) => return e.into_compile_error().into(), }; + let mut field_kind = vec![]; + + 'field_loop: for field in named_fields.iter() { + for attr in &field.attrs { + if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) { + if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() { + if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() { + if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) { + field_kind.push(BundleFieldKind::Ignore); + continue 'field_loop; + } + + return syn::Error::new(path.span(), format!("Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`")).into_compile_error().into(); + } + } + + return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); + } + } + + field_kind.push(BundleFieldKind::Component); + } + let field = named_fields .iter() .map(|field| field.ident.as_ref().unwrap()) @@ -102,17 +136,33 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { let mut field_component_ids = Vec::new(); let mut field_get_components = Vec::new(); let mut field_from_components = Vec::new(); - for (field_type, field) in field_type.iter().zip(field.iter()) { - field_component_ids.push(quote! { - <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids); - }); - field_get_components.push(quote! { - self.#field.get_components(&mut *func); - }); - field_from_components.push(quote! { - #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func), - }); + for ((field_type, field_kind), field) in + field_type.iter().zip(field_kind.iter()).zip(field.iter()) + { + match field_kind { + BundleFieldKind::Component => { + field_component_ids.push(quote! { + <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids); + }); + field_get_components.push(quote! { + self.#field.get_components(&mut *func); + }); + field_from_components.push(quote! { + #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func), + }); + } + + BundleFieldKind::Ignore => { + field_from_components.push(quote! { + #field: Default::default(), + }); + } + } } + let field_len = field_kind + .iter() + .filter(|fk| **fk != BundleFieldKind::Ignore) + .count(); let generics = ast.generics; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let struct_name = &ast.ident; diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index d9124f2493b84..9eaa2e187d7c5 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -82,10 +82,12 @@ use std::{any::TypeId, collections::HashMap}; /// used instead. /// The derived `Bundle` implementation contains the items of its fields, which all must /// implement `Bundle`. -/// As explained above, this includes any [`Component`] type, and other derived bundles: +/// As explained above, this includes any [`Component`] type, and other derived bundles. /// +/// If you want to add `PhantomData` to your `Bundle` you have to mark it with `#[bundle(ignore)]`. /// ``` -/// # use bevy_ecs::{component::Component, bundle::Bundle}; +/// # use std::marker::PhantomData; +/// use bevy_ecs::{component::Component, bundle::Bundle}; /// /// #[derive(Component)] /// struct XPosition(i32); @@ -99,12 +101,20 @@ use std::{any::TypeId, collections::HashMap}; /// y: YPosition, /// } /// +/// // You have to implement `Default` for ignored field types in bundle structs. +/// #[derive(Default)] +/// struct Other(f32); +/// /// #[derive(Bundle)] /// struct NamedPointBundle { /// // Or other bundles /// a: PositionBundle, /// // In addition to more components /// z: PointName, +/// +/// // when you need to use `PhantomData` you have to mark it as ignored +/// #[bundle(ignore)] +/// _phantom_data: PhantomData /// } /// /// #[derive(Component)] From 82622703f539e3b07c3e714f92ab08531f747e30 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Fri, 30 Sep 2022 01:09:55 +0100 Subject: [PATCH 2/9] fixed tests --- crates/bevy_ecs/macros/src/lib.rs | 13 ++++++++----- crates/bevy_ecs/src/bundle.rs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index ba6033d50baa6..8fbbcd5059011 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -113,7 +113,14 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { continue 'field_loop; } - return syn::Error::new(path.span(), format!("Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`")).into_compile_error().into(); + return syn::Error::new( + path.span(), + format!( + "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`" + ), + ) + .into_compile_error() + .into(); } } @@ -159,10 +166,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { } } } - let field_len = field_kind - .iter() - .filter(|fk| **fk != BundleFieldKind::Ignore) - .count(); let generics = ast.generics; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let struct_name = &ast.ident; diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 9eaa2e187d7c5..52bd4af31030e 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -106,7 +106,7 @@ use std::{any::TypeId, collections::HashMap}; /// struct Other(f32); /// /// #[derive(Bundle)] -/// struct NamedPointBundle { +/// struct NamedPointBundle { /// // Or other bundles /// a: PositionBundle, /// // In addition to more components From a69c1bc63848c360fa44ad355ddd913638918d14 Mon Sep 17 00:00:00 2001 From: Marc-Stefan Cassola Date: Fri, 30 Sep 2022 11:39:35 +0100 Subject: [PATCH 3/9] Update crates/bevy_ecs/macros/src/lib.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- crates/bevy_ecs/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 8fbbcd5059011..633594367751d 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -161,7 +161,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { BundleFieldKind::Ignore => { field_from_components.push(quote! { - #field: Default::default(), + #field: ::std::default::Default::default(), }); } } From 129d3e72f50b6641b41094c4bce7ddcd772b4f4d Mon Sep 17 00:00:00 2001 From: Marc-Stefan Cassola Date: Fri, 30 Sep 2022 11:40:14 +0100 Subject: [PATCH 4/9] Update crates/bevy_ecs/macros/src/lib.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- crates/bevy_ecs/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 633594367751d..7ce448ae63c49 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -101,7 +101,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { Err(e) => return e.into_compile_error().into(), }; - let mut field_kind = vec![]; + let mut field_kind = Vec::with_capacity(named_fields.len()); 'field_loop: for field in named_fields.iter() { for attr in &field.attrs { From 70bc74d84729dc5b5edb01ce5c0eada81a164f7f Mon Sep 17 00:00:00 2001 From: Marc-Stefan Cassola Date: Fri, 30 Sep 2022 11:41:37 +0100 Subject: [PATCH 5/9] Update crates/bevy_ecs/macros/src/lib.rs Co-authored-by: Afonso Lage --- crates/bevy_ecs/macros/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 7ce448ae63c49..0a4542be49a3f 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -122,9 +122,9 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { .into_compile_error() .into(); } + } else { + return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); } - - return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); } } From 27b696b0b301a041e4bcf55dc1214102ec3e676a Mon Sep 17 00:00:00 2001 From: Marc-Stefan Cassola Date: Fri, 30 Sep 2022 11:41:51 +0100 Subject: [PATCH 6/9] Update crates/bevy_ecs/macros/src/lib.rs Co-authored-by: Afonso Lage --- crates/bevy_ecs/macros/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 0a4542be49a3f..39f74e4a79433 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -111,16 +111,16 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) { field_kind.push(BundleFieldKind::Ignore); continue 'field_loop; - } - - return syn::Error::new( - path.span(), - format!( - "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`" - ), - ) - .into_compile_error() - .into(); + } else { + return syn::Error::new( + path.span(), + format!( + "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`" + ), + ) + .into_compile_error() + .into(); + } } } else { return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); From 7fabf1af681efe6e62fbec0cd9e0fbf0bce44973 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Fri, 30 Sep 2022 20:08:25 +0100 Subject: [PATCH 7/9] fixed tests --- crates/bevy_ecs/macros/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 39f74e4a79433..60d221fe36a65 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -82,14 +82,13 @@ pub fn all_tuples(input: TokenStream) -> TokenStream { }) } -#[derive(Eq, PartialEq)] enum BundleFieldKind { Component, Ignore, } -static BUNDLE_ATTRIBUTE_NAME: &str = "bundle"; -static BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore"; +const BUNDLE_ATTRIBUTE_NAME: &str = "bundle"; +const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore"; #[proc_macro_derive(Bundle, attributes(bundle))] pub fn derive_bundle(input: TokenStream) -> TokenStream { @@ -120,7 +119,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { ) .into_compile_error() .into(); - } + } } } else { return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); From decbc9364181d8d01529e30bc573ec240e36358d Mon Sep 17 00:00:00 2001 From: Maccesch Date: Fri, 30 Sep 2022 20:19:11 +0100 Subject: [PATCH 8/9] removed redundant else --- crates/bevy_ecs/macros/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 60d221fe36a65..2a52620ed099e 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -110,18 +110,18 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) { field_kind.push(BundleFieldKind::Ignore); continue 'field_loop; - } else { - return syn::Error::new( - path.span(), - format!( - "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`" - ), - ) - .into_compile_error() - .into(); } + + return syn::Error::new( + path.span(), + format!( + "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`" + ), + ) + .into_compile_error() + .into(); } - } else { + return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into(); } } From c4a89ffc0c8adfd0789919489e2bf2077ade7453 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Sat, 1 Oct 2022 13:34:54 +0100 Subject: [PATCH 9/9] added unit test for #[bundle(ignore)] --- crates/bevy_ecs/src/lib.rs | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 6143036737dbb..f3efaaf09e974 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -238,6 +238,45 @@ mod tests { b: B(2), } ); + + #[derive(Default, Component, PartialEq, Debug)] + struct Ignored; + + #[derive(Bundle, PartialEq, Debug)] + struct BundleWithIgnored { + c: C, + #[bundle(ignore)] + ignored: Ignored, + } + + let mut ids = Vec::new(); + ::component_ids( + &mut world.components, + &mut world.storages, + &mut |id| { + ids.push(id); + }, + ); + + assert_eq!(ids, &[world.init_component::(),]); + + let e4 = world + .spawn(BundleWithIgnored { + c: C, + ignored: Ignored, + }) + .id(); + + assert_eq!(world.get::(e4).unwrap(), &C); + assert_eq!(world.get::(e4), None); + + assert_eq!( + world.entity_mut(e4).remove::().unwrap(), + BundleWithIgnored { + c: C, + ignored: Ignored, + } + ); } #[test]