Required Components: pass through all tokens in {} and () syntax (#18578)

# Objective

#18555 added improved require syntax, but inline structs didn't support
`..Default::default()` syntax (for technical reasons we can't parse the
struct directly, so there is manual logic that missed this case).

## Solution

When a `{}` or `()` section is encountered for a required component,
rather than trying to parse the fields directly, just pass _all_ of the
tokens through. This ensures no tokens are dropped, protects us against
any future syntax changes, and optimizes our parsing logic (as we're
dropping the field parsing logic entirely).
This commit is contained in:
Carter Anderson 2025-03-27 14:20:08 -07:00 committed by GitHub
parent 0a2e183476
commit 1ba9da0812
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 14 additions and 10 deletions

View File

@ -9,8 +9,8 @@ use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::{Brace, Comma, Paren},
Data, DataEnum, DataStruct, DeriveInput, Expr, ExprCall, ExprPath, Field, FieldValue, Fields,
Ident, LitStr, Member, Path, Result, Token, Type, Visibility,
Data, DataEnum, DataStruct, DeriveInput, Expr, ExprCall, ExprPath, Field, Fields, Ident,
LitStr, Member, Path, Result, Token, Type, Visibility,
};
pub const EVENT: &str = "event";
@ -603,16 +603,16 @@ impl Parse for Require {
// This is a "value style" named-struct-like require
let content;
braced!(content in input);
let fields = Punctuated::<FieldValue, Token![,]>::parse_terminated(&content)?;
let tokens: TokenStream = quote::quote! (|| #path { #fields }).into();
let content = content.parse::<TokenStream2>()?;
let tokens: TokenStream = quote::quote! (|| #path { #content }).into();
Some(TokenStream2::from(tokens))
} else if input.peek(Paren) {
// This is a "value style" tuple-struct-like require
let content;
parenthesized!(content in input);
let content = content.parse::<TokenStream2>()?;
is_constructor_call = last_segment_is_lower;
let fields = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
let tokens: TokenStream = quote::quote! (|| #path (#fields)).into();
let tokens: TokenStream = quote::quote! (|| #path (#content)).into();
Some(TokenStream2::from(tokens))
} else if is_enum {
// if this is an enum, then it is an inline enum component declaration

View File

@ -166,7 +166,10 @@ use thiserror::Error;
/// #[derive(Component)]
/// #[require(
/// B(1), // tuple structs
/// C { value: 1 }, // named-field structs
/// C { // named-field structs
/// x: 1,
/// ..Default::default()
/// },
/// D::One, // enum variants
/// E::ONE, // associated consts
/// F::new(1) // constructors
@ -176,9 +179,10 @@ use thiserror::Error;
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct B(u8);
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// #[derive(Component, PartialEq, Eq, Debug, Default)]
/// struct C {
/// value: u8
/// x: u8,
/// y: u8,
/// }
///
/// #[derive(Component, PartialEq, Eq, Debug)]
@ -206,7 +210,7 @@ use thiserror::Error;
/// # let mut world = World::default();
/// let id = world.spawn(A).id();
/// assert_eq!(&B(1), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C { value: 1 }, world.entity(id).get::<C>().unwrap());
/// assert_eq!(&C { x: 1, y: 0 }, world.entity(id).get::<C>().unwrap());
/// assert_eq!(&D::One, world.entity(id).get::<D>().unwrap());
/// assert_eq!(&E(1), world.entity(id).get::<E>().unwrap());
/// assert_eq!(&F(1), world.entity(id).get::<F>().unwrap());