From 0c8ef77d7c80d18497af01c170c94214fb151df0 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 22 Aug 2023 19:33:09 +0200 Subject: [PATCH] Upgrade syn to 2.0 --- zbus_macros/Cargo.toml | 2 +- zbus_macros/src/iface.rs | 109 +++++++++++++++++++++-------- zbus_macros/src/lib.rs | 10 +-- zbus_macros/src/proxy.rs | 24 +++---- zbus_macros/src/utils.rs | 2 +- zvariant_derive/Cargo.toml | 2 +- zvariant_derive/src/dict.rs | 2 +- zvariant_derive/src/type.rs | 2 +- zvariant_derive/src/value.rs | 6 +- zvariant_utils/Cargo.toml | 2 +- zvariant_utils/src/macros.rs | 130 +++++++++++++++-------------------- 11 files changed, 162 insertions(+), 129 deletions(-) diff --git a/zbus_macros/Cargo.toml b/zbus_macros/Cargo.toml index c4614f368..fb5b4213e 100644 --- a/zbus_macros/Cargo.toml +++ b/zbus_macros/Cargo.toml @@ -21,7 +21,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -syn = { version = "1.0.103", features = ["extra-traits", "fold", "full"] } +syn = { version = "2.0.25", features = ["extra-traits", "fold", "full"] } quote = "1.0.21" proc-macro-crate = "1.2.1" regex = "1.6.0" diff --git a/zbus_macros/src/iface.rs b/zbus_macros/src/iface.rs index d77f5b6fa..f251f784c 100644 --- a/zbus_macros/src/iface.rs +++ b/zbus_macros/src/iface.rs @@ -2,10 +2,14 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::BTreeMap; use syn::{ - self, parse_quote, punctuated::Punctuated, spanned::Spanned, AngleBracketedGenericArguments, - AttributeArgs, Error, FnArg, GenericArgument, ImplItem, ItemImpl, Lit::Str, Meta, - Meta::NameValue, MetaList, MetaNameValue, NestedMeta, PatType, PathArguments, ReturnType, - Signature, Token, Type, TypePath, + self, + parse::{Parse, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + AngleBracketedGenericArguments, Attribute, Error, Expr, ExprLit, FnArg, GenericArgument, + ImplItem, ImplItemFn, ItemImpl, Lit, Meta, MetaNameValue, PatType, PathArguments, ReturnType, + Signature, Token, Type, TypePath, Visibility, }; use zvariant_utils::{case, def_attrs}; @@ -64,7 +68,7 @@ impl<'a> Property<'a> { } } -pub fn expand(args: AttributeArgs, mut input: ItemImpl) -> syn::Result { +pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Result { let zbus = zbus_path(); let self_ty = &input.self_ty; @@ -92,7 +96,7 @@ pub fn expand(args: AttributeArgs, mut input: ItemImpl) -> syn::Result name, @@ -104,9 +108,27 @@ pub fn expand(args: AttributeArgs, mut input: ItemImpl) -> syn::Result m, + for item in &mut input.items { + let (method, is_signal) = match item { + ImplItem::Fn(m) => (m, false), + // Since signals do not have a function body, they don't parse as ImplItemFn… + ImplItem::Verbatim(tokens) => { + // … thus parse them ourselves and construct an ImplItemFn from that + let decl = syn::parse2::(tokens.clone())?; + let ImplItemSignal { attrs, vis, sig } = decl; + *item = ImplItem::Fn(ImplItemFn { + attrs, + vis, + defaultness: None, + sig, + // This empty block will be replaced below. + block: parse_quote!({}), + }); + match item { + ImplItem::Fn(m) => (m, true), + _ => unreachable!(), + } + } _ => continue, }; @@ -122,24 +144,36 @@ pub fn expand(args: AttributeArgs, mut input: ItemImpl) -> syn::Result Some(s.value()), // non #[doc = "..."] attributes are not our concern // we leave them for rustc to handle - None + _ => None, } }) .collect(); let doc_comments = to_xml_docs(docs); let is_property = attrs.property; - let is_signal = attrs.signal; let out_args = attrs.out_args.as_deref(); assert!(!is_property || !is_signal); @@ -672,7 +706,7 @@ fn get_args_from_inputs( fn clean_input_args(inputs: &mut Punctuated) { for input in inputs { if let FnArg::Typed(t) = input { - t.attrs.retain(|attr| !attr.path.is_ident("zbus")); + t.attrs.retain(|attr| !attr.path().is_ident("zbus")); } } } @@ -707,25 +741,24 @@ fn introspect_input_args( .iter() .filter_map(move |pat_type @ PatType { ty, attrs, .. }| { let is_special_arg = attrs.iter().any(|attr| { - if !attr.path.is_ident("zbus") { + if !attr.path().is_ident("zbus") { return false; } - let meta = match attr.parse_meta() { - ::std::result::Result::Ok(meta) => meta, - ::std::result::Result::Err(_) => return false, - }; - - let nested = match meta { - Meta::List(MetaList { nested, .. }) => nested, - _ => return false, - }; + let meta = + match attr.parse_args_with(Punctuated::::parse_terminated) { + Ok(meta) => meta, + Err(_) => return false, + }; - let res = nested.iter().any(|nested_meta| { + let res = meta.iter().any(|nested_meta| { matches!( nested_meta, - NestedMeta::Meta(Meta::Path(path)) - if path.is_ident("object_server") || path.is_ident("connection") || path.is_ident("header") || path.is_ident("signal_context") + Meta::Path(path) + if path.is_ident("object_server") || + path.is_ident("connection") || + path.is_ident("header") || + path.is_ident("signal_context") ) }); @@ -904,3 +937,21 @@ pub fn to_xml_docs(lines: Vec) -> TokenStream { docs } + +// Like ImplItemFn, but with a semicolon at the end instead of a body block +struct ImplItemSignal { + attrs: Vec, + vis: Visibility, + sig: Signature, +} + +impl Parse for ImplItemSignal { + fn parse(input: ParseStream<'_>) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let vis = input.parse()?; + let sig = input.parse()?; + let _: Token![;] = input.parse()?; + + Ok(ImplItemSignal { attrs, vis, sig }) + } +} diff --git a/zbus_macros/src/lib.rs b/zbus_macros/src/lib.rs index 64723b2ed..059f086f9 100644 --- a/zbus_macros/src/lib.rs +++ b/zbus_macros/src/lib.rs @@ -11,7 +11,7 @@ )))] use proc_macro::TokenStream; -use syn::{parse_macro_input, AttributeArgs, DeriveInput, ItemImpl, ItemTrait}; +use syn::{parse_macro_input, punctuated::Punctuated, DeriveInput}; mod error; mod iface; @@ -192,8 +192,8 @@ mod utils; /// [dbus_emits_changed_signal]: https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format #[proc_macro_attribute] pub fn dbus_proxy(attr: TokenStream, item: TokenStream) -> TokenStream { - let args = parse_macro_input!(attr as AttributeArgs); - let input = parse_macro_input!(item as ItemTrait); + let args = parse_macro_input!(attr with Punctuated::parse_terminated); + let input = parse_macro_input!(item); proxy::expand(args, input) .unwrap_or_else(|err| err.to_compile_error()) .into() @@ -323,8 +323,8 @@ pub fn dbus_proxy(attr: TokenStream, item: TokenStream) -> TokenStream { /// [`Interface`]: https://docs.rs/zbus/latest/zbus/object_server/trait.Interface.html #[proc_macro_attribute] pub fn dbus_interface(attr: TokenStream, item: TokenStream) -> TokenStream { - let args = parse_macro_input!(attr as AttributeArgs); - let input = syn::parse_macro_input!(item as ItemImpl); + let args = parse_macro_input!(attr with Punctuated::parse_terminated); + let input = syn::parse_macro_input!(item); iface::expand(args, input) .unwrap_or_else(|err| err.to_compile_error()) .into() diff --git a/zbus_macros/src/proxy.rs b/zbus_macros/src/proxy.rs index 35acdb90b..7928320cf 100644 --- a/zbus_macros/src/proxy.rs +++ b/zbus_macros/src/proxy.rs @@ -3,8 +3,8 @@ use proc_macro2::{Literal, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use regex::Regex; use syn::{ - self, fold::Fold, parse_quote, parse_str, spanned::Spanned, AttributeArgs, Error, FnArg, Ident, - ItemTrait, Path, ReturnType, TraitItemMethod, + self, fold::Fold, parse_quote, parse_str, punctuated::Punctuated, spanned::Spanned, Error, + FnArg, Ident, ItemTrait, Meta, Path, ReturnType, Token, TraitItemFn, }; use zvariant_utils::{case, def_attrs}; @@ -62,7 +62,7 @@ impl AsyncOpts { } } -pub fn expand(args: AttributeArgs, input: ItemTrait) -> Result { +pub fn expand(args: Punctuated, input: ItemTrait) -> Result { let ImplAttributes { interface, name, @@ -73,7 +73,7 @@ pub fn expand(args: AttributeArgs, input: ItemTrait) -> Result Ok(Some(name)), @@ -163,7 +163,7 @@ pub fn create_proxy( let other_attrs: Vec<_> = input .attrs .iter() - .filter(|a| !a.path.is_ident("dbus_proxy")) + .filter(|a| !a.path().is_ident("dbus_proxy")) .collect(); let proxy_name = Ident::new(proxy_name, Span::call_site()); let ident = input.ident.to_string(); @@ -192,7 +192,7 @@ pub fn create_proxy( let async_opts = AsyncOpts::new(blocking); for i in input.items.iter() { - if let syn::TraitItem::Method(m) = i { + if let syn::TraitItem::Fn(m) = i { let mut attrs = MethodAttributes::parse(&m.attrs)?; let method_name = m.sig.ident.to_string(); @@ -448,7 +448,7 @@ pub fn create_proxy( fn gen_proxy_method_call( method_name: &str, snake_case_name: &str, - m: &TraitItemMethod, + m: &TraitItemFn, attrs: &MethodAttributes, async_opts: &AsyncOpts, ) -> Result { @@ -461,7 +461,7 @@ fn gen_proxy_method_call( let other_attrs: Vec<_> = m .attrs .iter() - .filter(|a| !a.path.is_ident("dbus_proxy")) + .filter(|a| !a.path().is_ident("dbus_proxy")) .collect(); let args: Vec<_> = m .sig @@ -666,7 +666,7 @@ impl PropertyEmitsChangedSignal { fn gen_proxy_property( property_name: &str, method_name: &str, - m: &TraitItemMethod, + m: &TraitItemFn, async_opts: &AsyncOpts, emits_changed_signal: PropertyEmitsChangedSignal, ) -> TokenStream { @@ -679,7 +679,7 @@ fn gen_proxy_property( let other_attrs: Vec<_> = m .attrs .iter() - .filter(|a| !a.path.is_ident("dbus_proxy")) + .filter(|a| !a.path().is_ident("dbus_proxy")) .collect(); let signature = &m.sig; if signature.inputs.len() > 1 { @@ -795,7 +795,7 @@ fn gen_proxy_signal( iface_name: &str, signal_name: &str, snake_case_name: &str, - method: &TraitItemMethod, + method: &TraitItemFn, async_opts: &AsyncOpts, gen_sig_args: bool, ) -> (TokenStream, TokenStream) { @@ -808,7 +808,7 @@ fn gen_proxy_signal( let other_attrs: Vec<_> = method .attrs .iter() - .filter(|a| !a.path.is_ident("dbus_proxy")) + .filter(|a| !a.path().is_ident("dbus_proxy")) .collect(); let input_types: Vec<_> = method .sig diff --git a/zbus_macros/src/utils.rs b/zbus_macros/src/utils.rs index 6fcaf4277..7d240dc0e 100644 --- a/zbus_macros/src/utils.rs +++ b/zbus_macros/src/utils.rs @@ -27,7 +27,7 @@ pub fn pat_ident(pat: &PatType) -> Option<&Ident> { } pub fn get_doc_attrs(attrs: &[Attribute]) -> Vec<&Attribute> { - attrs.iter().filter(|x| x.path.is_ident("doc")).collect() + attrs.iter().filter(|x| x.path().is_ident("doc")).collect() } // Convert to pascal case, assuming snake case. diff --git a/zvariant_derive/Cargo.toml b/zvariant_derive/Cargo.toml index 878e438c5..518adbb08 100644 --- a/zvariant_derive/Cargo.toml +++ b/zvariant_derive/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -syn = { version = "1.0.103", features = ["extra-traits", "full"] } +syn = { version = "2.0.25", features = ["extra-traits", "full"] } quote = "1.0.21" proc-macro-crate = "1.2.1" zvariant_utils = { path = "../zvariant_utils", version = "=1.0.1" } diff --git a/zvariant_derive/src/dict.rs b/zvariant_derive/src/dict.rs index 85b003d46..a0671e1c4 100644 --- a/zvariant_derive/src/dict.rs +++ b/zvariant_derive/src/dict.rs @@ -154,7 +154,7 @@ pub fn expand_deserialize_derive(input: DeriveInput) -> Result Result { - let repr = attrs.iter().find(|attr| attr.path.is_ident("repr")); + let repr = attrs.iter().find(|attr| attr.path().is_ident("repr")); match &variant.fields { Fields::Unit => { let repr = match repr { diff --git a/zvariant_derive/src/value.rs b/zvariant_derive/src/value.rs index b51398738..d5861ea7c 100644 --- a/zvariant_derive/src/value.rs +++ b/zvariant_derive/src/value.rs @@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ self, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Expr, Fields, Generics, - Ident, Lifetime, LifetimeDef, + Ident, Lifetime, LifetimeParam, }; use crate::utils::*; @@ -51,7 +51,7 @@ fn impl_struct( signature: Option, zv: &TokenStream, ) -> Result { - let statc_lifetime = LifetimeDef::new(Lifetime::new("'static", Span::call_site())); + let statc_lifetime = LifetimeParam::new(Lifetime::new("'static", Span::call_site())); let (value_type, value_lifetime) = match value_type { ValueType::Value => { let mut lifetimes = generics.lifetimes(); @@ -207,7 +207,7 @@ fn impl_enum( data: &DataEnum, zv: &TokenStream, ) -> Result { - let repr: TokenStream = match attrs.iter().find(|attr| attr.path.is_ident("repr")) { + let repr: TokenStream = match attrs.iter().find(|attr| attr.path().is_ident("repr")) { Some(repr_attr) => repr_attr.parse_args()?, None => quote! { u32 }, }; diff --git a/zvariant_utils/Cargo.toml b/zvariant_utils/Cargo.toml index cb89746fd..30804a412 100644 --- a/zvariant_utils/Cargo.toml +++ b/zvariant_utils/Cargo.toml @@ -17,5 +17,5 @@ readme = "README.md" [dependencies] proc-macro2 = "1.0" -syn = { version = "1.0.103", features = ["extra-traits", "full"] } +syn = { version = "2.0.25", features = ["extra-traits", "full"] } quote = "1.0.21" diff --git a/zvariant_utils/src/macros.rs b/zvariant_utils/src/macros.rs index fd3f393e8..cea491026 100644 --- a/zvariant_utils/src/macros.rs +++ b/zvariant_utils/src/macros.rs @@ -1,26 +1,11 @@ use syn::{ - spanned::Spanned, Attribute, Lit, LitBool, LitStr, Meta, MetaList, NestedMeta, Result, Type, - TypePath, + punctuated::Punctuated, spanned::Spanned, Attribute, Expr, ExprLit, Lit, LitBool, LitStr, Meta, + Result, Token, Type, TypePath, }; -// find the #[@attr_name] attribute in @attrs -fn find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result> { - let meta = match attrs.iter().find(|a| a.path.is_ident(attr_name)) { - Some(a) => a.parse_meta(), - _ => return Ok(None), - }?; +fn get_meta_value<'a>(meta: &'a Meta, attr: &str) -> Result<&'a Expr> { match meta { - Meta::List(n) => Ok(Some(n)), - _ => Err(syn::Error::new( - meta.span(), - format!("{attr_name} meta must specify a meta list"), - )), - } -} - -fn get_meta_value<'a>(meta: &'a Meta, attr: &str) -> Result<&'a Lit> { - match meta { - Meta::NameValue(meta) => Ok(&meta.lit), + Meta::NameValue(meta) => Ok(ungroup(&meta.value)), Meta::Path(_) => Err(syn::Error::new( meta.span(), format!("attribute `{attr}` must have a value"), @@ -32,6 +17,14 @@ fn get_meta_value<'a>(meta: &'a Meta, attr: &str) -> Result<&'a Lit> { } } +fn ungroup(mut expr: &Expr) -> &Expr { + while let Expr::Group(g) = expr { + expr = &g.expr; + } + + expr +} + /// Compares `ident` and `attr` and in case they match ensures `value` is `Some` and contains a /// [`struct@LitStr`]. Returns `true` in case `ident` and `attr` match, otherwise false. /// @@ -45,7 +38,10 @@ pub fn match_attribute_with_str_value<'a>( ) -> Result> { if meta.path().is_ident(attr) { match get_meta_value(meta, attr)? { - Lit::Str(value) => Ok(Some(value)), + Expr::Lit(ExprLit { + lit: Lit::Str(value), + .. + }) => Ok(Some(value)), _ => Err(syn::Error::new( meta.span(), format!("value of the `{attr}` attribute must be a string literal"), @@ -69,10 +65,13 @@ pub fn match_attribute_with_bool_value<'a>( ) -> Result> { if meta.path().is_ident(attr) { match get_meta_value(meta, attr)? { - Lit::Bool(value) => Ok(Some(value)), + Expr::Lit(ExprLit { + lit: Lit::Bool(value), + .. + }) => Ok(Some(value)), other => Err(syn::Error::new( other.span(), - format!("value of the `{attr}` attribute must be a boolean literal"), + format!("value of the `{attr}` attribute must be a boolean literal, got {other:?}"), )), } } else { @@ -84,23 +83,11 @@ pub fn match_attribute_with_str_list_value(meta: &Meta, attr: &str) -> Result { - let mut values = Vec::with_capacity(list.nested.len()); - - for meta in &list.nested { - values.push(match meta { - NestedMeta::Lit(Lit::Str(lit)) => Ok(lit.value()), - NestedMeta::Lit(lit) => Err(syn::Error::new( - lit.span(), - format!("invalid literal type for `{attr}` attribute"), - )), - NestedMeta::Meta(meta) => Err(syn::Error::new( - meta.span(), - format!("`{attr}` attribute must be a list of string literals"), - )), - }?) - } - - Ok(Some(values)) + let punctuated = + list.parse_args_with(Punctuated::::parse_terminated)?; + Ok(Some( + punctuated.into_iter().map(|lit| lit.value()).collect(), + )) } _ => Err(syn::Error::new( meta.span(), @@ -138,13 +125,25 @@ pub fn match_attribute_without_value(meta: &Meta, attr: &str) -> Result { /// Returns an iterator over the contents of all [`MetaList`]s with the specified identifier in an /// array of [`Attribute`]s. -pub fn iter_meta_lists( +pub fn iter_nested_meta( attrs: &[Attribute], - list_name: &str, -) -> Result> { - let meta = find_attribute_meta(attrs, list_name)?; - - Ok(meta.into_iter().flat_map(|meta| meta.nested.into_iter())) + attr_name: &str, +) -> Result> { + Ok(attrs + .iter() + .filter(|a| a.path().is_ident(attr_name)) + .map(|attr| match &attr.meta { + Meta::List(list) => { + list.parse_args_with(Punctuated::::parse_terminated) + } + _ => Err(syn::Error::new( + attr.meta.span(), + format!("{attr_name} meta must specify a meta list"), + )), + }) + .collect::>>()? + .into_iter() + .flatten()) } /// Generates one or more structures used for parsing attributes in proc macros. @@ -333,7 +332,12 @@ macro_rules! def_attrs { match $meta { ::syn::Meta::List(meta) => { $self.$attr_name = ::std::option::Option::Some($name::parse_nested_metas( - meta.nested.iter() + meta.parse_args_with( + ::syn::punctuated::Punctuated::< + ::syn::Meta, + ::syn::Token![,], + >::parse_terminated + )? )?); ::std::result::Result::Ok(()) } @@ -420,24 +424,13 @@ macro_rules! def_attrs { })) } - pub fn parse_nested_metas<'a, I>(iter: I) -> syn::Result + pub fn parse_nested_metas(iter: I) -> syn::Result where - I: ::std::iter::IntoIterator + I: ::std::iter::IntoIterator { let mut parsed = $name::default(); - for nested_meta in iter { - match nested_meta { - ::syn::NestedMeta::Meta(meta) => parsed.parse_meta(meta), - ::syn::NestedMeta::Lit(lit) => { - ::std::result::Result::Err(::syn::Error::new( - lit.span(), - ::std::concat!( - "attribute `", ::std::stringify!($list_name), - "` does not support literals in meta lists" - ) - )) - } - }?; + for meta in iter { + parsed.parse_meta(&meta)?; } Ok(parsed) @@ -445,19 +438,8 @@ macro_rules! def_attrs { pub fn parse(attrs: &[::syn::Attribute]) -> ::syn::Result { let mut parsed = $name::default(); - for nested_meta in $crate::macros::iter_meta_lists(attrs, ::std::stringify!($list_name))? { - match &nested_meta { - ::syn::NestedMeta::Meta(meta) => parsed.parse_meta(meta), - ::syn::NestedMeta::Lit(lit) => { - ::std::result::Result::Err(::syn::Error::new( - lit.span(), - ::std::concat!( - "attribute `", ::std::stringify!($list_name), - "` does not support literals in meta lists" - ) - )) - } - }?; + for meta in $crate::macros::iter_nested_meta(attrs, ::std::stringify!($list_name))? { + parsed.parse_meta(&meta)?; } Ok(parsed)