update syn, encase, glam and hexasphere (#8573)

# Objective

- Fixes #8282 
- Update `syn` to 2.0, `encase` to 0.6, `glam` to 0.24 and `hexasphere`
to 9.0


Blocked ~~on https://github.com/teoxoy/encase/pull/42~~ and ~~on
https://github.com/OptimisticPeach/hexasphere/pull/17~~

---------

Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com>
This commit is contained in:
François 2023-05-16 03:24:17 +02:00 committed by GitHub
parent 6b0986a6e8
commit 0736195a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 250 additions and 284 deletions

View File

@ -7,7 +7,7 @@ publish = false
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dev-dependencies] [dev-dependencies]
glam = "0.23" glam = "0.24"
rand = "0.8" rand = "0.8"
rand_chacha = "0.3" rand_chacha = "0.3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }

View File

@ -15,4 +15,4 @@ proc-macro = true
bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.11.0-dev" } bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.11.0-dev" }
quote = "1.0" quote = "1.0"
syn = { version = "1.0", features = ["full"] } syn = { version = "2.0", features = ["full"] }

View File

@ -11,6 +11,6 @@ proc-macro = true
[dependencies] [dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" } bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" }
syn = "1.0" syn = "2.0"
quote = "1.0" quote = "1.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"

View File

@ -1,8 +1,7 @@
use bevy_macro_utils::{get_lit_str, Symbol};
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens}; use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result}; use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, LitStr, Path, Result};
pub fn derive_resource(input: TokenStream) -> TokenStream { pub fn derive_resource(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput); let mut ast = parse_macro_input!(input as DeriveInput);
@ -48,8 +47,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
}) })
} }
pub const COMPONENT: Symbol = Symbol("component"); pub const COMPONENT: &str = "component";
pub const STORAGE: Symbol = Symbol("storage"); pub const STORAGE: &str = "storage";
struct Attrs { struct Attrs {
storage: StorageTy, storage: StorageTy,
@ -66,48 +65,27 @@ const TABLE: &str = "Table";
const SPARSE_SET: &str = "SparseSet"; const SPARSE_SET: &str = "SparseSet";
fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> { fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
let meta_items = bevy_macro_utils::parse_attrs(ast, COMPONENT)?;
let mut attrs = Attrs { let mut attrs = Attrs {
storage: StorageTy::Table, storage: StorageTy::Table,
}; };
for meta in meta_items { for meta in ast.attrs.iter().filter(|a| a.path().is_ident(COMPONENT)) {
use syn::{ meta.parse_nested_meta(|nested| {
Meta::NameValue, if nested.path.is_ident(STORAGE) {
NestedMeta::{Lit, Meta}, attrs.storage = match nested.value()?.parse::<LitStr>()?.value() {
}; s if s == TABLE => StorageTy::Table,
match meta { s if s == SPARSE_SET => StorageTy::SparseSet,
Meta(NameValue(m)) if m.path == STORAGE => {
attrs.storage = match get_lit_str(STORAGE, &m.lit)?.value().as_str() {
TABLE => StorageTy::Table,
SPARSE_SET => StorageTy::SparseSet,
s => { s => {
return Err(Error::new_spanned( return Err(nested.error(format!(
m.lit, "Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'.",
format!( )));
"Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'.",
),
))
} }
}; };
Ok(())
} else {
Err(nested.error("Unsuported attribute"))
} }
Meta(meta_item) => { })?;
return Err(Error::new_spanned(
meta_item.path(),
format!(
"unknown component attribute `{}`",
meta_item.path().into_token_stream()
),
));
}
Lit(lit) => {
return Err(Error::new_spanned(
lit,
"unexpected literal in component attribute",
))
}
}
} }
Ok(attrs) Ok(attrs)

View File

@ -6,7 +6,8 @@ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
parse_macro_input, parse_quote, parse_macro_input, parse_quote,
punctuated::Punctuated, punctuated::Punctuated,
Attribute, Data, DataStruct, DeriveInput, Field, Index, token::Comma,
Attribute, Data, DataStruct, DeriveInput, Field, Index, Meta,
}; };
use crate::bevy_ecs_path; use crate::bevy_ecs_path;
@ -14,7 +15,7 @@ use crate::bevy_ecs_path;
#[derive(Default)] #[derive(Default)]
struct FetchStructAttributes { struct FetchStructAttributes {
pub is_mutable: bool, pub is_mutable: bool,
pub derive_args: Punctuated<syn::NestedMeta, syn::token::Comma>, pub derive_args: Punctuated<syn::Meta, syn::token::Comma>,
} }
static MUTABLE_ATTRIBUTE_NAME: &str = "mutable"; static MUTABLE_ATTRIBUTE_NAME: &str = "mutable";
@ -35,7 +36,7 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
let mut fetch_struct_attributes = FetchStructAttributes::default(); let mut fetch_struct_attributes = FetchStructAttributes::default();
for attr in &ast.attrs { for attr in &ast.attrs {
if !attr if !attr
.path .path()
.get_ident() .get_ident()
.map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME) .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
{ {
@ -43,7 +44,7 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
} }
attr.parse_args_with(|input: ParseStream| { attr.parse_args_with(|input: ParseStream| {
let meta = input.parse_terminated::<syn::Meta, syn::token::Comma>(syn::Meta::parse)?; let meta = input.parse_terminated(syn::Meta::parse, Comma)?;
for meta in meta { for meta in meta {
let ident = meta.path().get_ident().unwrap_or_else(|| { let ident = meta.path().get_ident().unwrap_or_else(|| {
panic!( panic!(
@ -61,9 +62,10 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
} }
} else if ident == DERIVE_ATTRIBUTE_NAME { } else if ident == DERIVE_ATTRIBUTE_NAME {
if let syn::Meta::List(meta_list) = meta { if let syn::Meta::List(meta_list) = meta {
fetch_struct_attributes meta_list.parse_nested_meta(|meta| {
.derive_args fetch_struct_attributes.derive_args.push(Meta::Path(meta.path));
.extend(meta_list.nested.iter().cloned()); Ok(())
})?;
} else { } else {
panic!( panic!(
"Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute", "Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute",
@ -463,7 +465,7 @@ fn read_world_query_field_info(field: &Field) -> syn::Result<WorldQueryFieldInfo
let mut attrs = Vec::new(); let mut attrs = Vec::new();
for attr in &field.attrs { for attr in &field.attrs {
if attr if attr
.path .path()
.get_ident() .get_ident()
.map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME) .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
{ {

View File

@ -13,8 +13,8 @@ use proc_macro::TokenStream;
use proc_macro2::Span; use proc_macro2::Span;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{ use syn::{
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, ConstParam, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
DeriveInput, GenericParam, Ident, Index, Meta, MetaList, NestedMeta, Token, TypeParam, ConstParam, DeriveInput, GenericParam, Ident, Index, TypeParam,
}; };
enum BundleFieldKind { enum BundleFieldKind {
@ -37,28 +37,23 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
let mut field_kind = Vec::with_capacity(named_fields.len()); let mut field_kind = Vec::with_capacity(named_fields.len());
'field_loop: for field in named_fields.iter() { for field in named_fields.iter() {
for attr in &field.attrs { for attr in field
if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) { .attrs
if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() { .iter()
if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() { .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) { {
field_kind.push(BundleFieldKind::Ignore); if let Err(error) = attr.parse_nested_meta(|meta| {
continue 'field_loop; if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
} field_kind.push(BundleFieldKind::Ignore);
Ok(())
return syn::Error::new( } else {
path.span(), Err(meta.error(format!(
format!( "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
"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();
} }
}) {
return error.into_compile_error().into();
} }
} }
@ -308,7 +303,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect(); let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
let mut punctuated_generics = Punctuated::<_, Token![,]>::new(); let mut punctuated_generics = Punctuated::<_, Comma>::new();
punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g { punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
GenericParam::Type(g) => GenericParam::Type(TypeParam { GenericParam::Type(g) => GenericParam::Type(TypeParam {
default: None, default: None,
@ -321,14 +316,14 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
_ => unreachable!(), _ => unreachable!(),
})); }));
let mut punctuated_generic_idents = Punctuated::<_, Token![,]>::new(); let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g { punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
GenericParam::Type(g) => &g.ident, GenericParam::Type(g) => &g.ident,
GenericParam::Const(g) => &g.ident, GenericParam::Const(g) => &g.ident,
_ => unreachable!(), _ => unreachable!(),
})); }));
let punctuated_generics_no_bounds: Punctuated<_, Token![,]> = lifetimeless_generics let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
.iter() .iter()
.map(|&g| match g.clone() { .map(|&g| match g.clone() {
GenericParam::Type(mut g) => { GenericParam::Type(mut g) => {

View File

@ -13,4 +13,4 @@ proc-macro = true
[dependencies] [dependencies]
bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.11.0-dev" } bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.11.0-dev" }
encase_derive_impl = "0.5.0" encase_derive_impl = "0.6.1"

View File

@ -10,6 +10,6 @@ keywords = ["bevy"]
[dependencies] [dependencies]
toml_edit = "0.19" toml_edit = "0.19"
syn = "1.0" syn = "2.0"
quote = "1.0" quote = "1.0"
rustc-hash = "1.0" rustc-hash = "1.0"

View File

@ -1,41 +1,32 @@
use syn::DeriveInput; use syn::{Expr, ExprLit, Lit};
use crate::symbol::Symbol; use crate::symbol::Symbol;
pub fn parse_attrs(ast: &DeriveInput, attr_name: Symbol) -> syn::Result<Vec<syn::NestedMeta>> { pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr> {
let mut list = Vec::new(); if let Expr::Lit(ExprLit {
for attr in ast.attrs.iter().filter(|a| a.path == attr_name) { lit: Lit::Str(lit), ..
match attr.parse_meta()? { }) = &value
syn::Meta::List(meta) => list.extend(meta.nested.into_iter()), {
other => {
return Err(syn::Error::new_spanned(
other,
format!("expected #[{attr_name}(...)]"),
))
}
}
}
Ok(list)
}
pub fn get_lit_str(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<&syn::LitStr> {
if let syn::Lit::Str(lit) = lit {
Ok(lit) Ok(lit)
} else { } else {
Err(syn::Error::new_spanned( Err(syn::Error::new_spanned(
lit, value,
format!("expected {attr_name} attribute to be a string: `{attr_name} = \"...\"`"), format!("expected {attr_name} attribute to be a string: `{attr_name} = \"...\"`"),
)) ))
} }
} }
pub fn get_lit_bool(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<bool> { pub fn get_lit_bool(attr_name: Symbol, value: &Expr) -> syn::Result<bool> {
if let syn::Lit::Bool(lit) = lit { if let Expr::Lit(ExprLit {
lit: Lit::Bool(lit),
..
}) = &value
{
Ok(lit.value()) Ok(lit.value())
} else { } else {
Err(syn::Error::new_spanned( Err(syn::Error::new_spanned(
lit, value,
format!("expected {attr_name} attribute to be a bool value, `true` or `false`: `{attr_name} = ...`"), format!("expected {attr_name} attribute to be a bool value, `true` or `false`: `{attr_name} = ...`"),
)) ))?
} }
} }

View File

@ -214,7 +214,7 @@ pub fn derive_label(
) -> TokenStream { ) -> TokenStream {
// return true if the variant specified is an `ignore_fields` attribute // return true if the variant specified is an `ignore_fields` attribute
fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool { fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool {
if attr.path.get_ident().as_ref().unwrap() != &attr_name { if attr.path().get_ident().as_ref().unwrap() != &attr_name {
return false; return false;
} }

View File

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[dependencies] [dependencies]
glam = { version = "0.23", features = ["bytemuck"] } glam = { version = "0.24", features = ["bytemuck"] }
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
[features] [features]

View File

@ -11,7 +11,7 @@ license = "Zlib AND (MIT OR Apache-2.0)"
keywords = ["bevy", "3D", "graphics", "algorithm", "tangent"] keywords = ["bevy", "3D", "graphics", "algorithm", "tangent"]
[dependencies] [dependencies]
glam = "0.23" glam = "0.24"
[[example]] [[example]]
name = "generate" name = "generate"

View File

@ -31,7 +31,7 @@ thiserror = "1.0"
once_cell = "1.11" once_cell = "1.11"
serde = "1" serde = "1"
smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true } smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true }
glam = { version = "0.23", features = ["serde"], optional = true } glam = { version = "0.24", features = ["serde"], optional = true }
[dev-dependencies] [dev-dependencies]
ron = "0.8.0" ron = "0.8.0"

View File

@ -19,7 +19,7 @@ documentation = []
[dependencies] [dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" } bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" }
syn = { version = "1.0", features = ["full"] } syn = { version = "2.0", features = ["full"] }
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
uuid = { version = "1.1", features = ["v4"] } uuid = { version = "1.1", features = ["v4"] }

View File

@ -13,7 +13,7 @@ use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::token::Comma; use syn::token::Comma;
use syn::{Meta, NestedMeta, Path}; use syn::{Meta, Path};
// The "special" trait idents that are used internally for reflection. // The "special" trait idents that are used internally for reflection.
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]` // Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
@ -45,12 +45,16 @@ pub(crate) enum TraitImpl {
impl TraitImpl { impl TraitImpl {
/// Merges this [`TraitImpl`] with another. /// Merges this [`TraitImpl`] with another.
/// ///
/// Returns whichever value is not [`TraitImpl::NotImplemented`]. /// Update `self` with whichever value is not [`TraitImpl::NotImplemented`].
/// If both values are [`TraitImpl::NotImplemented`], then that is returned. /// If `other` is [`TraitImpl::NotImplemented`], then `self` is not modified.
/// Otherwise, an error is returned if neither value is [`TraitImpl::NotImplemented`]. /// An error is returned if neither value is [`TraitImpl::NotImplemented`].
pub fn merge(self, other: TraitImpl) -> Result<TraitImpl, syn::Error> { pub fn merge(&mut self, other: TraitImpl) -> Result<(), syn::Error> {
match (self, other) { match (&self, other) {
(TraitImpl::NotImplemented, value) | (value, TraitImpl::NotImplemented) => Ok(value), (TraitImpl::NotImplemented, value) => {
*self = value;
Ok(())
}
(_, TraitImpl::NotImplemented) => Ok(()),
(_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => { (_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => {
Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE)) Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE))
} }
@ -128,15 +132,12 @@ pub(crate) struct ReflectTraits {
} }
impl ReflectTraits { impl ReflectTraits {
/// Create a new [`ReflectTraits`] instance from a set of nested metas. pub fn from_metas(metas: Punctuated<Meta, Comma>) -> Result<Self, syn::Error> {
pub fn from_nested_metas(
nested_metas: &Punctuated<NestedMeta, Comma>,
) -> Result<Self, syn::Error> {
let mut traits = ReflectTraits::default(); let mut traits = ReflectTraits::default();
for nested_meta in nested_metas.iter() { for meta in &metas {
match nested_meta { match meta {
// Handles `#[reflect( Hash, Default, ... )]` // Handles `#[reflect( Hash, Default, ... )]`
NestedMeta::Meta(Meta::Path(path)) => { Meta::Path(path) => {
// Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`)
let Some(segment) = path.segments.iter().next() else { let Some(segment) = path.segments.iter().next() else {
continue; continue;
@ -149,14 +150,13 @@ impl ReflectTraits {
match ident_name.as_str() { match ident_name.as_str() {
DEBUG_ATTR => { DEBUG_ATTR => {
traits.debug = traits.debug.merge(TraitImpl::Implemented(span))?; traits.debug.merge(TraitImpl::Implemented(span))?;
} }
PARTIAL_EQ_ATTR => { PARTIAL_EQ_ATTR => {
traits.partial_eq = traits.partial_eq.merge(TraitImpl::Implemented(span))?;
traits.partial_eq.merge(TraitImpl::Implemented(span))?;
} }
HASH_ATTR => { HASH_ATTR => {
traits.hash = traits.hash.merge(TraitImpl::Implemented(span))?; traits.hash.merge(TraitImpl::Implemented(span))?;
} }
// We only track reflected idents for traits not considered special // We only track reflected idents for traits not considered special
_ => { _ => {
@ -170,7 +170,7 @@ impl ReflectTraits {
} }
} }
// Handles `#[reflect( Hash(custom_hash_fn) )]` // Handles `#[reflect( Hash(custom_hash_fn) )]`
NestedMeta::Meta(Meta::List(list)) => { Meta::List(list) => {
// Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`)
let Some(segment) = list.path.segments.iter().next() else { let Some(segment) = list.path.segments.iter().next() else {
continue; continue;
@ -181,23 +181,25 @@ impl ReflectTraits {
// Track the span where the trait is implemented for future errors // Track the span where the trait is implemented for future errors
let span = ident.span(); let span = ident.span();
let list_meta = list.nested.iter().next(); list.parse_nested_meta(|meta| {
if let Some(NestedMeta::Meta(Meta::Path(path))) = list_meta {
// This should be the path of the custom function // This should be the path of the custom function
let trait_func_ident = TraitImpl::Custom(path.clone(), span); let trait_func_ident = TraitImpl::Custom(meta.path, span);
match ident.as_str() { match ident.as_str() {
DEBUG_ATTR => { DEBUG_ATTR => {
traits.debug = traits.debug.merge(trait_func_ident)?; traits.debug.merge(trait_func_ident)?;
} }
PARTIAL_EQ_ATTR => { PARTIAL_EQ_ATTR => {
traits.partial_eq = traits.partial_eq.merge(trait_func_ident)?; traits.partial_eq.merge(trait_func_ident)?;
} }
HASH_ATTR => { HASH_ATTR => {
traits.hash = traits.hash.merge(trait_func_ident)?; traits.hash.merge(trait_func_ident)?;
}
_ => {
return Err(syn::Error::new(span, "Can only use custom functions for special traits (i.e. `Hash`, `PartialEq`, `Debug`)"));
} }
_ => {}
} }
} Ok(())
})?;
} }
_ => {} _ => {}
} }
@ -289,26 +291,20 @@ impl ReflectTraits {
/// Merges the trait implementations of this [`ReflectTraits`] with another one. /// Merges the trait implementations of this [`ReflectTraits`] with another one.
/// ///
/// An error is returned if the two [`ReflectTraits`] have conflicting implementations. /// An error is returned if the two [`ReflectTraits`] have conflicting implementations.
pub fn merge(self, other: ReflectTraits) -> Result<Self, syn::Error> { pub fn merge(&mut self, other: ReflectTraits) -> Result<(), syn::Error> {
Ok(ReflectTraits { self.debug.merge(other.debug)?;
debug: self.debug.merge(other.debug)?, self.hash.merge(other.hash)?;
hash: self.hash.merge(other.hash)?, self.partial_eq.merge(other.partial_eq)?;
partial_eq: self.partial_eq.merge(other.partial_eq)?, for ident in other.idents {
idents: { add_unique_ident(&mut self.idents, ident)?;
let mut idents = self.idents; }
for ident in other.idents { Ok(())
add_unique_ident(&mut idents, ident)?;
}
idents
},
})
} }
} }
impl Parse for ReflectTraits { impl Parse for ReflectTraits {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let result = Punctuated::<NestedMeta, Comma>::parse_terminated(input)?; ReflectTraits::from_metas(Punctuated::<Meta, Comma>::parse_terminated(input)?)
ReflectTraits::from_nested_metas(&result)
} }
} }

View File

@ -4,11 +4,12 @@ use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync};
use crate::utility::{members_to_serialization_denylist, WhereClauseOptions}; use crate::utility::{members_to_serialization_denylist, WhereClauseOptions};
use bit_set::BitSet; use bit_set::BitSet;
use quote::quote; use quote::quote;
use syn::token::Comma;
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant}; use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Variant};
pub(crate) enum ReflectDerive<'a> { pub(crate) enum ReflectDerive<'a> {
Struct(ReflectStruct<'a>), Struct(ReflectStruct<'a>),
@ -136,8 +137,8 @@ impl<'a> ReflectDerive<'a> {
#[cfg(feature = "documentation")] #[cfg(feature = "documentation")]
let mut doc = crate::documentation::Documentation::default(); let mut doc = crate::documentation::Documentation::default();
for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { for attribute in &input.attrs {
match attribute { match &attribute.meta {
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => { Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
if !matches!(reflect_mode, None | Some(ReflectMode::Normal)) { if !matches!(reflect_mode, None | Some(ReflectMode::Normal)) {
return Err(syn::Error::new( return Err(syn::Error::new(
@ -147,8 +148,10 @@ impl<'a> ReflectDerive<'a> {
} }
reflect_mode = Some(ReflectMode::Normal); reflect_mode = Some(ReflectMode::Normal);
let new_traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; let new_traits = ReflectTraits::from_metas(
traits = traits.merge(new_traits)?; meta_list.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)?,
)?;
traits.merge(new_traits)?;
} }
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => { Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) { if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
@ -159,8 +162,10 @@ impl<'a> ReflectDerive<'a> {
} }
reflect_mode = Some(ReflectMode::Value); reflect_mode = Some(ReflectMode::Value);
let new_traits = ReflectTraits::from_nested_metas(&meta_list.nested)?; let new_traits = ReflectTraits::from_metas(
traits = traits.merge(new_traits)?; meta_list.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)?,
)?;
traits.merge(new_traits)?;
} }
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => { Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) { if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
@ -174,7 +179,11 @@ impl<'a> ReflectDerive<'a> {
} }
#[cfg(feature = "documentation")] #[cfg(feature = "documentation")]
Meta::NameValue(pair) if pair.path.is_ident("doc") => { Meta::NameValue(pair) if pair.path.is_ident("doc") => {
if let syn::Lit::Str(lit) = pair.lit { if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value
{
doc.push(lit.value()); doc.push(lit.value());
} }
} }
@ -247,7 +256,7 @@ impl<'a> ReflectDerive<'a> {
} }
fn collect_enum_variants( fn collect_enum_variants(
variants: &'a Punctuated<Variant, Token![,]>, variants: &'a Punctuated<Variant, Comma>,
) -> Result<Vec<EnumVariant<'a>>, syn::Error> { ) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
let sifter: utility::ResultSifter<EnumVariant<'a>> = variants let sifter: utility::ResultSifter<EnumVariant<'a>> = variants
.iter() .iter()

View File

@ -3,7 +3,7 @@
use crate::fq_std::FQOption; use crate::fq_std::FQOption;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{Attribute, Lit, Meta}; use syn::{Attribute, Expr, ExprLit, Lit, Meta};
/// A struct used to represent a type's documentation, if any. /// A struct used to represent a type's documentation, if any.
/// ///
@ -21,18 +21,18 @@ impl Documentation {
pub fn from_attributes<'a>(attributes: impl IntoIterator<Item = &'a Attribute>) -> Self { pub fn from_attributes<'a>(attributes: impl IntoIterator<Item = &'a Attribute>) -> Self {
let docs = attributes let docs = attributes
.into_iter() .into_iter()
.filter_map(|attr| { .filter_map(|attr| match &attr.meta {
let meta = attr.parse_meta().ok()?; Meta::NameValue(pair) if pair.path.is_ident("doc") => {
match meta { if let Expr::Lit(ExprLit {
Meta::NameValue(pair) if pair.path.is_ident("doc") => { lit: Lit::Str(lit), ..
if let Lit::Str(lit) = pair.lit { }) = &pair.value
Some(lit.value()) {
} else { Some(lit.value())
None } else {
} None
} }
_ => None,
} }
_ => None,
}) })
.collect(); .collect();

View File

@ -7,7 +7,7 @@
use crate::REFLECT_ATTRIBUTE_NAME; use crate::REFLECT_ATTRIBUTE_NAME;
use quote::ToTokens; use quote::ToTokens;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{Attribute, Lit, Meta, NestedMeta}; use syn::{Attribute, Expr, ExprLit, Lit, Meta};
pub(crate) static IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing"; pub(crate) static IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
pub(crate) static IGNORE_ALL_ATTR: &str = "ignore"; pub(crate) static IGNORE_ALL_ATTR: &str = "ignore";
@ -76,10 +76,9 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result<ReflectFieldAttr,
let attrs = attrs let attrs = attrs
.iter() .iter()
.filter(|a| a.path.is_ident(REFLECT_ATTRIBUTE_NAME)); .filter(|a| a.path().is_ident(REFLECT_ATTRIBUTE_NAME));
for attr in attrs { for attr in attrs {
let meta = attr.parse_meta()?; if let Err(err) = parse_meta(&mut args, &attr.meta) {
if let Err(err) = parse_meta(&mut args, &meta) {
if let Some(ref mut error) = errors { if let Some(ref mut error) = errors {
error.combine(err); error.combine(err);
} else { } else {
@ -117,18 +116,15 @@ fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error
format!("unknown attribute parameter: {}", path.to_token_stream()), format!("unknown attribute parameter: {}", path.to_token_stream()),
)), )),
Meta::NameValue(pair) if pair.path.is_ident(DEFAULT_ATTR) => { Meta::NameValue(pair) if pair.path.is_ident(DEFAULT_ATTR) => {
let lit = &pair.lit; if let Expr::Lit(ExprLit {lit: Lit::Str(lit_str), ..}) = &pair.value {
match lit { args.default = DefaultBehavior::Func(lit_str.parse()?);
Lit::Str(lit_str) => { Ok(())
args.default = DefaultBehavior::Func(lit_str.parse()?); }
Ok(()) else {
} Err(syn::Error::new(
err => { pair.span(),
Err(syn::Error::new( format!("expected a string literal containing the name of a function, but found: {}", pair.to_token_stream()),
err.span(), ))?
format!("expected a string literal containing the name of a function, but found: {}", err.to_token_stream()),
))
}
} }
} }
Meta::NameValue(pair) => { Meta::NameValue(pair) => {
@ -142,10 +138,8 @@ fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error
Err(syn::Error::new(list.path.span(), "unexpected property")) Err(syn::Error::new(list.path.span(), "unexpected property"))
} }
Meta::List(list) => { Meta::List(list) => {
for nested in &list.nested { if let Ok(meta) = list.parse_args() {
if let NestedMeta::Meta(meta) = nested { parse_meta(args, &meta)?;
parse_meta(args, meta)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -3,6 +3,7 @@ extern crate proc_macro;
use bevy_macro_utils::BevyManifest; use bevy_macro_utils::BevyManifest;
use quote::quote; use quote::quote;
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::token::Comma;
use syn::*; use syn::*;
use uuid::Uuid; use uuid::Uuid;
@ -15,22 +16,13 @@ pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::To
let type_ident = ast.ident; let type_ident = ast.ident;
let mut uuid = None; let mut uuid = None;
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { for attribute in ast.attrs.iter().filter(|attr| attr.path().is_ident("uuid")) {
let Meta::NameValue(name_value) = attribute else { let Meta::NameValue(ref name_value) = attribute.meta else {
continue; continue;
}; };
if name_value let uuid_str = match &name_value.value {
.path Expr::Lit(ExprLit{lit: Lit::Str(lit_str), ..}) => lit_str,
.get_ident()
.map(|i| i != "uuid")
.unwrap_or(true)
{
continue;
}
let uuid_str = match name_value.lit {
Lit::Str(lit_str) => lit_str,
_ => panic!("`uuid` attribute must take the form `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"`."), _ => panic!("`uuid` attribute must take the form `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"`."),
}; };
@ -101,7 +93,7 @@ impl Parse for TypeUuidDef {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let type_ident = input.parse::<Ident>()?; let type_ident = input.parse::<Ident>()?;
let generics = input.parse::<Generics>()?; let generics = input.parse::<Generics>()?;
input.parse::<Token![,]>()?; input.parse::<Comma>()?;
let uuid = input.parse::<LitStr>()?.value(); let uuid = input.parse::<LitStr>()?.value();
let uuid = Uuid::parse_str(&uuid).map_err(|err| input.error(format!("{err}")))?; let uuid = Uuid::parse_str(&uuid).map_err(|err| input.error(format!("{err}")))?;

View File

@ -1636,6 +1636,24 @@ bevy_reflect::tests::should_reflect_debug::Test {
assert_eq!("Foo".to_string(), format!("{foo:?}")); assert_eq!("Foo".to_string(), format!("{foo:?}"));
} }
#[test]
fn custom_debug_function() {
#[derive(Reflect)]
#[reflect(Debug(custom_debug))]
struct Foo {
a: u32,
}
fn custom_debug(_x: &Foo, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "123")
}
let foo = Foo { a: 1 };
let foo: &dyn Reflect = &foo;
assert_eq!("123", format!("{:?}", foo));
}
#[cfg(feature = "glam")] #[cfg(feature = "glam")]
mod glam { mod glam {
use super::*; use super::*;

View File

@ -69,7 +69,7 @@ thread_local = "1.1"
thiserror = "1.0" thiserror = "1.0"
futures-lite = "1.4.0" futures-lite = "1.4.0"
anyhow = "1.0" anyhow = "1.0"
hexasphere = "8.1" hexasphere = "9.0"
parking_lot = "0.12.1" parking_lot = "0.12.1"
regex = "1.5" regex = "1.5"
ddsfile = { version = "0.5.0", optional = true } ddsfile = { version = "0.5.0", optional = true }
@ -79,7 +79,7 @@ flate2 = { version = "1.0.22", optional = true }
ruzstd = { version = "0.2.4", optional = true } ruzstd = { version = "0.2.4", optional = true }
# For transcoding of UASTC/ETC1S universal formats, and for .basis file support # For transcoding of UASTC/ETC1S universal formats, and for .basis file support
basis-universal = { version = "0.2.0", optional = true } basis-universal = { version = "0.2.0", optional = true }
encase = { version = "0.5", features = ["glam"] } encase = { version = "0.6.1", features = ["glam"] }
# For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans. # For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans.
profiling = { version = "1", features = ["profile-with-tracing"], optional = true } profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
async-channel = "1.8" async-channel = "1.8"

View File

@ -14,6 +14,6 @@ proc-macro = true
[dependencies] [dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" } bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" }
syn = "1.0" syn = "2.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"

View File

@ -5,7 +5,8 @@ use quote::{quote, ToTokens};
use syn::{ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
punctuated::Punctuated, punctuated::Punctuated,
Data, DataStruct, Error, Fields, LitInt, LitStr, NestedMeta, Result, Token, token::Comma,
Data, DataStruct, Error, Fields, LitInt, LitStr, Meta, Result,
}; };
const UNIFORM_ATTRIBUTE_NAME: Symbol = Symbol("uniform"); const UNIFORM_ATTRIBUTE_NAME: Symbol = Symbol("uniform");
@ -48,7 +49,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
// Read struct-level attributes // Read struct-level attributes
for attr in &ast.attrs { for attr in &ast.attrs {
if let Some(attr_ident) = attr.path.get_ident() { if let Some(attr_ident) = attr.path().get_ident() {
if attr_ident == BIND_GROUP_DATA_ATTRIBUTE_NAME { if attr_ident == BIND_GROUP_DATA_ATTRIBUTE_NAME {
if let Ok(prepared_data_ident) = if let Ok(prepared_data_ident) =
attr.parse_args_with(|input: ParseStream| input.parse::<Ident>()) attr.parse_args_with(|input: ParseStream| input.parse::<Ident>())
@ -117,7 +118,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
// Read field-level attributes // Read field-level attributes
for field in fields.iter() { for field in fields.iter() {
for attr in &field.attrs { for attr in &field.attrs {
let Some(attr_ident) = attr.path.get_ident() else { let Some(attr_ident) = attr.path().get_ident() else {
continue; continue;
}; };
@ -462,14 +463,14 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
/// like `#[uniform(LitInt, Ident)]` /// like `#[uniform(LitInt, Ident)]`
struct UniformBindingMeta { struct UniformBindingMeta {
lit_int: LitInt, lit_int: LitInt,
_comma: Token![,], _comma: Comma,
ident: Ident, ident: Ident,
} }
/// Represents the arguments for any general binding attribute. /// Represents the arguments for any general binding attribute.
/// ///
/// If parsed, represents an attribute /// If parsed, represents an attribute
/// like `#[foo(LitInt, ...)]` where the rest is optional [`NestedMeta`]. /// like `#[foo(LitInt, ...)]` where the rest is optional [`Meta`].
enum BindingMeta { enum BindingMeta {
IndexOnly(LitInt), IndexOnly(LitInt),
IndexWithOptions(BindingIndexOptions), IndexWithOptions(BindingIndexOptions),
@ -480,13 +481,13 @@ enum BindingMeta {
/// This represents, for example, `#[texture(0, dimension = "2d_array")]`. /// This represents, for example, `#[texture(0, dimension = "2d_array")]`.
struct BindingIndexOptions { struct BindingIndexOptions {
lit_int: LitInt, lit_int: LitInt,
_comma: Token![,], _comma: Comma,
meta_list: Punctuated<NestedMeta, Token![,]>, meta_list: Punctuated<Meta, Comma>,
} }
impl Parse for BindingMeta { impl Parse for BindingMeta {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
if input.peek2(Token![,]) { if input.peek2(Comma) {
input.parse().map(Self::IndexWithOptions) input.parse().map(Self::IndexWithOptions)
} else { } else {
input.parse().map(Self::IndexOnly) input.parse().map(Self::IndexOnly)
@ -499,7 +500,7 @@ impl Parse for BindingIndexOptions {
Ok(Self { Ok(Self {
lit_int: input.parse()?, lit_int: input.parse()?,
_comma: input.parse()?, _comma: input.parse()?,
meta_list: input.parse_terminated(NestedMeta::parse)?, meta_list: input.parse_terminated(Meta::parse, Comma)?,
}) })
} }
} }
@ -523,7 +524,7 @@ fn get_uniform_binding_attr(attr: &syn::Attribute) -> Result<(u32, Ident)> {
Ok((binding_index, ident)) Ok((binding_index, ident))
} }
fn get_binding_nested_attr(attr: &syn::Attribute) -> Result<(u32, Vec<NestedMeta>)> { fn get_binding_nested_attr(attr: &syn::Attribute) -> Result<(u32, Vec<Meta>)> {
let binding_meta = attr.parse_args_with(BindingMeta::parse)?; let binding_meta = attr.parse_args_with(BindingMeta::parse)?;
match binding_meta { match binding_meta {
@ -598,43 +599,39 @@ const VISIBILITY_COMPUTE: Symbol = Symbol("compute");
const VISIBILITY_ALL: Symbol = Symbol("all"); const VISIBILITY_ALL: Symbol = Symbol("all");
const VISIBILITY_NONE: Symbol = Symbol("none"); const VISIBILITY_NONE: Symbol = Symbol("none");
fn get_visibility_flag_value( fn get_visibility_flag_value(meta: Meta) -> Result<ShaderStageVisibility> {
nested_metas: &Punctuated<NestedMeta, Token![,]>,
) -> Result<ShaderStageVisibility> {
let mut visibility = VisibilityFlags::vertex_fragment(); let mut visibility = VisibilityFlags::vertex_fragment();
for meta in nested_metas { use syn::Meta::Path;
use syn::{Meta::Path, NestedMeta::Meta}; match meta {
match meta { // Parse `#[visibility(all)]`.
// Parse `visibility(all)]`. Path(path) if path == VISIBILITY_ALL => {
Meta(Path(path)) if path == VISIBILITY_ALL => { return Ok(ShaderStageVisibility::All)
return Ok(ShaderStageVisibility::All)
}
// Parse `visibility(none)]`.
Meta(Path(path)) if path == VISIBILITY_NONE => {
return Ok(ShaderStageVisibility::None)
}
// Parse `visibility(vertex, ...)]`.
Meta(Path(path)) if path == VISIBILITY_VERTEX => {
visibility.vertex = true;
}
// Parse `visibility(fragment, ...)]`.
Meta(Path(path)) if path == VISIBILITY_FRAGMENT => {
visibility.fragment = true;
}
// Parse `visibility(compute, ...)]`.
Meta(Path(path)) if path == VISIBILITY_COMPUTE => {
visibility.compute = true;
}
Meta(Path(path)) => return Err(Error::new_spanned(
path,
"Not a valid visibility flag. Must be `all`, `none`, or a list-combination of `vertex`, `fragment` and/or `compute`."
)),
_ => return Err(Error::new_spanned(
meta,
"Invalid visibility format: `visibility(...)`.",
)),
} }
// Parse `#[visibility(none)]`.
Path(path) if path == VISIBILITY_NONE => {
return Ok(ShaderStageVisibility::None)
}
// Parse `#[visibility(vertex, ...)]`.
Path(path) if path == VISIBILITY_VERTEX => {
visibility.vertex = true;
}
// Parse `#[visibility(fragment, ...)]`.
Path(path) if path == VISIBILITY_FRAGMENT => {
visibility.fragment = true;
}
// Parse `#[visibility(compute, ...)]`.
Path(path) if path == VISIBILITY_COMPUTE => {
visibility.compute = true;
}
Path(path) => return Err(Error::new_spanned(
path,
"Not a valid visibility flag. Must be `all`, `none`, or a list-combination of `vertex`, `fragment` and/or `compute`."
)),
_ => return Err(Error::new_spanned(
meta,
"Invalid visibility format: `visibility(...)`.",
)),
} }
Ok(ShaderStageVisibility::Flags(visibility)) Ok(ShaderStageVisibility::Flags(visibility))
@ -727,7 +724,7 @@ const DEPTH: &str = "depth";
const S_INT: &str = "s_int"; const S_INT: &str = "s_int";
const U_INT: &str = "u_int"; const U_INT: &str = "u_int";
fn get_texture_attrs(metas: Vec<NestedMeta>) -> Result<TextureAttrs> { fn get_texture_attrs(metas: Vec<Meta>) -> Result<TextureAttrs> {
let mut dimension = Default::default(); let mut dimension = Default::default();
let mut sample_type = Default::default(); let mut sample_type = Default::default();
let mut multisampled = Default::default(); let mut multisampled = Default::default();
@ -737,35 +734,32 @@ fn get_texture_attrs(metas: Vec<NestedMeta>) -> Result<TextureAttrs> {
let mut visibility = ShaderStageVisibility::vertex_fragment(); let mut visibility = ShaderStageVisibility::vertex_fragment();
for meta in metas { for meta in metas {
use syn::{ use syn::Meta::{List, NameValue};
Meta::{List, NameValue},
NestedMeta::Meta,
};
match meta { match meta {
// Parse #[texture(0, dimension = "...")]. // Parse #[texture(0, dimension = "...")].
Meta(NameValue(m)) if m.path == DIMENSION => { NameValue(m) if m.path == DIMENSION => {
let value = get_lit_str(DIMENSION, &m.lit)?; let value = get_lit_str(DIMENSION, &m.value)?;
dimension = get_texture_dimension_value(value)?; dimension = get_texture_dimension_value(value)?;
} }
// Parse #[texture(0, sample_type = "...")]. // Parse #[texture(0, sample_type = "...")].
Meta(NameValue(m)) if m.path == SAMPLE_TYPE => { NameValue(m) if m.path == SAMPLE_TYPE => {
let value = get_lit_str(SAMPLE_TYPE, &m.lit)?; let value = get_lit_str(SAMPLE_TYPE, &m.value)?;
sample_type = get_texture_sample_type_value(value)?; sample_type = get_texture_sample_type_value(value)?;
} }
// Parse #[texture(0, multisampled = "...")]. // Parse #[texture(0, multisampled = "...")].
Meta(NameValue(m)) if m.path == MULTISAMPLED => { NameValue(m) if m.path == MULTISAMPLED => {
multisampled = get_lit_bool(MULTISAMPLED, &m.lit)?; multisampled = get_lit_bool(MULTISAMPLED, &m.value)?;
} }
// Parse #[texture(0, filterable = "...")]. // Parse #[texture(0, filterable = "...")].
Meta(NameValue(m)) if m.path == FILTERABLE => { NameValue(m) if m.path == FILTERABLE => {
filterable = get_lit_bool(FILTERABLE, &m.lit)?.into(); filterable = get_lit_bool(FILTERABLE, &m.value)?.into();
filterable_ident = m.path.into(); filterable_ident = m.path.into();
} }
// Parse #[texture(0, visibility(...))]. // Parse #[texture(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => { List(m) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?; visibility = get_visibility_flag_value(Meta::Path(m.path))?;
} }
Meta(NameValue(m)) => { NameValue(m) => {
return Err(Error::new_spanned( return Err(Error::new_spanned(
m.path, m.path,
"Not a valid name. Available attributes: `dimension`, `sample_type`, `multisampled`, or `filterable`." "Not a valid name. Available attributes: `dimension`, `sample_type`, `multisampled`, or `filterable`."
@ -865,26 +859,23 @@ const FILTERING: &str = "filtering";
const NON_FILTERING: &str = "non_filtering"; const NON_FILTERING: &str = "non_filtering";
const COMPARISON: &str = "comparison"; const COMPARISON: &str = "comparison";
fn get_sampler_attrs(metas: Vec<NestedMeta>) -> Result<SamplerAttrs> { fn get_sampler_attrs(metas: Vec<Meta>) -> Result<SamplerAttrs> {
let mut sampler_binding_type = Default::default(); let mut sampler_binding_type = Default::default();
let mut visibility = ShaderStageVisibility::vertex_fragment(); let mut visibility = ShaderStageVisibility::vertex_fragment();
for meta in metas { for meta in metas {
use syn::{ use syn::Meta::{List, NameValue};
Meta::{List, NameValue},
NestedMeta::Meta,
};
match meta { match meta {
// Parse #[sampler(0, sampler_type = "..."))]. // Parse #[sampler(0, sampler_type = "..."))].
Meta(NameValue(m)) if m.path == SAMPLER_TYPE => { NameValue(m) if m.path == SAMPLER_TYPE => {
let value = get_lit_str(DIMENSION, &m.lit)?; let value = get_lit_str(DIMENSION, &m.value)?;
sampler_binding_type = get_sampler_binding_type_value(value)?; sampler_binding_type = get_sampler_binding_type_value(value)?;
} }
// Parse #[sampler(0, visibility(...))]. // Parse #[sampler(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => { List(m) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?; visibility = get_visibility_flag_value(Meta::Path(m.path))?;
} }
Meta(NameValue(m)) => { NameValue(m) => {
return Err(Error::new_spanned( return Err(Error::new_spanned(
m.path, m.path,
"Not a valid name. Available attributes: `sampler_type`.", "Not a valid name. Available attributes: `sampler_type`.",
@ -928,22 +919,22 @@ struct StorageAttrs {
const READ_ONLY: Symbol = Symbol("read_only"); const READ_ONLY: Symbol = Symbol("read_only");
const BUFFER: Symbol = Symbol("buffer"); const BUFFER: Symbol = Symbol("buffer");
fn get_storage_binding_attr(metas: Vec<NestedMeta>) -> Result<StorageAttrs> { fn get_storage_binding_attr(metas: Vec<Meta>) -> Result<StorageAttrs> {
let mut visibility = ShaderStageVisibility::vertex_fragment(); let mut visibility = ShaderStageVisibility::vertex_fragment();
let mut read_only = false; let mut read_only = false;
let mut buffer = false; let mut buffer = false;
for meta in metas { for meta in metas {
use syn::{Meta::List, Meta::Path, NestedMeta::Meta}; use syn::{Meta::List, Meta::Path};
match meta { match meta {
// Parse #[storage(0, visibility(...))]. // Parse #[storage(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => { List(m) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?; visibility = get_visibility_flag_value(Meta::Path(m.path))?;
} }
Meta(Path(path)) if path == READ_ONLY => { Path(path) if path == READ_ONLY => {
read_only = true; read_only = true;
} }
Meta(Path(path)) if path == BUFFER => { Path(path) if path == BUFFER => {
buffer = true; buffer = true;
} }
_ => { _ => {

View File

@ -20,7 +20,7 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream {
let filter = if let Some(attr) = ast let filter = if let Some(attr) = ast
.attrs .attrs
.iter() .iter()
.find(|a| a.path.is_ident("extract_component_filter")) .find(|a| a.path().is_ident("extract_component_filter"))
{ {
let filter = match attr.parse_args::<syn::Type>() { let filter = match attr.parse_args::<syn::Type>() {
Ok(filter) => filter, Ok(filter) => filter,

View File

@ -9,6 +9,6 @@ license = "MIT OR Apache-2.0"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = "1.0" syn = "2.0"
quote = "1.0" quote = "1.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"