Upgrade to syn 2

This commit is contained in:
Alex Gaynor 2023-06-13 19:34:45 -04:00
parent 0b4187a672
commit afbb1d435c
Failed to extract signature
13 changed files with 53 additions and 86 deletions

View File

@ -0,0 +1 @@
Switched from syn 1.x to syn 2.x

View File

@ -18,7 +18,7 @@ quote = { version = "1", default-features = false }
proc-macro2 = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false }
[dependencies.syn] [dependencies.syn]
version = "1.0.85" version = "2"
default-features = false default-features = false
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"] features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]

View File

@ -146,21 +146,13 @@ pub type FromPyWithAttribute = KeywordAttribute<kw::from_py_with, LitStrValue<Ex
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>; pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> { pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
if is_attribute_ident(attr, "pyo3") { if attr.path().is_ident("pyo3") {
attr.parse_args_with(Punctuated::parse_terminated).map(Some) attr.parse_args_with(Punctuated::parse_terminated).map(Some)
} else { } else {
Ok(None) Ok(None)
} }
} }
pub fn is_attribute_ident(attr: &syn::Attribute, name: &str) -> bool {
if let Some(path_segment) = attr.path.segments.last() {
attr.path.segments.len() == 1 && path_segment.ident == name
} else {
false
}
}
/// Takes attributes from an attribute vector. /// Takes attributes from an attribute vector.
/// ///
/// For each attribute in `attrs`, `extractor` is called. If `extractor` returns `Ok(true)`, then /// For each attribute in `attrs`, `extractor` is called. If `extractor` returns `Ok(true)`, then

View File

