diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 133f63413a..6ed4b1754b 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -107,8 +107,8 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result Result ctx.new_int(Self::#ident).into()) + let value = if args.item.is_const() { + // TODO: ctx.new_value + quote_spanned!(ident.span() => ctx.new_int(Self::#ident).into()) + } else { + quote_spanned!(ident.span() => Self::#ident(ctx)) + }; + ( + ident, + py_name.clone(), + quote! { + class.set_str_attr(#py_name, #value); + }, + ) } else { - quote_spanned!(ident.span() => Self::#ident(ctx)) + return Err(self.new_syn_error( + args.item.span(), + "can only be on a const or an associated method without argument", + )); }; - ( - py_name.clone(), - quote! { - class.set_str_attr(#py_name, #value); - }, - ) - } else { - return Err(self.new_syn_error( - args.item.span(), - "can only be on a const or an associated method without argument", - )); - }; args.context .impl_extend_items - .add_item(py_name, cfgs, tokens)?; + .add_item(ident.clone(), vec![py_name], cfgs, tokens)?; Ok(()) } diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index 78b6bd9215..0ae51d632c 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -87,7 +87,7 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result { - let func = #new_func; - vm.__module_set_attr(module, #py_name, func).unwrap(); - }} + ( + quote_spanned! { ident.span() => { + let func = #new_func; + vm.__module_set_attr(module, #py_name, func).unwrap(); + }}, + vec![py_name], + ) } else { let mut py_names = HashSet::new(); - py_names.insert(py_name.clone()); + py_names.insert(py_name); for attr_index in self.pyattrs.iter().rev() { let mut loop_unit = || { let attr_attr = args.attrs.remove(*attr_index); @@ -368,18 +371,24 @@ impl ModuleItem for FunctionItem { args.context.errors.ok_or_push(r); } let py_names: Vec<_> = py_names.into_iter().collect(); - quote_spanned! { ident.span().resolved_at(Span::call_site()) => { - let func = #new_func; - for name in [#(#py_names,)*] { - vm.__module_set_attr(module, name, func.clone()).unwrap(); - } - }} + ( + quote_spanned! { ident.span().resolved_at(Span::call_site()) => { + let func = #new_func; + for name in [#(#py_names,)*] { + vm.__module_set_attr(module, name, func.clone()).unwrap(); + } + }}, + py_names, + ) } }; - args.context - .module_extend_items - .add_item(py_name, args.cfgs.to_vec(), item)?; + args.context.module_extend_items.add_item( + ident.clone(), + py_names, + args.cfgs.to_vec(), + tokens, + )?; Ok(()) } } @@ -422,7 +431,7 @@ impl ModuleItem for ClassItem { (class_name, class_new) }; - let mut names = Vec::new(); + let mut py_names = Vec::new(); for attr_index in self.pyattrs.iter().rev() { let mut loop_unit = || { let attr_attr = args.attrs.remove(*attr_index); @@ -431,7 +440,7 @@ impl ModuleItem for ClassItem { let py_name = item_meta .optional_name() .unwrap_or_else(|| class_name.clone()); - names.push(py_name); + py_names.push(py_name); Ok(()) }; @@ -439,25 +448,26 @@ impl ModuleItem for ClassItem { args.context.errors.ok_or_push(r); } - let set_attr = match names.len() { + let set_attr = match py_names.len() { 0 => quote! { let _ = new_class; }, 1 => { - let py_name = &names[0]; + let py_name = &py_names[0]; quote! { vm.__module_set_attr(&module, #py_name, new_class).unwrap(); } } _ => quote! { - for name in [#(#names,)*] { + for name in [#(#py_names,)*] { vm.__module_set_attr(&module, name, new_class.clone()).unwrap(); } }, }; args.context.module_extend_items.add_item( - ident.to_string(), + ident.clone(), + py_names, args.cfgs.to_vec(), quote_spanned! { ident.span() => { #class_new @@ -542,9 +552,12 @@ impl ModuleItem for AttributeItem { let tokens = quote_spanned! { ident.span() => vm.__module_set_attr(module, #py_name, vm.new_pyobj(#ident)).unwrap(); }; - args.context - .module_extend_items - .add_item(py_name, cfgs.clone(), tokens)?; + args.context.module_extend_items.add_item( + ident.clone(), + vec![py_name], + cfgs.clone(), + tokens, + )?; Ok(()) }); } @@ -555,13 +568,16 @@ impl ModuleItem for AttributeItem { } }; - let tokens = if self.pyattrs.is_empty() { - quote_spanned! { ident.span() => { - #let_obj - vm.__module_set_attr(module, #py_name, obj).unwrap(); - }} + let (tokens, py_names) = if self.pyattrs.is_empty() { + ( + quote_spanned! { ident.span() => { + #let_obj + vm.__module_set_attr(module, #py_name, obj).unwrap(); + }}, + vec![py_name], + ) } else { - let mut names = vec![py_name.clone()]; + let mut names = vec![py_name]; for attr_index in self.pyattrs.iter().rev() { let mut loop_unit = || { let attr_attr = args.attrs.remove(*attr_index); @@ -585,17 +601,20 @@ impl ModuleItem for AttributeItem { let r = loop_unit(); args.context.errors.ok_or_push(r); } - quote_spanned! { ident.span() => { - #let_obj - for name in [(#(#names,)*)] { - vm.__module_set_attr(module, name, obj.clone()).unwrap(); - } - }} + ( + quote_spanned! { ident.span() => { + #let_obj + for name in [(#(#names,)*)] { + vm.__module_set_attr(module, name, obj.clone()).unwrap(); + } + }}, + names, + ) }; args.context .module_extend_items - .add_item(py_name, cfgs, tokens)?; + .add_item(ident, py_names, cfgs, tokens)?; Ok(()) } diff --git a/derive/src/util.rs b/derive/src/util.rs index eeaa144d80..05f408a18a 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -1,7 +1,6 @@ -use indexmap::map::IndexMap; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use syn::{ spanned::Spanned, Attribute, Ident, Meta, MetaList, NestedMeta, Path, Result, Signature, UseTree, @@ -25,34 +24,62 @@ pub(crate) const ALL_ALLOWED_NAMES: &[&str] = &[ "extend_class", ]; +struct NurseryItem { + attr_name: Ident, + py_names: Vec, + cfgs: Vec, + tokens: TokenStream, +} + #[derive(Default)] -pub(crate) struct ItemNursery(IndexMap<(String, Vec), TokenStream>); +pub(crate) struct ItemNursery(Vec); + +pub(crate) struct ValidatedItemNursery(ItemNursery); impl ItemNursery { pub fn add_item( &mut self, - name: String, + attr_name: Ident, + py_names: Vec, cfgs: Vec, tokens: TokenStream, ) -> Result<()> { - if let Some(existing) = self.0.insert((name.clone(), cfgs), tokens) { - Err(syn::Error::new_spanned( - existing, - format!("Duplicated #[py*] attribute found for '{}'", name), - )) - } else { - Ok(()) + self.0.push(NurseryItem { + attr_name, + py_names, + cfgs, + tokens, + }); + Ok(()) + } + + pub fn validate(self) -> Result { + let mut by_name: HashSet<(String, Vec)> = HashSet::new(); + for item in &self.0 { + for py_name in &item.py_names { + let inserted = by_name.insert((py_name.clone(), item.cfgs.clone())); + if !inserted { + return Err(syn::Error::new( + item.attr_name.span(), + &format!("Duplicated #[py*] attribute found for {:?}", &item.py_names), + )); + } + } } + Ok(ValidatedItemNursery(self)) } } -impl ToTokens for ItemNursery { +impl ToTokens for ValidatedItemNursery { fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(self.0.iter().map(|((_, cfgs), item)| { + let nursery = &self.0; + tokens.extend(nursery.0.iter().map(|item| { + let cfgs = &item.cfgs; + let tokens = &item.tokens; quote! { #( #cfgs )* { - #item + #tokens } } }))