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"
[dev-dependencies]
glam = "0.23"
glam = "0.24"
rand = "0.8"
rand_chacha = "0.3"
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" }
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]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" }
syn = "1.0"
syn = "2.0"
quote = "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_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, LitStr, Path, Result};
pub fn derive_resource(input: TokenStream) -> TokenStream {
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 STORAGE: Symbol = Symbol("storage");
pub const COMPONENT: &str = "component";
pub const STORAGE: &str = "storage";
struct Attrs {
storage: StorageTy,
@ -66,48 +65,27 @@ const TABLE: &str = "Table";
const SPARSE_SET: &str = "SparseSet";
fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
let meta_items = bevy_macro_utils::parse_attrs(ast, COMPONENT)?;
let mut attrs = Attrs {
storage: StorageTy::Table,
};
for meta in meta_items {
use syn::{
Meta::NameValue,
NestedMeta::{Lit, Meta},
};
match meta {
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,
for meta in ast.attrs.iter().filter(|a| a.path().is_ident(COMPONENT)) {
meta.parse_nested_meta(|nested| {
if nested.path.is_ident(STORAGE) {
attrs.storage = match nested.value()?.parse::<LitStr>()?.value() {
s if s == TABLE => StorageTy::Table,
s if s == SPARSE_SET => StorageTy::SparseSet,
s => {
return Err(Error::new_spanned(
m.lit,
format!(
"Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'.",
),
))
return Err(nested.error(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)

View file

@ -6,7 +6,8 @@ use syn::{
parse::{Parse, ParseStream},
parse_macro_input, parse_quote,
punctuated::Punctuated,
Attribute, Data, DataStruct, DeriveInput, Field, Index,
token::Comma,
Attribute, Data, DataStruct, DeriveInput, Field, Index, Meta,
};
use crate::bevy_ecs_path;
@ -14,7 +15,7 @@ use crate::bevy_ecs_path;
#[derive(Default)]
struct FetchStructAttributes {
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";
@ -35,7 +36,7 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
let mut fetch_struct_attributes = FetchStructAttributes::default();
for attr in &ast.attrs {
if !attr
.path
.path()
.get_ident()
.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| {
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 {
let ident = meta.path().get_ident().unwrap_or_else(|| {
panic!(
@ -61,9 +62,10 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
}
} else if ident == DERIVE_ATTRIBUTE_NAME {
if let syn::Meta::List(meta_list) = meta {
fetch_struct_attributes
.derive_args
.extend(meta_list.nested.iter().cloned());
meta_list.parse_nested_meta(|meta| {
fetch_struct_attributes.derive_args.push(Meta::Path(meta.path));
Ok(())
})?;
} else {
panic!(
"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();
for attr in &field.attrs {
if attr
.path
.path()
.get_ident()
.map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
{

View file

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

View file

@ -13,4 +13,4 @@ proc-macro = true
[dependencies]
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]
toml_edit = "0.19"
syn = "1.0"
syn = "2.0"
quote = "1.0"
rustc-hash = "1.0"

View file

@ -1,41 +1,32 @@
use syn::DeriveInput;
use syn::{Expr, ExprLit, Lit};
use crate::symbol::Symbol;
pub fn parse_attrs(ast: &DeriveInput, attr_name: Symbol) -> syn::Result<Vec<syn::NestedMeta>> {
let mut list = Vec::new();
for attr in ast.attrs.iter().filter(|a| a.path == attr_name) {
match attr.parse_meta()? {
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 {
pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr> {
if let Expr::Lit(ExprLit {
lit: Lit::Str(lit), ..
}) = &value
{
Ok(lit)
} else {
Err(syn::Error::new_spanned(
lit,
value,
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> {
if let syn::Lit::Bool(lit) = lit {
pub fn get_lit_bool(attr_name: Symbol, value: &Expr) -> syn::Result<bool> {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(lit),
..
}) = &value
{
Ok(lit.value())
} else {
Err(syn::Error::new_spanned(
lit,
value,
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 {
// return true if the variant specified is an `ignore_fields` attribute
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;
}

View file

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

View file

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

View file

@ -31,7 +31,7 @@ thiserror = "1.0"
once_cell = "1.11"
serde = "1"
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]
ron = "0.8.0"

View file

@ -19,7 +19,7 @@ documentation = []
[dependencies]
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"
quote = "1.0"
uuid = { version = "1.1", features = ["v4"] }

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,7 @@ extern crate proc_macro;
use bevy_macro_utils::BevyManifest;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::token::Comma;
use syn::*;
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 mut uuid = None;
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
let Meta::NameValue(name_value) = attribute else {
for attribute in ast.attrs.iter().filter(|attr| attr.path().is_ident("uuid")) {
let Meta::NameValue(ref name_value) = attribute.meta else {
continue;
};
if name_value
.path
.get_ident()
.map(|i| i != "uuid")
.unwrap_or(true)
{
continue;
}
let uuid_str = match name_value.lit {
Lit::Str(lit_str) => lit_str,
let uuid_str = match &name_value.value {
Expr::Lit(ExprLit{lit: Lit::Str(lit_str), ..}) => lit_str,
_ => 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> {
let type_ident = input.parse::<Ident>()?;
let generics = input.parse::<Generics>()?;
input.parse::<Token![,]>()?;
input.parse::<Comma>()?;
let uuid = input.parse::<LitStr>()?.value();
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:?}"));
}
#[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")]
mod glam {
use super::*;

View file

@ -69,7 +69,7 @@ thread_local = "1.1"
thiserror = "1.0"
futures-lite = "1.4.0"
anyhow = "1.0"
hexasphere = "8.1"
hexasphere = "9.0"
parking_lot = "0.12.1"
regex = "1.5"
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 }
# For transcoding of UASTC/ETC1S universal formats, and for .basis file support
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.
profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
async-channel = "1.8"

View file

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

View file

@ -5,7 +5,8 @@ use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
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");
@ -48,7 +49,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
// Read struct-level attributes
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 let Ok(prepared_data_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
for field in fields.iter() {
for attr in &field.attrs {
let Some(attr_ident) = attr.path.get_ident() else {
let Some(attr_ident) = attr.path().get_ident() else {
continue;
};
@ -462,14 +463,14 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
/// like `#[uniform(LitInt, Ident)]`
struct UniformBindingMeta {
lit_int: LitInt,
_comma: Token![,],
_comma: Comma,
ident: Ident,
}
/// Represents the arguments for any general binding 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 {
IndexOnly(LitInt),
IndexWithOptions(BindingIndexOptions),
@ -480,13 +481,13 @@ enum BindingMeta {
/// This represents, for example, `#[texture(0, dimension = "2d_array")]`.
struct BindingIndexOptions {
lit_int: LitInt,
_comma: Token![,],
meta_list: Punctuated<NestedMeta, Token![,]>,
_comma: Comma,
meta_list: Punctuated<Meta, Comma>,
}
impl Parse for BindingMeta {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek2(Token![,]) {
if input.peek2(Comma) {
input.parse().map(Self::IndexWithOptions)
} else {
input.parse().map(Self::IndexOnly)
@ -499,7 +500,7 @@ impl Parse for BindingIndexOptions {
Ok(Self {
lit_int: 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))
}
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)?;
match binding_meta {
@ -598,43 +599,39 @@ const VISIBILITY_COMPUTE: Symbol = Symbol("compute");
const VISIBILITY_ALL: Symbol = Symbol("all");
const VISIBILITY_NONE: Symbol = Symbol("none");
fn get_visibility_flag_value(
nested_metas: &Punctuated<NestedMeta, Token![,]>,
) -> Result<ShaderStageVisibility> {
fn get_visibility_flag_value(meta: Meta) -> Result<ShaderStageVisibility> {
let mut visibility = VisibilityFlags::vertex_fragment();
for meta in nested_metas {
use syn::{Meta::Path, NestedMeta::Meta};
match meta {
// Parse `visibility(all)]`.
Meta(Path(path)) if path == VISIBILITY_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(...)`.",
)),
use syn::Meta::Path;
match meta {
// Parse `#[visibility(all)]`.
Path(path) if path == VISIBILITY_ALL => {
return Ok(ShaderStageVisibility::All)
}
// 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))
@ -727,7 +724,7 @@ const DEPTH: &str = "depth";
const S_INT: &str = "s_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 sample_type = 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();
for meta in metas {
use syn::{
Meta::{List, NameValue},
NestedMeta::Meta,
};
use syn::Meta::{List, NameValue};
match meta {
// Parse #[texture(0, dimension = "...")].
Meta(NameValue(m)) if m.path == DIMENSION => {
let value = get_lit_str(DIMENSION, &m.lit)?;
NameValue(m) if m.path == DIMENSION => {
let value = get_lit_str(DIMENSION, &m.value)?;
dimension = get_texture_dimension_value(value)?;
}
// Parse #[texture(0, sample_type = "...")].
Meta(NameValue(m)) if m.path == SAMPLE_TYPE => {
let value = get_lit_str(SAMPLE_TYPE, &m.lit)?;
NameValue(m) if m.path == SAMPLE_TYPE => {
let value = get_lit_str(SAMPLE_TYPE, &m.value)?;
sample_type = get_texture_sample_type_value(value)?;
}
// Parse #[texture(0, multisampled = "...")].
Meta(NameValue(m)) if m.path == MULTISAMPLED => {
multisampled = get_lit_bool(MULTISAMPLED, &m.lit)?;
NameValue(m) if m.path == MULTISAMPLED => {
multisampled = get_lit_bool(MULTISAMPLED, &m.value)?;
}
// Parse #[texture(0, filterable = "...")].
Meta(NameValue(m)) if m.path == FILTERABLE => {
filterable = get_lit_bool(FILTERABLE, &m.lit)?.into();
NameValue(m) if m.path == FILTERABLE => {
filterable = get_lit_bool(FILTERABLE, &m.value)?.into();
filterable_ident = m.path.into();
}
// Parse #[texture(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?;
List(m) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(Meta::Path(m.path))?;
}
Meta(NameValue(m)) => {
NameValue(m) => {
return Err(Error::new_spanned(
m.path,
"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 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 visibility = ShaderStageVisibility::vertex_fragment();
for meta in metas {
use syn::{
Meta::{List, NameValue},
NestedMeta::Meta,
};
use syn::Meta::{List, NameValue};
match meta {
// Parse #[sampler(0, sampler_type = "..."))].
Meta(NameValue(m)) if m.path == SAMPLER_TYPE => {
let value = get_lit_str(DIMENSION, &m.lit)?;
NameValue(m) if m.path == SAMPLER_TYPE => {
let value = get_lit_str(DIMENSION, &m.value)?;
sampler_binding_type = get_sampler_binding_type_value(value)?;
}
// Parse #[sampler(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?;
List(m) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(Meta::Path(m.path))?;
}
Meta(NameValue(m)) => {
NameValue(m) => {
return Err(Error::new_spanned(
m.path,
"Not a valid name. Available attributes: `sampler_type`.",
@ -928,22 +919,22 @@ struct StorageAttrs {
const READ_ONLY: Symbol = Symbol("read_only");
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 read_only = false;
let mut buffer = false;
for meta in metas {
use syn::{Meta::List, Meta::Path, NestedMeta::Meta};
use syn::{Meta::List, Meta::Path};
match meta {
// Parse #[storage(0, visibility(...))].
Meta(List(m)) if m.path == VISIBILITY => {
visibility = get_visibility_flag_value(&m.nested)?;
List(m) if m.path == VISIBILITY => {
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;
}
Meta(Path(path)) if path == BUFFER => {
Path(path) if path == BUFFER => {
buffer = true;
}
_ => {

View file

@ -20,7 +20,7 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream {
let filter = if let Some(attr) = ast
.attrs
.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>() {
Ok(filter) => filter,

View file

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