@ -544,7 +544,7 @@ impl FieldPyO3Attributes {
} }
} }
fn verify_and_get_lifetime(generics: &syn::Generics) -> Result<Option<&syn::LifetimeDef>> { fn verify_and_get_lifetime(generics: &syn::Generics) -> Result<Option<&syn::LifetimeParam>> {
let mut lifetimes = generics.lifetimes(); let mut lifetimes = generics.lifetimes();
let lifetime = lifetimes.next(); let lifetime = lifetimes.next();
ensure_spanned!( ensure_spanned!(

View File

@ -1,7 +1,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::{ use crate::{
attributes::{self, get_pyo3_options, is_attribute_ident, take_attributes, NameAttribute}, attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
deprecations::Deprecations, deprecations::Deprecations,
}; };
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
@ -64,9 +64,9 @@ impl ConstAttributes {
}; };
take_attributes(attrs, |attr| { take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "classattr") { if attr.path().is_ident("classattr") {
ensure_spanned!( ensure_spanned!(
attr.tokens.is_empty(), matches!(attr.meta, syn::Meta::Path(..)),
attr.span() => "`#[classattr]` does not take any arguments" attr.span() => "`#[classattr]` does not take any arguments"
); );
attributes.is_class_attr = true; attributes.is_class_attr = true;

View File

@ -651,8 +651,8 @@ fn parse_method_attributes(
} }
for attr in attrs.drain(..) { for attr in attrs.drain(..) {
match attr.parse_meta() { match attr.meta {
Ok(syn::Meta::Path(name)) => { syn::Meta::Path(ref name) => {
if name.is_ident("new") || name.is_ident("__new__") { if name.is_ident("new") || name.is_ident("__new__") {
set_compound_ty!(MethodTypeAttribute::New, name); set_compound_ty!(MethodTypeAttribute::New, name);
} else if name.is_ident("init") || name.is_ident("__init__") { } else if name.is_ident("init") || name.is_ident("__init__") {
@ -680,9 +680,7 @@ fn parse_method_attributes(
new_attrs.push(attr) new_attrs.push(attr)
} }
} }
Ok(syn::Meta::List(syn::MetaList { syn::Meta::List(ref ml @ syn::MetaList { ref path, .. }) => {
path, mut nested, ..
})) => {
if path.is_ident("new") { if path.is_ident("new") {
set_ty!(MethodTypeAttribute::New, path); set_ty!(MethodTypeAttribute::New, path);
} else if path.is_ident("init") { } 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" 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") { if path.is_ident("setter") {
set_ty!(MethodTypeAttribute::Setter, path); set_ty!(MethodTypeAttribute::Setter, path);
@ -715,31 +709,21 @@ fn parse_method_attributes(
python_name.span() => "`name` may only be specified once" python_name.span() => "`name` may only be specified once"
); );
python_name = match nested.pop().unwrap().into_value() { if let Ok(ident) = ml.parse_args::<syn::Ident>() {
syn::NestedMeta::Meta(syn::Meta::Path(w)) if w.segments.len() == 1 => { python_name = Some(ident);
Some(w.segments[0].ident.clone()) } else if let Ok(syn::Lit::Str(s)) = ml.parse_args::<syn::Lit>() {
} python_name = Some(s.parse()?);
syn::NestedMeta::Lit(lit) => match lit { } else {
syn::Lit::Str(s) => Some(s.parse()?), return Err(syn::Error::new_spanned(
_ => { ml,
return Err(syn::Error::new_spanned( "expected ident or string literal for property name",
lit, ));
"setter/getter attribute requires str value", }
))
}
},
_ => {
return Err(syn::Error::new_spanned(
nested.first().unwrap(),
"expected ident or string literal for property name",
))
}
};
} else { } else {
new_attrs.push(attr) new_attrs.push(attr)
} }
} }
Ok(syn::Meta::NameValue(_)) | Err(_) => new_attrs.push(attr), syn::Meta::NameValue(_) => new_attrs.push(attr),
} }
} }

View File

@ -1,9 +1,7 @@
//! Code generation for the function that initializes a python module and adds classes and function. //! Code generation for the function that initializes a python module and adds classes and function.
use crate::{ use crate::{
attributes::{ attributes::{self, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute},
self, is_attribute_ident, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute,
},
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions}, pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
utils::{get_pyo3_crate, PythonDoc}, utils::{get_pyo3_crate, PythonDoc},
}; };
@ -165,7 +163,7 @@ fn get_pyfn_attr(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<PyFnArgs
let mut pyfn_args: Option<PyFnArgs> = None; let mut pyfn_args: Option<PyFnArgs> = None;
take_attributes(attrs, |attr| { take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "pyfn") { if attr.path().is_ident("pyfn") {
ensure_spanned!( ensure_spanned!(
pyfn_args.is_none(), pyfn_args.is_none(),
attr.span() => "`#[pyfn] may only be specified once" attr.span() => "`#[pyfn] may only be specified once"

View File

@ -264,7 +264,7 @@ enum Annotated<X, Y> {
Struct(Y), Struct(Y),
} }
impl<X: Spanned, Y: Spanned> Spanned for Annotated<X, Y> { impl<X: Spanned, Y: Spanned> Annotated<X, Y> {
fn span(&self) -> Span { fn span(&self) -> Span {
match self { match self {
Self::Field(x) => x.span(), 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 // "Under the default representation, the specified discriminant is interpreted as an isize
// value", so `isize` should be enough by default. // value", so `isize` should be enough by default.
let mut repr_type = syn::Ident::new("isize", proc_macro2::Span::call_site()); 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 = let args =
attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?; attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?;
if let Some(ident) = args if let Some(ident) = args
@ -447,7 +447,7 @@ pub fn build_py_enum(
} else if let Some(subclass) = &args.options.subclass { } else if let Some(subclass) = &args.options.subclass {
bail_spanned!(subclass.span() => "enums can't be inherited by other classes"); bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
} else if enum_.variants.is_empty() { } 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); let doc = utils::get_doc(&enum_.attrs, None);
@ -518,7 +518,7 @@ fn impl_enum(
); );
quote! { #cls::#variant_name => #repr, } 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 { fn __pyo3__repr__(&self) -> &'static str {
match self { match self {
#(#variants_repr)* #(#variants_repr)*
@ -537,7 +537,7 @@ fn impl_enum(
let variant_name = variant.ident; let variant_name = variant.ident;
quote! { #cls::#variant_name => #cls::#variant_name as #repr_type, } 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 { fn __pyo3__int__(&self) -> #repr_type {
match self { match self {
#(#variants_to_int)* #(#variants_to_int)*
@ -549,7 +549,7 @@ fn impl_enum(
}; };
let (default_richcmp, default_richcmp_slot) = { 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__( fn __pyo3__richcmp__(
&self, &self,
py: _pyo3::Python, py: _pyo3::Python,
@ -623,7 +623,7 @@ fn impl_enum(
fn generate_default_protocol_slot( fn generate_default_protocol_slot(
cls: &syn::Type, cls: &syn::Type,
method: &mut syn::ImplItemMethod, method: &mut syn::ImplItemFn,
slot: &SlotDef, slot: &SlotDef,
) -> syn::Result<MethodAndSlotDef> { ) -> syn::Result<MethodAndSlotDef> {
let spec = FnSpec::parse( let spec = FnSpec::parse(

View File

@ -23,7 +23,7 @@ impl Parse for Signature {
let content; let content;
let paren_token = syn::parenthesized!(content in input); 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 }) Ok(Signature { paren_token, items })
} }

View File

@ -99,7 +99,7 @@ pub fn impl_methods(
for iimpl in impls.iter_mut() { for iimpl in impls.iter_mut() {
match iimpl { match iimpl {
syn::ImplItem::Method(meth) => { syn::ImplItem::Fn(meth) => {
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?; let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone()); 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)? { 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> { fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> {
attrs attrs
.iter() .iter()
.filter(|attr| attr.path.is_ident("cfg")) .filter(|attr| attr.path().is_ident("cfg"))
.collect() .collect()
} }

View File

@ -541,7 +541,11 @@ pub fn impl_py_setter_def(
let mut cfg_attrs = TokenStream::new(); let mut cfg_attrs = TokenStream::new();
if let PropertyType::Descriptor { field, .. } = &property_type { 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); attr.to_tokens(&mut cfg_attrs);
} }
} }
@ -667,7 +671,11 @@ pub fn impl_py_getter_def(
let mut cfg_attrs = TokenStream::new(); let mut cfg_attrs = TokenStream::new();
if let PropertyType::Descriptor { field, .. } = &property_type { 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); attr.to_tokens(&mut cfg_attrs);
} }
} }

View File

@ -80,18 +80,18 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
let mut current_part = text_signature.unwrap_or_default(); let mut current_part = text_signature.unwrap_or_default();
for attr in attrs.iter() { for attr in attrs.iter() {
if attr.path.is_ident("doc") { if attr.path().is_ident("doc") {
if let Ok(DocArgs { if let Ok(nv) = attr.meta.require_name_value() {
_eq_token,
token_stream,
}) = syn::parse2(attr.tokens.clone())
{
if !first { if !first {
current_part.push('\n'); current_part.push('\n');
} else { } else {
first = false; 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. // Strip single left space from literal strings, if needed.
// e.g. `/// Hello world` expands to #[doc = " Hello world"] // e.g. `/// Hello world` expands to #[doc = " Hello world"]
let doc_line = lit_str.value(); let doc_line = lit_str.value();
@ -101,7 +101,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
// Reset the string buffer, write that part, and then push this macro part too. // Reset the string buffer, write that part, and then push this macro part too.
parts.push(current_part.to_token_stream()); parts.push(current_part.to_token_stream());
current_part.clear(); 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<String>) ->
let mut tokens = TokenStream::new(); let mut tokens = TokenStream::new();
syn::Ident::new("concat", Span::call_site()).to_tokens(&mut tokens); 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| { syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
parts.to_tokens(tokens); parts.to_tokens(tokens);
syn::token::Comma(Span::call_site()).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<Self> {
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<()> { pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> {
if let Some(asyncness) = &sig.asyncness { if let Some(asyncness) = &sig.asyncness {
bail_spanned!( bail_spanned!(

View File

@ -21,5 +21,5 @@ abi3 = ["pyo3-macros-backend/abi3"]
[dependencies] [dependencies]
proc-macro2 = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false }
quote = "1" 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" } pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.19.0" }