diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index f2d81348770..fa9b7a8080f 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -18,7 +18,7 @@ quote = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false } [dependencies.syn] -version = "1.0.85" +version = "2" default-features = false features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"] diff --git a/pyo3-macros-backend/src/attributes.rs b/pyo3-macros-backend/src/attributes.rs index d26f13b211f..e329a4bc93c 100644 --- a/pyo3-macros-backend/src/attributes.rs +++ b/pyo3-macros-backend/src/attributes.rs @@ -154,8 +154,8 @@ pub fn get_pyo3_options(attr: &syn::Attribute) -> Result bool { - if let Some(path_segment) = attr.path.segments.last() { - attr.path.segments.len() == 1 && path_segment.ident == name + if let Some(path_segment) = attr.path().segments.last() { + attr.path().segments.len() == 1 && path_segment.ident == name } else { false } diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 876a750f914..2b527dc8a29 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -544,7 +544,7 @@ impl FieldPyO3Attributes { } } -fn verify_and_get_lifetime(generics: &syn::Generics) -> Result> { +fn verify_and_get_lifetime(generics: &syn::Generics) -> Result> { let mut lifetimes = generics.lifetimes(); let lifetime = lifetimes.next(); ensure_spanned!( diff --git a/pyo3-macros-backend/src/konst.rs b/pyo3-macros-backend/src/konst.rs index 30cffe89dd2..1a96695b902 100644 --- a/pyo3-macros-backend/src/konst.rs +++ b/pyo3-macros-backend/src/konst.rs @@ -66,7 +66,7 @@ impl ConstAttributes { take_attributes(attrs, |attr| { if is_attribute_ident(attr, "classattr") { ensure_spanned!( - attr.tokens.is_empty(), + matches!(attr.meta, syn::Meta::Path(..)), attr.span() => "`#[classattr]` does not take any arguments" ); attributes.is_class_attr = true; diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 4718434f137..4ca8ec0e6b1 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -651,8 +651,8 @@ fn parse_method_attributes( } for attr in attrs.drain(..) { - match attr.parse_meta() { - Ok(syn::Meta::Path(name)) => { + match attr.meta { + syn::Meta::Path(ref name) => { if name.is_ident("new") || name.is_ident("__new__") { set_compound_ty!(MethodTypeAttribute::New, name); } else if name.is_ident("init") || name.is_ident("__init__") { @@ -680,9 +680,7 @@ fn parse_method_attributes( new_attrs.push(attr) } } - Ok(syn::Meta::List(syn::MetaList { - path, mut nested, .. - })) => { + syn::Meta::List(ref ml @ syn::MetaList { ref path, .. }) => { if path.is_ident("new") { set_ty!(MethodTypeAttribute::New, path); } else if path.is_ident("init") { @@ -699,10 +697,6 @@ fn parse_method_attributes( attr.span() => "inner attribute is not supported for setter and getter" ); } - ensure_spanned!( - nested.len() == 1, - attr.span() => "setter/getter requires one value" - ); if path.is_ident("setter") { set_ty!(MethodTypeAttribute::Setter, path); @@ -715,31 +709,21 @@ fn parse_method_attributes( python_name.span() => "`name` may only be specified once" ); - python_name = match nested.pop().unwrap().into_value() { - syn::NestedMeta::Meta(syn::Meta::Path(w)) if w.segments.len() == 1 => { - Some(w.segments[0].ident.clone()) - } - syn::NestedMeta::Lit(lit) => match lit { - syn::Lit::Str(s) => Some(s.parse()?), - _ => { - return Err(syn::Error::new_spanned( - lit, - "setter/getter attribute requires str value", - )) - } - }, - _ => { - return Err(syn::Error::new_spanned( - nested.first().unwrap(), - "expected ident or string literal for property name", - )) - } - }; + if let Ok(ident) = ml.parse_args::() { + python_name = Some(ident); + } else if let Ok(syn::Lit::Str(s)) = ml.parse_args::() { + python_name = Some(s.parse()?); + } else { + return Err(syn::Error::new_spanned( + ml, + "expected ident or string literal for property name", + )); + } } else { new_attrs.push(attr) } } - Ok(syn::Meta::NameValue(_)) | Err(_) => new_attrs.push(attr), + syn::Meta::NameValue(_) => new_attrs.push(attr), } } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index f723da0951f..d944bff0435 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -264,7 +264,7 @@ enum Annotated { Struct(Y), } -impl Spanned for Annotated { +impl Annotated { fn span(&self) -> Span { match self { Self::Field(x) => x.span(), @@ -410,7 +410,7 @@ impl<'a> PyClassEnum<'a> { // "Under the default representation, the specified discriminant is interpreted as an isize // value", so `isize` should be enough by default. let mut repr_type = syn::Ident::new("isize", proc_macro2::Span::call_site()); - if let Some(attr) = enum_.attrs.iter().find(|attr| attr.path.is_ident("repr")) { + if let Some(attr) = enum_.attrs.iter().find(|attr| attr.path().is_ident("repr")) { let args = attr.parse_args_with(Punctuated::::parse_terminated)?; if let Some(ident) = args @@ -447,7 +447,7 @@ pub fn build_py_enum( } else if let Some(subclass) = &args.options.subclass { bail_spanned!(subclass.span() => "enums can't be inherited by other classes"); } else if enum_.variants.is_empty() { - bail_spanned!(enum_.brace_token.span => "#[pyclass] can't be used on enums without any variants"); + bail_spanned!(enum_.brace_token.span.join() => "#[pyclass] can't be used on enums without any variants"); } let doc = utils::get_doc(&enum_.attrs, None); @@ -518,7 +518,7 @@ fn impl_enum( ); quote! { #cls::#variant_name => #repr, } }); - let mut repr_impl: syn::ImplItemMethod = syn::parse_quote! { + let mut repr_impl: syn::ImplItemFn = syn::parse_quote! { fn __pyo3__repr__(&self) -> &'static str { match self { #(#variants_repr)* @@ -537,7 +537,7 @@ fn impl_enum( let variant_name = variant.ident; quote! { #cls::#variant_name => #cls::#variant_name as #repr_type, } }); - let mut int_impl: syn::ImplItemMethod = syn::parse_quote! { + let mut int_impl: syn::ImplItemFn = syn::parse_quote! { fn __pyo3__int__(&self) -> #repr_type { match self { #(#variants_to_int)* @@ -549,7 +549,7 @@ fn impl_enum( }; let (default_richcmp, default_richcmp_slot) = { - let mut richcmp_impl: syn::ImplItemMethod = syn::parse_quote! { + let mut richcmp_impl: syn::ImplItemFn = syn::parse_quote! { fn __pyo3__richcmp__( &self, py: _pyo3::Python, @@ -623,7 +623,7 @@ fn impl_enum( fn generate_default_protocol_slot( cls: &syn::Type, - method: &mut syn::ImplItemMethod, + method: &mut syn::ImplItemFn, slot: &SlotDef, ) -> syn::Result { let spec = FnSpec::parse( diff --git a/pyo3-macros-backend/src/pyfunction/signature.rs b/pyo3-macros-backend/src/pyfunction/signature.rs index ad705f4ac4e..ed3256ad461 100644 --- a/pyo3-macros-backend/src/pyfunction/signature.rs +++ b/pyo3-macros-backend/src/pyfunction/signature.rs @@ -23,7 +23,7 @@ impl Parse for Signature { let content; let paren_token = syn::parenthesized!(content in input); - let items = content.parse_terminated(SignatureItem::parse)?; + let items = content.parse_terminated(SignatureItem::parse, Token![,])?; Ok(Signature { paren_token, items }) } diff --git a/pyo3-macros-backend/src/pyimpl.rs b/pyo3-macros-backend/src/pyimpl.rs index 983a57658c1..059384e47fd 100644 --- a/pyo3-macros-backend/src/pyimpl.rs +++ b/pyo3-macros-backend/src/pyimpl.rs @@ -99,7 +99,7 @@ pub fn impl_methods( for iimpl in impls.iter_mut() { match iimpl { - syn::ImplItem::Method(meth) => { + syn::ImplItem::Fn(meth) => { let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?; fun_options.krate = fun_options.krate.or_else(|| options.krate.clone()); match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? { @@ -299,6 +299,6 @@ fn submit_methods_inventory( fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> { attrs .iter() - .filter(|attr| attr.path.is_ident("cfg")) + .filter(|attr| attr.path().is_ident("cfg")) .collect() } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 9585c92aa30..f860185eb74 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -541,7 +541,11 @@ pub fn impl_py_setter_def( let mut cfg_attrs = TokenStream::new(); if let PropertyType::Descriptor { field, .. } = &property_type { - for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) { + for attr in field + .attrs + .iter() + .filter(|attr| attr.path().is_ident("cfg")) + { attr.to_tokens(&mut cfg_attrs); } } @@ -667,7 +671,11 @@ pub fn impl_py_getter_def( let mut cfg_attrs = TokenStream::new(); if let PropertyType::Descriptor { field, .. } = &property_type { - for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) { + for attr in field + .attrs + .iter() + .filter(|attr| attr.path().is_ident("cfg")) + { attr.to_tokens(&mut cfg_attrs); } } diff --git a/pyo3-macros-backend/src/utils.rs b/pyo3-macros-backend/src/utils.rs index 5cffe120925..c3c4088c22b 100644 --- a/pyo3-macros-backend/src/utils.rs +++ b/pyo3-macros-backend/src/utils.rs @@ -80,18 +80,18 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option) -> let mut current_part = text_signature.unwrap_or_default(); for attr in attrs.iter() { - if attr.path.is_ident("doc") { - if let Ok(DocArgs { - _eq_token, - token_stream, - }) = syn::parse2(attr.tokens.clone()) - { + if attr.path().is_ident("doc") { + if let Ok(nv) = attr.meta.require_name_value() { if !first { current_part.push('\n'); } else { first = false; } - if let Ok(syn::Lit::Str(lit_str)) = syn::parse2(token_stream.clone()) { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }) = &nv.value + { // Strip single left space from literal strings, if needed. // e.g. `/// Hello world` expands to #[doc = " Hello world"] let doc_line = lit_str.value(); @@ -101,7 +101,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option) -> // Reset the string buffer, write that part, and then push this macro part too. parts.push(current_part.to_token_stream()); current_part.clear(); - parts.push(token_stream); + parts.push(nv.value.to_token_stream()); } } } @@ -116,7 +116,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option) -> let mut tokens = TokenStream::new(); syn::Ident::new("concat", Span::call_site()).to_tokens(&mut tokens); - syn::token::Bang(Span::call_site()).to_tokens(&mut tokens); + syn::token::Not(Span::call_site()).to_tokens(&mut tokens); syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| { parts.to_tokens(tokens); syn::token::Comma(Span::call_site()).to_tokens(tokens); @@ -137,22 +137,6 @@ impl quote::ToTokens for PythonDoc { } } -struct DocArgs { - _eq_token: syn::Token![=], - token_stream: TokenStream, -} - -impl syn::parse::Parse for DocArgs { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { - let this = Self { - _eq_token: input.parse()?, - token_stream: input.parse()?, - }; - ensure_spanned!(input.is_empty(), input.span() => "expected end of doc attribute"); - Ok(this) - } -} - pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> { if let Some(asyncness) = &sig.asyncness { bail_spanned!( diff --git a/pyo3-macros/Cargo.toml b/pyo3-macros/Cargo.toml index 7ecc2b226cb..4d5c0477bf1 100644 --- a/pyo3-macros/Cargo.toml +++ b/pyo3-macros/Cargo.toml @@ -21,5 +21,5 @@ abi3 = ["pyo3-macros-backend/abi3"] [dependencies] proc-macro2 = { version = "1", default-features = false } quote = "1" -syn = { version = "1.0.85", features = ["full", "extra-traits"] } +syn = { version = "2", features = ["full", "extra-traits"] } pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.19.0" }