Merge pull request #3239 from alex/syn-2

Upgrade to syn 2
This commit is contained in:
David Hewitt 2023-06-14 16:49:25 +00:00 committed by GitHub
commit 527f3c286b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 }
[dependencies.syn]
version = "1.0.85"
version = "2"
default-features = false
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 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)
} else {
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.
///
/// 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 lifetime = lifetimes.next();
ensure_spanned!(

View File

@ -1,7 +1,7 @@
use std::borrow::Cow;
use crate::{
attributes::{self, get_pyo3_options, is_attribute_ident, take_attributes, NameAttribute},
attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
deprecations::Deprecations,
};
use proc_macro2::{Ident, TokenStream};
@ -64,9 +64,9 @@ impl ConstAttributes {
};
take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "classattr") {
if attr.path().is_ident("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;

View File

@ -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::<syn::Ident>() {
python_name = Some(ident);
} else if let Ok(syn::Lit::Str(s)) = ml.parse_args::<syn::Lit>() {
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),
}
}

View File

@ -1,9 +1,7 @@
//! Code generation for the function that initializes a python module and adds classes and function.
use crate::{
attributes::{
self, is_attribute_ident, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute,
},
attributes::{self, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute},
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
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;
take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "pyfn") {
if attr.path().is_ident("pyfn") {
ensure_spanned!(
pyfn_args.is_none(),
attr.span() => "`#[pyfn] may only be specified once"

View File

@ -264,7 +264,7 @@ enum Annotated<X, 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 {
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::<TokenStream, Token![!]>::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<MethodAndSlotDef> {
let spec = FnSpec::parse(

View File

@ -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 })
}

View File

@ -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()
}

View File

@ -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);
}
}

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();
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<String>) ->
// 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<String>) ->
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<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<()> {
if let Some(asyncness) = &sig.asyncness {
bail_spanned!(

View File

@ -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" }