new name collition detection

This commit is contained in:
Jeong Yunwon
2022-04-20 00:57:06 +09:00
parent 95bd172278
commit 3582eb2e19
3 changed files with 133 additions and 80 deletions

View File

@@ -107,8 +107,8 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
} = extract_impl_attrs(attr, &Ident::new(&quote!(ty).to_string(), ty.span()))?;
let getset_impl = &context.getset_items;
let extend_impl = &context.impl_extend_items;
let slots_impl = &context.extend_slots_items;
let extend_impl = context.impl_extend_items.validate()?;
let slots_impl = context.extend_slots_items.validate()?;
let class_extensions = &context.class_extensions;
quote! {
#imp
@@ -143,8 +143,8 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
} = extract_impl_attrs(attr, &trai.ident)?;
let getset_impl = &context.getset_items;
let extend_impl = &context.impl_extend_items;
let slots_impl = &context.extend_slots_items;
let extend_impl = &context.impl_extend_items.validate()?;
let slots_impl = &context.extend_slots_items.validate()?;
let class_extensions = &context.class_extensions;
let extra_methods = iter_chain![
parse_quote! {
@@ -502,9 +502,12 @@ where
}
};
args.context
.impl_extend_items
.add_item(py_name, args.cfgs.to_vec(), tokens)?;
args.context.impl_extend_items.add_item(
ident.clone(),
vec![py_name],
args.cfgs.to_vec(),
tokens,
)?;
Ok(())
}
}
@@ -560,8 +563,10 @@ where
}
};
let pyname = format!("(slot {})", slot_name);
args.context.extend_slots_items.add_item(
format!("(slot {})", slot_name),
ident.clone(),
vec![pyname],
args.cfgs.to_vec(),
tokens,
)?;
@@ -583,32 +588,34 @@ where
let py_name = item_meta.simple_name()?;
Ok(py_name)
};
let (py_name, tokens) = if args.item.function_or_method().is_ok() || args.item.is_const() {
let ident = args.item.get_ident().unwrap();
let py_name = get_py_name(&attr, ident)?;
let (ident, py_name, tokens) =
if args.item.function_or_method().is_ok() || args.item.is_const() {
let ident = args.item.get_ident().unwrap();
let py_name = get_py_name(&attr, ident)?;
let value = if args.item.is_const() {
// TODO: ctx.new_value
quote_spanned!(ident.span() => 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(())
}

View File

@@ -87,7 +87,7 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result<TokenStre
// append additional items
let module_name = context.name.as_str();
let module_extend_items = context.module_extend_items;
let module_extend_items = context.module_extend_items.validate()?;
let doc = doc.or_else(|| {
crate::doc::try_read(module_name)
.ok()
@@ -317,7 +317,7 @@ impl ModuleItem for FunctionItem {
let py_name = item_meta.simple_name()?;
let sig_doc = text_signature(func.sig(), &py_name);
let item = {
let (tokens, py_names) = {
let module = args.module_name();
let doc = args.attrs.doc().or_else(|| {
crate::doc::try_module_item(module, &py_name)
@@ -340,13 +340,16 @@ impl ModuleItem for FunctionItem {
);
if self.pyattrs.is_empty() {
quote_spanned! { ident.span() => {
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(())
}

View File

@@ -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<String>,
cfgs: Vec<Attribute>,
tokens: TokenStream,
}
#[derive(Default)]
pub(crate) struct ItemNursery(IndexMap<(String, Vec<Attribute>), TokenStream>);
pub(crate) struct ItemNursery(Vec<NurseryItem>);
pub(crate) struct ValidatedItemNursery(ItemNursery);
impl ItemNursery {
pub fn add_item(
&mut self,
name: String,
attr_name: Ident,
py_names: Vec<String>,
cfgs: Vec<Attribute>,
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<ValidatedItemNursery> {
let mut by_name: HashSet<(String, Vec<Attribute>)> = 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
}
}
}))