From 29dc4dabbbcdb9a5a03a403b91eb78908c473bc2 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 23 Aug 2020 05:25:17 +0900 Subject: [PATCH] multiple error tracking --- derive/src/pyclass.rs | 125 +++++++++++++++++++++-------------------- derive/src/pymodule.rs | 92 ++++++++++++++++++------------ derive/src/util.rs | 24 +++++--- 3 files changed, 138 insertions(+), 103 deletions(-) diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 1fb6c8854..96c055e54 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -16,92 +16,99 @@ use syn_ext::ext::*; struct ImplContext { impl_extend_items: ItemNursery, getset_items: GetSetNursery, + errors: Vec, } pub(crate) fn impl_pyimpl( attr: AttributeArgs, item: Item, ) -> std::result::Result { - match item { + let mut context = ImplContext::default(); + let mut tokens = match item { Item::Impl(mut imp) => { - let mut context = ImplContext::default(); for item in imp.items.iter_mut() { - item.try_split_attr_mut(|attrs, item| { + let r = item.try_split_attr_mut(|attrs, item| { let (pyitems, cfgs) = attrs_to_content_items(&attrs, new_impl_item::)?; for pyitem in pyitems.iter().rev() { - pyitem.gen_impl_item(ImplItemArgs { + let r = pyitem.gen_impl_item(ImplItemArgs { item, attrs, context: &mut context, cfgs: cfgs.as_slice(), - })?; + }); + context.errors.ok_or_push(r); } Ok(()) - })?; + }); + context.errors.ok_or_push(r); } - context.getset_items.validate()?; + context.errors.ok_or_push(context.getset_items.validate()); - let tokens = { - let (with_impl, flags) = extract_impl_attrs(attr)?; - let ty = &imp.self_ty; - let getset_impl = &context.getset_items; - let extend_impl = &context.impl_extend_items; - quote! { - #imp - impl ::rustpython_vm::pyobject::PyClassImpl for #ty { - const TP_FLAGS: ::rustpython_vm::slots::PyTpFlags = ::rustpython_vm::slots::PyTpFlags::from_bits_truncate(#flags); + let (with_impl, flags) = extract_impl_attrs(attr)?; + let ty = &imp.self_ty; + let getset_impl = &context.getset_items; + let extend_impl = &context.impl_extend_items; + quote! { + #imp + impl ::rustpython_vm::pyobject::PyClassImpl for #ty { + const TP_FLAGS: ::rustpython_vm::slots::PyTpFlags = ::rustpython_vm::slots::PyTpFlags::from_bits_truncate(#flags); - fn impl_extend_class( - ctx: &::rustpython_vm::pyobject::PyContext, - class: &::rustpython_vm::obj::objtype::PyClassRef, - ) { - #getset_impl - #extend_impl - #with_impl - } - } - } - }; - Ok(tokens) - } - Item::Trait(mut trai) => { - let mut context = ImplContext::default(); - for item in trai.items.iter_mut() { - item.try_split_attr_mut(|attrs, item| { - let (pyitems, cfgs) = - attrs_to_content_items(&attrs, new_impl_item::)?; - for pyitem in pyitems.iter().rev() { - pyitem.gen_impl_item(ImplItemArgs { - item, - attrs, - context: &mut context, - cfgs: cfgs.as_slice(), - })?; - } - Ok(()) - })?; - } - context.getset_items.validate()?; - - let tokens = { - let getset_impl = &context.getset_items; - let extend_impl = &context.impl_extend_items; - trai.items.push(parse_quote! { - fn __extend_py_class( + fn impl_extend_class( ctx: &::rustpython_vm::pyobject::PyContext, class: &::rustpython_vm::obj::objtype::PyClassRef, ) { #getset_impl #extend_impl + #with_impl } + } + } + } + Item::Trait(mut trai) => { + let mut context = ImplContext::default(); + for item in trai.items.iter_mut() { + let r = item.try_split_attr_mut(|attrs, item| { + let (pyitems, cfgs) = + attrs_to_content_items(&attrs, new_impl_item::)?; + for pyitem in pyitems.iter().rev() { + let r = pyitem.gen_impl_item(ImplItemArgs { + item, + attrs, + context: &mut context, + cfgs: cfgs.as_slice(), + }); + context.errors.ok_or_push(r); + } + Ok(()) }); - trai.into_token_stream() - }; - Ok(tokens) + context.errors.ok_or_push(r); + } + context.errors.ok_or_push(context.getset_items.validate()); + + let getset_impl = &context.getset_items; + let extend_impl = &context.impl_extend_items; + trai.items.push(parse_quote! { + fn __extend_py_class( + ctx: &::rustpython_vm::pyobject::PyContext, + class: &::rustpython_vm::obj::objtype::PyClassRef, + ) { + #getset_impl + #extend_impl + } + }); + trai.into_token_stream() + } + item => quote!(#item), + }; + if let Some(error) = context.errors.into_error() { + let error = Diagnostic::from(error); + tokens = quote! { + #tokens + #error } - item => Ok(quote!(#item)), } + Ok(tokens) } fn generate_class_def( @@ -478,9 +485,7 @@ impl GetSetNursery { )); }; } - if !errors.is_empty() { - return Err(errors.into_error()); - } + errors.into_result()?; self.validated = true; Ok(()) } diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index c0187fa84..d85ae3553 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -1,6 +1,6 @@ use crate::error::Diagnostic; use crate::util::{ - AttributeExt, ClassItemMeta, ContentItem, ContentItemInner, ItemMeta, ItemNursery, + AttributeExt, ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, }; use proc_macro2::TokenStream; @@ -8,9 +8,11 @@ use quote::{quote, quote_spanned, ToTokens}; use syn::{parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Result, UseTree}; use syn_ext::ext::*; +#[derive(Default)] struct ModuleContext { name: String, module_extend_items: ItemNursery, + errors: Vec, } pub fn impl_pymodule( @@ -19,16 +21,16 @@ pub fn impl_pymodule( ) -> std::result::Result { let mut module_item = match module_item { Item::Mod(m) => m, - other => bail_span!(other, "#[pymodule] can only be on a module declaration"), + other => bail_span!(other, "#[pymodule] can only be on a full module"), }; let fake_ident = Ident::new("pymodule", module_item.span()); let module_meta = SimpleItemMeta::from_nested(module_item.ident.clone(), fake_ident, attr.into_iter())?; // generation resources - let mut module_context = ModuleContext { + let mut context = ModuleContext { name: module_meta.simple_name()?, - module_extend_items: ItemNursery::default(), + ..Default::default() }; let items = module_item.items_mut().ok_or_else(|| { module_meta.new_meta_error("requires actual module, not a module declaration") @@ -36,23 +38,25 @@ pub fn impl_pymodule( // collect to context for item in items.iter_mut() { - item.try_split_attr_mut(|attrs, item| { + let r = item.try_split_attr_mut(|attrs, item| { let (pyitems, cfgs) = attrs_to_module_items(&attrs, new_module_item)?; for pyitem in pyitems.iter().rev() { - pyitem.gen_module_item(ModuleItemArgs { + let r = pyitem.gen_module_item(ModuleItemArgs { item, attrs, - module: &mut module_context, + context: &mut context, cfgs: cfgs.as_slice(), - })?; + }); + context.errors.ok_or_push(r); } Ok(()) - })?; + }); + context.errors.ok_or_push(r); } // append additional items - let module_name = module_context.name.as_str(); - let module_extend_items = module_context.module_extend_items; + let module_name = context.name.as_str(); + let module_extend_items = context.module_extend_items; items.extend(vec![ parse_quote! { pub(crate) const MODULE_NAME: &'static str = #module_name; @@ -77,7 +81,15 @@ pub fn impl_pymodule( }, ]); - Ok(module_item.into_token_stream()) + Ok(if let Some(error) = context.errors.into_error() { + let error = Diagnostic::from(error); + quote! { + #module_item + #error + } + } else { + module_item.into_token_stream() + }) } fn new_module_item( @@ -219,13 +231,13 @@ impl ContentItem for AttributeItem { struct ModuleItemArgs<'a> { item: &'a Item, attrs: &'a mut Vec, - module: &'a mut ModuleContext, + context: &'a mut ModuleContext, cfgs: &'a [Attribute], } impl<'a> ModuleItemArgs<'a> { fn module_name(&'a self) -> &'a str { - self.module.name.as_str() + self.context.name.as_str() } } @@ -258,7 +270,7 @@ impl ModuleItem for FunctionItem { } }; - args.module + args.context .module_extend_items .add_item(py_name, args.cfgs.to_vec(), item)?; Ok(()) @@ -284,7 +296,7 @@ impl ModuleItem for ClassItem { class_attr, format!( "#[{name}] requires #[pyattr] to be a module attribute. \ - To keep it free type, try #[{name}(noattr)]", + To keep it free type, try #[{name}(noattr)]", name = self.attr_name() ), )); @@ -296,7 +308,7 @@ impl ModuleItem for ClassItem { class_attr.get_ident().unwrap().clone(), class_attr.promoted_nested()?.into_iter(), )?; - let module_name = args.module.name.clone(); + let module_name = args.context.name.clone(); class_attr.fill_nested_meta("module", || { parse_quote! {module = #module_name} })?; @@ -304,26 +316,34 @@ impl ModuleItem for ClassItem { (module_name, class_name) }; for attr_index in self.pyattrs.iter().rev() { - let attr_attr = args.attrs.remove(*attr_index); - let (meta_ident, nested) = attr_attr.ident_and_promoted_nested()?; - let item_meta = - SimpleItemMeta::from_nested(ident.clone(), meta_ident.clone(), nested.into_iter())?; + let mut loop_unit = || { + let attr_attr = args.attrs.remove(*attr_index); + let (meta_ident, nested) = attr_attr.ident_and_promoted_nested()?; + let item_meta = SimpleItemMeta::from_nested( + ident.clone(), + meta_ident.clone(), + nested.into_iter(), + )?; - let py_name = item_meta - .optional_name() - .unwrap_or_else(|| class_name.clone()); - let new_class = quote_spanned!(ident.span() => - #ident::make_class(&vm.ctx); - ); - let item = quote! { - let new_class = #new_class; - new_class.set_str_attr("__module__", vm.ctx.new_str(#module_name)); - vm.__module_set_attr(&module, #py_name, new_class).unwrap(); + let py_name = item_meta + .optional_name() + .unwrap_or_else(|| class_name.clone()); + let new_class = quote_spanned!(ident.span() => + #ident::make_class(&vm.ctx); + ); + let item = quote! { + let new_class = #new_class; + new_class.set_str_attr("__module__", vm.ctx.new_str(#module_name)); + vm.__module_set_attr(&module, #py_name, new_class).unwrap(); + }; + + args.context + .module_extend_items + .add_item(py_name, args.cfgs.to_vec(), item)?; + Ok(()) }; - - args.module - .module_extend_items - .add_item(py_name.clone(), args.cfgs.to_vec(), item)?; + let r = loop_unit(); + args.context.errors.ok_or_push(r); } Ok(()) } @@ -386,7 +406,7 @@ impl ModuleItem for AttributeItem { }; args.attrs.remove(self.index()); - args.module + args.context .module_extend_items .add_item(py_name, args.cfgs.to_vec(), tokens)?; diff --git a/derive/src/util.rs b/derive/src/util.rs index 250870b20..2fa3e40ef 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -397,19 +397,29 @@ impl AttributeExt for Attribute { } } -pub(crate) trait ErrorVec { - fn into_error(self) -> syn::Error; +pub(crate) trait ErrorVec: Sized { + fn into_error(self) -> Option; + fn into_result(self) -> Result<()> { + if let Some(error) = self.into_error() { + Err(error) + } else { + Ok(()) + } + } fn ok_or_push(&mut self, r: Result) -> Option; } impl ErrorVec for Vec { - fn into_error(self) -> syn::Error { + fn into_error(self) -> Option { let mut iter = self.into_iter(); - let mut first = iter.next().expect("Cannot convert empty vec to syn::Error"); - for err in iter { - first.combine(err); + if let Some(mut first) = iter.next() { + for err in iter { + first.combine(err); + } + Some(first) + } else { + None } - first } fn ok_or_push(&mut self, r: Result) -> Option { match r {