From f67a6e47d767d912183b2339ce67ac81582a90f8 Mon Sep 17 00:00:00 2001 From: Nikita Strygin Date: Tue, 1 Aug 2023 16:35:09 +0300 Subject: [PATCH] [refactor] #3830: iroha_ffi_derive: replace proc-macro-error with manyhow --- Cargo.lock | 3 +- ffi/derive/Cargo.toml | 3 +- ffi/derive/src/convert.rs | 238 +++++++++++++++++---------------- ffi/derive/src/impl_visitor.rs | 145 +++++++++++++------- ffi/derive/src/lib.rs | 163 +++++++++++----------- ffi/derive/src/util.rs | 76 ++++++----- ffi/derive/src/wrapper.rs | 18 +-- 7 files changed, 355 insertions(+), 291 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a0e2472c9d..a7337099f20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3245,11 +3245,12 @@ dependencies = [ "derive_more", "getset", "iroha_ffi", - "proc-macro-error", + "manyhow", "proc-macro2", "quote", "rustc-hash", "syn 1.0.109", + "syn 2.0.26", "trybuild", ] diff --git a/ffi/derive/Cargo.toml b/ffi/derive/Cargo.toml index 54d3a073634..63af33fda9f 100644 --- a/ffi/derive/Cargo.toml +++ b/ffi/derive/Cargo.toml @@ -13,9 +13,10 @@ proc-macro = true [dependencies] syn = { workspace = true, features = ["full", "visit", "visit-mut", "extra-traits"] } +syn2 = { workspace = true, features = ["full", "visit", "visit-mut", "extra-traits"] } quote = { workspace = true } proc-macro2 = { workspace = true } -proc-macro-error = { workspace = true } +manyhow = { workspace = true, features = ["syn1"] } derive_more = { workspace = true, default-features = true } rustc-hash = { workspace = true } diff --git a/ffi/derive/src/convert.rs b/ffi/derive/src/convert.rs index 3769a2b047e..8d4651c863d 100644 --- a/ffi/derive/src/convert.rs +++ b/ffi/derive/src/convert.rs @@ -1,25 +1,25 @@ use core::str::FromStr as _; +use manyhow::{bail, emit, error_message, Emitter, Result}; use proc_macro2::TokenStream; -use proc_macro_error::{abort, OptionExt}; use quote::quote; use syn::{parse_quote, visit::Visit, Data, DataEnum, DeriveInput, Generics, Ident, Type}; use crate::{find_attr, is_opaque, is_repr_attr, without_repr}; -pub fn derive_ffi_type(mut input: DeriveInput) -> TokenStream { +pub fn derive_ffi_type(mut input: DeriveInput) -> Result { let name = &input.ident; if let Data::Enum(enum_) = &input.data { if enum_.variants.is_empty() { - abort!(enum_.variants, "Uninhabited enums are not allowed in FFI"); + bail!(enum_.variants, "Uninhabited enums are not allowed in FFI"); } } if is_opaque(&input) { - return derive_ffi_type_for_opaque_item(name, &input.generics); + return Ok(derive_ffi_type_for_opaque_item(name, &input.generics)); } - if is_transparent(&input) { + if is_transparent(&input)? { return derive_ffi_type_for_transparent_item(&mut input); } @@ -30,13 +30,13 @@ pub fn derive_ffi_type(mut input: DeriveInput) -> TokenStream { if is_fieldless_enum(item) { if item.variants.len() == 1 { // NOTE: one-variant fieldless enums have representation of () - return derive_ffi_type_for_opaque_item(name, &input.generics); + return Ok(derive_ffi_type_for_opaque_item(name, &input.generics)); } if without_repr(&repr) { - abort!(name, "Fieldless enum missing #[repr(int)]"); + bail!(name, "Fieldless enum missing #[repr(int)]"); } if let Some(variant) = item.variants.iter().find(|v| v.discriminant.is_some()) { - abort!( + bail!( variant, "Fieldless enums with explicit discriminants are prohibited", ) @@ -44,14 +44,14 @@ pub fn derive_ffi_type(mut input: DeriveInput) -> TokenStream { derive_ffi_type_for_fieldless_enum(&input.ident, item, &repr) } else { - verify_is_non_owning(&input.data); + verify_is_non_owning(&input.data)?; let local = !is_non_local(&input.attrs); derive_ffi_type_for_data_carrying_enum(&input.ident, input.generics, item, local) } } Data::Struct(item) => { - let ffi_type_impl = derive_ffi_type_for_repr_c(&input); + let ffi_type_impl = derive_ffi_type_for_repr_c(&input)?; let repr_c_impl = { let predicates = &mut input.generics.make_where_clause().predicates; @@ -74,13 +74,13 @@ pub fn derive_ffi_type(mut input: DeriveInput) -> TokenStream { derive_unsafe_repr_c(&input.ident, &input.generics) }; - quote! { + Ok(quote! { #repr_c_impl #ffi_type_impl - } + }) } Data::Union(item) => { - let ffi_type_impl = derive_ffi_type_for_repr_c(&input); + let ffi_type_impl = derive_ffi_type_for_repr_c(&input)?; let repr_c_impl = { let predicates = &mut input.generics.make_where_clause().predicates; @@ -95,10 +95,10 @@ pub fn derive_ffi_type(mut input: DeriveInput) -> TokenStream { derive_unsafe_repr_c(&input.ident, &input.generics) }; - quote! { + Ok(quote! { #repr_c_impl #ffi_type_impl - } + }) } } } @@ -132,7 +132,7 @@ fn derive_ffi_type_for_opaque_item(name: &Ident, generics: &Generics) -> TokenSt } } -fn derive_ffi_type_for_transparent_item(input: &mut syn::DeriveInput) -> TokenStream { +fn derive_ffi_type_for_transparent_item(input: &mut syn::DeriveInput) -> Result { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name = &input.ident; @@ -141,17 +141,16 @@ fn derive_ffi_type_for_transparent_item(input: &mut syn::DeriveInput) -> TokenSt // It is just assumed that it is the first field let inner = match &input.data { Data::Enum(item) => { - let first_variant = item - .variants - .iter() - .next() - .expect_or_abort("transparent enum must have at least one variant"); + let first_variant = + item.variants.iter().next().ok_or_else(|| { + error_message!("transparent enum must have at least one variant") + })?; if let Some(first_variant) = first_variant.fields.iter().next() { &first_variant.ty } else { // NOTE: one-variant fieldless enums have representation of () - return derive_ffi_type_for_opaque_item(name, &input.generics); + return Ok(derive_ffi_type_for_opaque_item(name, &input.generics)); } } Data::Struct(item) => { @@ -159,35 +158,35 @@ fn derive_ffi_type_for_transparent_item(input: &mut syn::DeriveInput) -> TokenSt &first_field.ty } else { // NOTE: Fieldless structs have representation of () - return derive_ffi_type_for_opaque_item(name, &input.generics); + return Ok(derive_ffi_type_for_opaque_item(name, &input.generics)); } } Data::Union(_) => unreachable!("https://github.com/rust-lang/rust/issues/60405"), }; if is_robust(&input.attrs) { - return quote! { + return Ok(quote! { iroha_ffi::ffi_type! { // SAFETY: User must make sure the type is robust unsafe impl #impl_generics Transparent for #name #ty_generics #where_clause { type Target = #inner; } } - }; + }); } - quote! {} + Ok(quote! {}) } fn derive_ffi_type_for_fieldless_enum( enum_name: &Ident, enum_: &DataEnum, repr: &[syn::NestedMeta], -) -> TokenStream { - let ffi_type = enum_size(enum_name, repr); +) -> Result { + let ffi_type = enum_size(enum_name, repr)?; let (discriminants, discriminant_decls) = gen_discriminants(enum_name, enum_, &ffi_type); - quote! { + Ok(quote! { iroha_ffi::ffi_type! { unsafe impl Transparent for #enum_name { type Target = #ffi_type; @@ -207,7 +206,7 @@ fn derive_ffi_type_for_fieldless_enum( impl iroha_ffi::WrapperTypeOf<#enum_name> for #ffi_type { type Type = #enum_name; } - } + }) } #[allow(clippy::too_many_lines)] @@ -216,9 +215,9 @@ fn derive_ffi_type_for_data_carrying_enum( mut generics: Generics, enum_: &DataEnum, local: bool, -) -> TokenStream { +) -> Result { let (repr_c_enum_name, repr_c_enum) = - gen_data_carrying_repr_c_enum(enum_name, &mut generics, enum_); + gen_data_carrying_repr_c_enum(enum_name, &mut generics, enum_)?; generics.make_where_clause(); let lifetime = quote! {'__iroha_ffi_itm}; @@ -237,7 +236,7 @@ fn derive_ffi_type_for_data_carrying_enum( }, ) }) - .collect::>(); + .collect::>>()?; let variant_ffi_stores = enum_ .variants @@ -252,35 +251,40 @@ fn derive_ffi_type_for_data_carrying_enum( }, ) }) - .collect::>(); + .collect::>>()?; - let variants_into_ffi = enum_.variants.iter().enumerate().map(|(i, variant)| { - let idx = TokenStream::from_str(&format!("{i}")).expect("Valid"); - let payload_name = gen_repr_c_enum_payload_name(enum_name); - let variant_name = &variant.ident; + let variants_into_ffi = enum_ + .variants + .iter() + .enumerate() + .map(|(i, variant)| { + let idx = TokenStream::from_str(&format!("{i}")).expect("Valid"); + let payload_name = gen_repr_c_enum_payload_name(enum_name); + let variant_name = &variant.ident; - variant_mapper( - variant, - || { - quote! { Self::#variant_name => #repr_c_enum_name { - tag: #idx, payload: #payload_name {#variant_name: ()} - }} - }, - |_| { - quote! { - Self::#variant_name(payload) => { - let payload = #payload_name { - #variant_name: core::mem::ManuallyDrop::new( - iroha_ffi::FfiConvert::into_ffi(payload, &mut store.#idx) - ) - }; - - #repr_c_enum_name { tag: #idx, payload } + variant_mapper( + variant, + || { + quote! { Self::#variant_name => #repr_c_enum_name { + tag: #idx, payload: #payload_name {#variant_name: ()} + }} + }, + |_| { + quote! { + Self::#variant_name(payload) => { + let payload = #payload_name { + #variant_name: core::mem::ManuallyDrop::new( + iroha_ffi::FfiConvert::into_ffi(payload, &mut store.#idx) + ) + }; + + #repr_c_enum_name { tag: #idx, payload } + } } - } - }, - ) - }); + }, + ) + }) + .collect::>>()?; let variants_try_from_ffi = enum_.variants.iter().enumerate().map(|(i, variant)| { let idx = TokenStream::from_str(&format!("{i}")).expect("Valid"); @@ -301,7 +305,7 @@ fn derive_ffi_type_for_data_carrying_enum( } }, ) - }); + }).collect::>>()?; // TODO: Tuples don't support impl of `Default` for arity > 12 currently. // Once this limitation is lifted `Option` will not be necessary @@ -325,25 +329,28 @@ fn derive_ffi_type_for_data_carrying_enum( let non_locality = if local { quote! {} } else { - let mut non_local_where_clause = where_clause.expect_or_abort("Defined").clone(); + let mut non_local_where_clause = where_clause + .expect("Expected where_clause to be defined") // TODO: is this user-facing? + .clone(); - enum_ - .variants - .iter() - .filter_map(|variant| match &variant.fields { + for variant in &enum_.variants { + let ty = match &variant.fields { syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { - Some(&unnamed[0].ty) + &unnamed[0].ty } syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { - abort!(unnamed, "Only 1-sized variants are supported") + bail!(unnamed, "Only 1-sized variants are supported") } syn::Fields::Named(syn::FieldsNamed { named, .. }) => { - abort!(named, "Named variants are not supported") + bail!(named, "Named variants are not supported") } - syn::Fields::Unit => None, - }) - .map(|ty| parse_quote! {#ty: iroha_ffi::repr_c::NonLocal<<#ty as iroha_ffi::ir::Ir>::Type>}) - .for_each(|predicate| non_local_where_clause.predicates.push(predicate)); + syn::Fields::Unit => continue, + }; + + non_local_where_clause.predicates.push( + parse_quote! {#ty: iroha_ffi::repr_c::NonLocal<<#ty as iroha_ffi::ir::Ir>::Type>}, + ); + } quote! { unsafe impl<#impl_generics> iroha_ffi::repr_c::NonLocal for #enum_name #ty_generics #non_local_where_clause {} @@ -368,7 +375,7 @@ fn derive_ffi_type_for_data_carrying_enum( } }; - quote! { + Ok(quote! { #repr_c_enum // NOTE: Data-carrying enum cannot implement `ReprC` unless it is robust `repr(C)` @@ -405,32 +412,32 @@ fn derive_ffi_type_for_data_carrying_enum( impl<#impl_generics> iroha_ffi::repr_c::Cloned for #enum_name #ty_generics #where_clause where Self: Clone {} #non_locality - } + }) } -fn derive_ffi_type_for_repr_c(input: &DeriveInput) -> TokenStream { - verify_is_non_owning(&input.data); - assert_is_repr_c(&input.attrs); +fn derive_ffi_type_for_repr_c(input: &DeriveInput) -> Result { + verify_is_non_owning(&input.data)?; + assert_is_repr_c(&input.attrs)?; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name = &input.ident; - quote! { + Ok(quote! { iroha_ffi::ffi_type! { impl #impl_generics Robust for #name #ty_generics #where_clause {} } - } + }) } fn gen_data_carrying_repr_c_enum( enum_name: &Ident, generics: &mut Generics, enum_: &DataEnum, -) -> (Ident, TokenStream) { - let (payload_name, payload) = gen_data_carrying_enum_payload(enum_name, generics, enum_); +) -> Result<(Ident, TokenStream)> { + let (payload_name, payload) = gen_data_carrying_enum_payload(enum_name, generics, enum_)?; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let doc = format!(" [`ReprC`] equivalent of [`{enum_name}`]"); - let enum_tag_type = gen_enum_tag_type(enum_name, enum_); + let enum_tag_type = gen_enum_tag_type(enum_name, enum_)?; let repr_c_enum_name = gen_repr_c_enum_name(enum_name); let repr_c_enum = quote! { @@ -448,14 +455,14 @@ fn gen_data_carrying_repr_c_enum( unsafe impl #impl_generics iroha_ffi::ReprC for #repr_c_enum_name #ty_generics #where_clause {} }; - (repr_c_enum_name, repr_c_enum) + Ok((repr_c_enum_name, repr_c_enum)) } fn gen_data_carrying_enum_payload( enum_name: &Ident, generics: &mut Generics, enum_: &DataEnum, -) -> (Ident, TokenStream) { +) -> Result<(Ident, TokenStream)> { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let field_names = enum_.variants.iter().map(|variant| &variant.ident); let payload_name = gen_repr_c_enum_payload_name(enum_name); @@ -474,7 +481,7 @@ fn gen_data_carrying_enum_payload( }, ) }) - .collect::>(); + .collect::>>()?; let payload = quote! { #[repr(C)] @@ -489,7 +496,7 @@ fn gen_data_carrying_enum_payload( unsafe impl #impl_generics iroha_ffi::ReprC for #payload_name #ty_generics #where_clause {} }; - (payload_name, payload) + Ok((payload_name, payload)) } fn gen_discriminants( @@ -531,18 +538,18 @@ fn variant_mapper TokenStream, F1: FnOnce(&syn::Field) -> TokenS variant: &syn::Variant, unit_mapper: F0, field_mapper: F1, -) -> TokenStream { +) -> Result { match &variant.fields { syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { - field_mapper(&unnamed[0]) + Ok(field_mapper(&unnamed[0])) } syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { - abort!(unnamed, "Only 1-sized variants are supported") + bail!(unnamed, "Only 1-sized variants are supported") } syn::Fields::Named(syn::FieldsNamed { named, .. }) => { - abort!(named, "Named variants are not supported") + bail!(named, "Named variants are not supported") } - syn::Fields::Unit => unit_mapper(), + syn::Fields::Unit => Ok(unit_mapper()), } } @@ -560,7 +567,7 @@ fn gen_repr_c_enum_payload_name(enum_name: &Ident) -> Ident { ) } -fn is_transparent(input: &DeriveInput) -> bool { +fn is_transparent(input: &DeriveInput) -> Result { let repr = &find_attr(&input.attrs, "repr"); is_repr_attr(repr, "transparent") } @@ -572,10 +579,10 @@ fn is_robust(attrs: &[syn::Attribute]) -> bool { // NOTE: Except for the raw pointers there should be no other type // that is at the same time Robust and also transfers ownership -fn verify_is_non_owning(data: &syn::Data) { - const RAW_POINTER_FOUND_MSG: &str = "Raw pointer found. If the pointer doesn't own the data, attach `#[ffi_type(unsafe {non_owning})` to the field. Otherwise, mark the entire type as opaque with `#[ffi_type(opaque)]`"; - - struct PtrVistor; +fn verify_is_non_owning(data: &syn::Data) -> Result<()> { + struct PtrVistor { + emitter: Emitter, + } impl Visit<'_> for PtrVistor { fn visit_field(&mut self, node: &syn::Field) { let non_owning = parse_quote!(#[ffi_type(unsafe {non_owning})]); @@ -585,12 +592,16 @@ fn verify_is_non_owning(data: &syn::Data) { } } fn visit_type_ptr(&mut self, node: &syn::TypePtr) { - abort!(node, RAW_POINTER_FOUND_MSG); + emit!(self.emitter, node, "Raw pointer found. If the pointer doesn't own the data, attach `#[ffi_type(unsafe {{non_owning}})` to the field. Otherwise, mark the entire type as opaque with `#[ffi_type(opaque)]`"); } } - let mut ptr_visitor = PtrVistor; + let mut ptr_visitor = PtrVistor { + emitter: Emitter::new(), + }; ptr_visitor.visit_data(data); + + ptr_visitor.emitter.into_result() } fn is_non_local(attrs: &[syn::Attribute]) -> bool { @@ -604,42 +615,43 @@ fn is_fieldless_enum(item: &DataEnum) -> bool { .all(|variant| matches!(variant.fields, syn::Fields::Unit)) } -fn assert_is_repr_c(attrs: &[syn::Attribute]) { +fn assert_is_repr_c(attrs: &[syn::Attribute]) -> Result<()> { let repr = find_attr(attrs, "repr"); - assert!(is_repr_attr(&repr, "C")); + assert!(is_repr_attr(&repr, "C")?); + Ok(()) } -fn enum_size(enum_name: &Ident, repr: &[syn::NestedMeta]) -> Type { - if is_repr_attr(repr, "u8") { +fn enum_size(enum_name: &Ident, repr: &[syn::NestedMeta]) -> Result { + Ok(if is_repr_attr(repr, "u8")? { parse_quote! {u8} - } else if is_repr_attr(repr, "i8") { + } else if is_repr_attr(repr, "i8")? { parse_quote! {i8} - } else if is_repr_attr(repr, "u16") { + } else if is_repr_attr(repr, "u16")? { parse_quote! {u16} - } else if is_repr_attr(repr, "i16") { + } else if is_repr_attr(repr, "i16")? { parse_quote! {i16} - } else if is_repr_attr(repr, "u32") { + } else if is_repr_attr(repr, "u32")? { parse_quote! {u32} - } else if is_repr_attr(repr, "i32") { + } else if is_repr_attr(repr, "i32")? { parse_quote! {i32} } else { - abort!(enum_name, "Enum representation not supported") - } + bail!(enum_name, "Enum representation not supported") + }) } -fn gen_enum_tag_type(enum_name: &Ident, enum_: &DataEnum) -> TokenStream { +fn gen_enum_tag_type(enum_name: &Ident, enum_: &DataEnum) -> Result { const U8_MAX: usize = u8::MAX as usize; const U16_MAX: usize = u16::MAX as usize; const U32_MAX: usize = u32::MAX as usize; // NOTE: Arms are matched in the order of declaration #[allow(clippy::match_overlapping_arm)] - match enum_.variants.len() { + Ok(match enum_.variants.len() { 0..=U8_MAX => quote! {u8}, 0..=U16_MAX => quote! {u16}, 0..=U32_MAX => quote! {u32}, - _ => abort!(enum_name, "Too many variants"), - } + _ => bail!(enum_name, "Too many variants"), + }) } fn split_for_impl( diff --git a/ffi/derive/src/impl_visitor.rs b/ffi/derive/src/impl_visitor.rs index 9d3453af0b1..f81a5ae06d1 100644 --- a/ffi/derive/src/impl_visitor.rs +++ b/ffi/derive/src/impl_visitor.rs @@ -1,6 +1,6 @@ use derive_more::Constructor; +use manyhow::{emit, Emitter, Result}; use proc_macro2::Span; -use proc_macro_error::{abort, OptionExt}; use syn::{ parse_quote, visit::{visit_signature, Visit}, @@ -94,6 +94,7 @@ pub struct FnDescriptor<'ast> { } struct ImplVisitor<'ast> { + emitter: Emitter, attrs: Vec<&'ast Attribute>, trait_name: Option<&'ast Path>, /// Resolved type of the `Self` type @@ -103,6 +104,7 @@ struct ImplVisitor<'ast> { } struct FnVisitor<'ast> { + emitter: Emitter, attrs: Vec<&'ast Attribute>, doc: Vec<&'ast syn::Attribute>, trait_name: Option<&'ast Path>, @@ -124,20 +126,21 @@ struct FnVisitor<'ast> { } impl<'ast> ImplDescriptor<'ast> { - pub fn from_impl(node: &'ast syn::ItemImpl) -> Self { + pub fn from_impl(node: &'ast syn::ItemImpl) -> Result { let mut visitor = ImplVisitor::new(); visitor.visit_item_impl(node); ImplDescriptor::from_visitor(visitor) } - fn from_visitor(visitor: ImplVisitor<'ast>) -> Self { - Self { + fn from_visitor(mut visitor: ImplVisitor<'ast>) -> Result { + visitor.emitter.into_result()?; + Ok(Self { attrs: visitor.attrs, trait_name: visitor.trait_name, associated_types: visitor.associated_types, fns: visitor.fns, - } + }) } pub fn trait_name(&self) -> Option<&Ident> { @@ -150,32 +153,33 @@ impl<'ast> FnDescriptor<'ast> { self_ty: &'ast Path, trait_name: Option<&'ast Path>, node: &'ast syn::ImplItemMethod, - ) -> Self { + ) -> Result { let mut visitor = FnVisitor::new(Some(self_ty), trait_name); visitor.visit_impl_item_method(node); FnDescriptor::from_visitor(visitor) } - pub fn from_fn(node: &'ast syn::ItemFn) -> Self { + pub fn from_fn(node: &'ast syn::ItemFn) -> Result { let mut visitor = FnVisitor::new(None, None); visitor.visit_item_fn(node); Self::from_visitor(visitor) } - fn from_visitor(visitor: FnVisitor<'ast>) -> Self { - Self { + fn from_visitor(mut visitor: FnVisitor<'ast>) -> Result { + visitor.emitter.into_result()?; + Ok(Self { attrs: visitor.attrs, doc: visitor.doc, self_ty: visitor.self_ty.map(Clone::clone), - sig: visitor.sig.expect_or_abort("Missing signature").clone(), + sig: visitor.sig.expect("Missing signature").clone(), receiver: visitor.receiver, input_args: visitor.input_args, output_arg: visitor.output_arg, - } + }) } pub fn self_ty_name(&self) -> Option<&Ident> { @@ -184,8 +188,9 @@ impl<'ast> FnDescriptor<'ast> { } impl<'ast> ImplVisitor<'ast> { - const fn new() -> Self { + fn new() -> Self { Self { + emitter: Emitter::new(), attrs: Vec::new(), trait_name: None, self_ty: None, @@ -197,19 +202,28 @@ impl<'ast> ImplVisitor<'ast> { fn visit_self_type(&mut self, node: &'ast Type) { if let Type::Path(self_ty) = node { if self_ty.qself.is_some() { - abort!(self_ty, "Qualified types are not supported as self type"); + emit!( + self.emitter, + self_ty, + "Qualified types are not supported as self type" + ); } self.self_ty = Some(&self_ty.path); } else { - abort!(node, "Only nominal types are supported as self type"); + emit!( + self.emitter, + node, + "Only nominal types are supported as self type" + ); } } } impl<'ast> FnVisitor<'ast> { - pub const fn new(self_ty: Option<&'ast Path>, trait_name: Option<&'ast Path>) -> Self { + pub fn new(self_ty: Option<&'ast Path>, trait_name: Option<&'ast Path>) -> Self { Self { + emitter: Emitter::new(), attrs: Vec::new(), doc: Vec::new(), trait_name, @@ -226,7 +240,7 @@ impl<'ast> FnVisitor<'ast> { } fn add_input_arg(&mut self, src_type: &'ast Type) { - let arg_name = self.curr_arg_name.take().expect_or_abort("Defined").clone(); + let arg_name = self.curr_arg_name.take().expect("Defined").clone(); self.input_args.push(Arg::new( self.self_ty.map(Clone::clone), arg_name, @@ -253,14 +267,15 @@ impl<'ast> Visit<'ast> for ImplVisitor<'ast> { self.attrs.push(node); } fn visit_generic_param(&mut self, node: &'ast syn::GenericParam) { - abort!(node, "Generics are not supported"); + emit!(self.emitter, node, "Generics are not supported"); } fn visit_item_impl(&mut self, node: &'ast syn::ItemImpl) { if node.unsafety.is_some() { - abort!(node.generics, "Unsafe impl not supported"); + emit!(self.emitter, node.unsafety, "Unsafe impl not supported"); } if node.defaultness.is_some() { - abort!(node.generics, "Default impl not supported"); + emit!(self.emitter, node.defaultness, "Default impl not supported"); + // TODO: should we return here, or can the execution continue? } for it in &node.attrs { @@ -271,7 +286,7 @@ impl<'ast> Visit<'ast> for ImplVisitor<'ast> { self.trait_name = node.trait_.as_ref().map(|(_, trait_, _)| trait_); self.visit_self_type(&node.self_ty); - let self_ty = self.self_ty.expect_or_abort("Defined"); + let self_ty = self.self_ty.expect("Defined"); self.associated_types .extend(node.items.iter().filter_map(|item| match item { syn::ImplItem::Type(associated_type) => { @@ -279,21 +294,19 @@ impl<'ast> Visit<'ast> for ImplVisitor<'ast> { } _ => None, })); - self.fns - .extend(node.items.iter().filter_map(|item| match item { - syn::ImplItem::Method(method) => { - // NOTE: private methods in inherent impl are skipped - if self.trait_name.is_none() && !matches!(method.vis, Visibility::Public(_)) { - return None; - } - Some(FnDescriptor::from_impl_method( - self_ty, - self.trait_name, - method, - )) + + for item in node.items.iter() { + if let syn::ImplItem::Method(method) = item { + // NOTE: private methods in inherent impl are skipped + if self.trait_name.is_none() && !matches!(method.vis, Visibility::Public(_)) { + continue; } - _ => None, - })); + match FnDescriptor::from_impl_method(self_ty, self.trait_name, method) { + Ok(desc) => self.fns.push(desc), + Err(e) => self.emitter.emit(e), + } + } + } } } @@ -307,10 +320,10 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { } fn visit_abi(&mut self, node: &'ast syn::Abi) { - abort!(node, "You shouldn't specify function ABI"); + emit!(self.emitter, node, "You shouldn't specify function ABI"); } fn visit_generic_param(&mut self, node: &'ast syn::GenericParam) { - abort!(node, "Generics are not supported"); + emit!(self.emitter, node, "Generics are not supported"); } fn visit_impl_item_method(&mut self, node: &'ast syn::ImplItemMethod) { for attr in &node.attrs { @@ -332,24 +345,44 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { } fn visit_visibility(&mut self, node: &'ast syn::Visibility) { if self.trait_name.is_none() && !matches!(node, Visibility::Public(_)) { - abort!(node, "Private methods should not be exported"); + emit!(self.emitter, node, "Private methods should not be exported"); } } fn visit_signature(&mut self, node: &'ast syn::Signature) { if node.constness.is_some() { - abort!(node.constness, "Const functions not supported"); + emit!( + self.emitter, + node.constness, + "Const functions not supported" + ); } if node.asyncness.is_some() { - abort!(node.asyncness, "Async functions not supported"); + emit!( + self.emitter, + node.asyncness, + "Async functions not supported" + ); } if node.unsafety.is_some() { - abort!(node.unsafety, "You shouldn't specify function unsafety"); + emit!( + self.emitter, + node.unsafety, + "You shouldn't specify function unsafety" + ); } if node.abi.is_some() { - abort!(node.abi, "Extern fn declarations not supported") + emit!( + self.emitter, + node.abi, + "Extern fn declarations not supported" + ) } if node.variadic.is_some() { - abort!(node.variadic, "Variadic arguments not supported") + emit!( + self.emitter, + node.variadic, + "Variadic arguments not supported" + ) } visit_signature(self, node); @@ -361,7 +394,7 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { } if let Some((_, lifetime)) = &node.reference { if lifetime.is_some() { - abort!(lifetime, "Explicit lifetimes not supported"); + emit!(self.emitter, lifetime, "Explicit lifetimes not supported"); } } @@ -369,7 +402,7 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { || parse_quote! {Self}, |it| { if it.1.is_some() { - abort!(it.1, "Explicit lifetime not supported"); + emit!(self.emitter, it.1, "Explicit lifetime not supported"); } if node.mutability.is_some() { @@ -396,7 +429,11 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { if let syn::Pat::Ident(ident) = &*node.pat { self.visit_pat_ident(ident); } else { - abort!(node.pat, "Unsupported pattern in variable name binding"); + emit!( + self.emitter, + node.pat, + "Unsupported pattern in variable name binding" + ); } self.add_input_arg(&node.ty); @@ -407,13 +444,21 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { self.visit_attribute(it); } if node.by_ref.is_some() { - abort!(node.by_ref, "ref patterns not supported in argument name"); + emit!( + self.emitter, + node.by_ref, + "ref patterns not supported in argument name" + ); } if node.mutability.is_some() { // NOTE: It's irrelevant } if node.subpat.is_some() { - abort!(node, "Subpatterns not supported in argument name"); + emit!( + self.emitter, + node, + "Subpatterns not supported in argument name" + ); } self.curr_arg_name = Some(&node.ident); @@ -479,7 +524,7 @@ impl VisitMut for TypeImplTraitResolver { if let Type::ImplTrait(impl_trait) = node { for bound in &impl_trait.bounds { if let syn::TypeParamBound::Trait(trait_) = bound { - let trait_ = trait_.path.segments.last().expect_or_abort("Defined"); + let trait_ = trait_.path.segments.last().expect("Defined"); if trait_.ident == "IntoIterator" || trait_.ident == "ExactSizeIterator" { if let syn::PathArguments::AngleBracketed(args) = &trait_.arguments { @@ -521,12 +566,12 @@ impl VisitMut for TypeImplTraitResolver { } fn last_seg_ident(path: &syn::Path) -> &Ident { - &path.segments.last().expect_or_abort("Defined").ident + &path.segments.last().expect("Defined").ident } pub fn unwrap_result_type(node: &Type) -> Option<(&Type, &Type)> { if let Type::Path(type_) = node { - let last_seg = type_.path.segments.last().expect_or_abort("Defined"); + let last_seg = type_.path.segments.last().expect("Defined"); if last_seg.ident == "Result" { if let syn::PathArguments::AngleBracketed(args) = &last_seg.arguments { diff --git a/ffi/derive/src/lib.rs b/ffi/derive/src/lib.rs index 7ba99138b21..55c7a9da05e 100644 --- a/ffi/derive/src/lib.rs +++ b/ffi/derive/src/lib.rs @@ -2,10 +2,10 @@ #![allow(clippy::arithmetic_side_effects)] use impl_visitor::{FnDescriptor, ImplDescriptor}; -use proc_macro::TokenStream; -use proc_macro_error::abort; +use manyhow::{bail, manyhow, Result}; +use proc_macro2::TokenStream; use quote::quote; -use syn::{parse_macro_input, parse_quote, Item, NestedMeta}; +use syn::{parse_quote, Item, NestedMeta}; use wrapper::wrap_method; use crate::convert::derive_ffi_type; @@ -31,7 +31,7 @@ impl syn::parse::Parse for FfiItems { } impl quote::ToTokens for FfiItems { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + fn to_tokens(&self, tokens: &mut TokenStream) { let items = &self.0; tokens.extend(quote! {#(#items)*}) } @@ -39,7 +39,7 @@ impl quote::ToTokens for FfiItems { fn has_getset_attr(attrs: &[syn::Attribute]) -> bool { for derive in find_attr(attrs, "derive") { - if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = &derive { + if let NestedMeta::Meta(syn::Meta::Path(path)) = &derive { if path.segments.first().expect("Must have one segment").ident == "getset" { return true; } @@ -57,55 +57,58 @@ fn has_getset_attr(attrs: &[syn::Attribute]) -> bool { /// item wrapped with this macro (e.g. fieldless enums). This is so that most of the time /// users can safely wrap all of their structs with this macro and not be concerned with the /// cognitive load of figuring out which structs are converted to opaque pointers. +#[manyhow] #[proc_macro] -#[proc_macro_error::proc_macro_error] -pub fn ffi(input: TokenStream) -> TokenStream { - let items = parse_macro_input!(input as FfiItems).0; - - let items = items.into_iter().map(|item| { - if !matches!(item.vis, syn::Visibility::Public(_)) { - abort!(item, "Only public types are allowed in FFI"); - } - - if !is_opaque(&item) { - return quote! { - #[derive(iroha_ffi::FfiType)] - #item - }; - } - - if let syn::Data::Struct(struct_) = &item.data { - if has_getset_attr(&item.attrs) { - let derived_methods: Vec<_> = - util::gen_derived_methods(&item.ident, &item.attrs, &struct_.fields).collect(); +pub fn ffi(input: TokenStream) -> Result { + let items = syn::parse2::(input)?.0; + + let items = items + .into_iter() + .map(|item| { + if !matches!(item.vis, syn::Visibility::Public(_)) { + bail!(item, "Only public types are allowed in FFI"); + } - let ffi_fns: Vec<_> = derived_methods - .iter() - .map(|fn_| ffi_fn::gen_declaration(fn_, None)) - .collect(); - - let impl_block = wrapper::wrap_impl_items(&ImplDescriptor { - attrs: Vec::new(), - trait_name: None, - associated_types: Vec::new(), - fns: derived_methods, + if !is_opaque(&item) { + return Ok(quote! { + #[derive(iroha_ffi::FfiType)] + #item }); - let opaque = wrapper::wrap_as_opaque(item); - - return quote! { - #opaque + } - #impl_block - #(#ffi_fns)* - }; + if let syn::Data::Struct(struct_) = &item.data { + if has_getset_attr(&item.attrs) { + let derived_methods: Vec<_> = + util::gen_derived_methods(&item.ident, &item.attrs, &struct_.fields)? + .collect(); + + let ffi_fns: Vec<_> = derived_methods + .iter() + .map(|fn_| ffi_fn::gen_declaration(fn_, None)) + .collect(); + + let impl_block = wrapper::wrap_impl_items(&ImplDescriptor { + attrs: Vec::new(), + trait_name: None, + associated_types: Vec::new(), + fns: derived_methods, + }); + let opaque = wrapper::wrap_as_opaque(item)?; + + return Ok(quote! { + #opaque + + #impl_block + #(#ffi_fns)* + }); + } } - } - let opaque = wrapper::wrap_as_opaque(item); - quote! { #opaque } - }); + wrapper::wrap_as_opaque(item) + }) + .collect::>>()?; - quote! { #(#items)* }.into() + Ok(quote! { #(#items)* }) } // TODO: ffi_type(`local`) is a workaround for https://github.com/rust-lang/rust/issues/48214 @@ -150,17 +153,16 @@ pub fn ffi(input: TokenStream) -> TokenStream { /// /// * wrapping type must allow for all possible values of the pointer including `null` (it's robust) /// * the wrapping types's field of the pointer type must not carry ownership (it's non owning) +#[manyhow] #[proc_macro_derive(FfiType, attributes(ffi_type))] -#[proc_macro_error::proc_macro_error] -pub fn ffi_type_derive(input: TokenStream) -> TokenStream { - let item = parse_macro_input!(input as syn::DeriveInput); +pub fn ffi_type_derive(input: TokenStream) -> Result { + let item: syn::DeriveInput = syn::parse2(input)?; if !matches!(item.vis, syn::Visibility::Public(_)) { - abort!(item, "Only public types are allowed in FFI"); + bail!(item, "Only public types are allowed in FFI"); } - let ffi_derive = derive_ffi_type(item); - quote! { #ffi_derive }.into() + derive_ffi_type(item) } /// Generate FFI functions @@ -212,16 +214,16 @@ pub fn ffi_type_derive(input: TokenStream) -> TokenStream { /// FfiReturn::Ok /// } */ /// ``` +#[manyhow] #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] -pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { - match parse_macro_input!(item) { +pub fn ffi_export(attr: TokenStream, item: TokenStream) -> Result { + Ok(match syn::parse2(item)? { Item::Impl(item) => { if !attr.is_empty() { - abort!(item, "Unknown tokens in the attribute"); + bail!(item, "Unknown tokens in the attribute"); } - let impl_descriptor = ImplDescriptor::from_impl(&item); + let impl_descriptor = ImplDescriptor::from_impl(&item)?; let ffi_fns = impl_descriptor .fns .iter() @@ -233,7 +235,7 @@ pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { } } Item::Fn(item) => { - let fn_descriptor = FnDescriptor::from_fn(&item); + let fn_descriptor = FnDescriptor::from_fn(&item)?; let ffi_fn = ffi_fn::gen_definition(&fn_descriptor, None); quote! { @@ -243,14 +245,15 @@ pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { } Item::Struct(item) => { if !is_opaque_struct(&item) || !has_getset_attr(&item.attrs) { - return quote! { #item }.into(); + return Ok(quote! { #item }); } if !item.generics.params.is_empty() { - abort!(item.generics, "Generics on derived methods not supported"); + bail!(item.generics, "Generics on derived methods not supported"); } - let derived_ffi_fns = util::gen_derived_methods(&item.ident, &item.attrs, &item.fields) - .map(|fn_| ffi_fn::gen_definition(&fn_, None)); + let derived_ffi_fns = + util::gen_derived_methods(&item.ident, &item.attrs, &item.fields)? + .map(|fn_| ffi_fn::gen_definition(&fn_, None)); quote! { #item @@ -259,9 +262,8 @@ pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { } Item::Enum(item) => quote! { #item }, Item::Union(item) => quote! { #item }, - item => abort!(item, "Item not supported"), - } - .into() + item => bail!(item, "Item not supported"), + }) } /// Replace the function's body with a call to FFI function. Counterpart of [`ffi_export`] @@ -290,17 +292,17 @@ pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { /// fn __return_first_elem_from_arr(arr: *const [u8; 8]) -> u8; /// } */ /// ``` +#[manyhow] #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] -pub fn ffi_import(attr: TokenStream, item: TokenStream) -> TokenStream { - match parse_macro_input!(item) { +pub fn ffi_import(attr: TokenStream, item: TokenStream) -> Result { + Ok(match syn::parse2(item)? { Item::Impl(item) => { if !attr.is_empty() { - abort!(item, "Unknown tokens in the attribute"); + bail!(item, "Unknown tokens in the attribute"); } let attrs = &item.attrs; - let impl_desc = ImplDescriptor::from_impl(&item); + let impl_desc = ImplDescriptor::from_impl(&item)?; let wrapped_items = wrapper::wrap_impl_items(&impl_desc); let is_shared_fn = impl_desc @@ -331,7 +333,7 @@ pub fn ffi_import(attr: TokenStream, item: TokenStream) -> TokenStream { } } Item::Fn(item) => { - let fn_descriptor = FnDescriptor::from_fn(&item); + let fn_descriptor = FnDescriptor::from_fn(&item)?; let ffi_fn = ffi_fn::gen_declaration(&fn_descriptor, None); let wrapped_item = wrap_method(&fn_descriptor, None); @@ -343,9 +345,8 @@ pub fn ffi_import(attr: TokenStream, item: TokenStream) -> TokenStream { Item::Struct(item) => quote! { #item }, Item::Enum(item) => quote! { #item }, Item::Union(item) => quote! { #item }, - item => abort!(item, "Item not supported"), - } - .into() + item => bail!(item, "Item not supported"), + }) } fn is_opaque_struct(input: &syn::ItemStruct) -> bool { @@ -376,21 +377,21 @@ fn without_repr(repr: &[NestedMeta]) -> bool { repr.is_empty() } -fn is_repr_attr(repr: &[NestedMeta], name: &str) -> bool { - repr.iter().any(|meta| { +fn is_repr_attr(repr: &[NestedMeta], name: &str) -> Result { + for meta in repr { if let NestedMeta::Meta(item) = meta { match item { syn::Meta::Path(ref path) => { if path.is_ident(name) { - return true; + return Ok(true); } } - _ => abort!(item, "Unknown repr attribute"), + _ => bail!(item, "Unknown repr attribute"), } } + } - false - }) + Ok(false) } fn find_attr(attrs: &[syn::Attribute], name: &str) -> syn::AttributeArgs { diff --git a/ffi/derive/src/util.rs b/ffi/derive/src/util.rs index f502f7b9753..c00a8366ea6 100644 --- a/ffi/derive/src/util.rs +++ b/ffi/derive/src/util.rs @@ -1,7 +1,7 @@ use std::default::Default; +use manyhow::{bail, Result}; use proc_macro2::TokenStream; -use proc_macro_error::{abort, OptionExt}; use quote::quote; use rustc_hash::{FxHashMap, FxHashSet}; use syn::{parse_quote, visit::Visit, Fields, Ident}; @@ -21,27 +21,29 @@ pub fn gen_derived_methods<'a>( name: &Ident, attrs: &[syn::Attribute], fields: &'a syn::Fields, -) -> impl Iterator> { - let struct_derives = parse_derives(attrs).unwrap_or_default(); +) -> Result>> { + let struct_derives = parse_derives(attrs)?.unwrap_or_default(); let mut ffi_derives = FxHashMap::default(); match fields { - Fields::Named(syn::FieldsNamed { named, .. }) => named.iter().for_each(|field| { - if let Some(mut field_derives) = parse_derives(&field.attrs) { - field_derives.extend(struct_derives.clone()); - - for derive in field_derives { - let fn_ = gen_derived_method(name, field, derive); - ffi_derives.insert(fn_.sig.ident.clone(), fn_); + Fields::Named(syn::FieldsNamed { named, .. }) => { + for field in named.iter() { + if let Some(mut field_derives) = parse_derives(&field.attrs)? { + field_derives.extend(struct_derives.clone()); + + for derive in field_derives { + let fn_ = gen_derived_method(name, field, derive); + ffi_derives.insert(fn_.sig.ident.clone(), fn_); + } } } - }), + } Fields::Unnamed(_) | Fields::Unit => { - abort!(name, "Only named structs supported") + bail!(name, "Only named structs supported") } } - ffi_derives.into_values() + Ok(ffi_derives.into_values()) } pub fn gen_resolve_type(arg: &Arg) -> TokenStream { @@ -64,8 +66,10 @@ pub fn gen_resolve_type(arg: &Arg) -> TokenStream { } /// Parse `getset` attributes to find out which methods it derives -fn parse_derives(attrs: &[syn::Attribute]) -> Option> { - attrs +fn parse_derives(attrs: &[syn::Attribute]) -> Result>> { + let mut result = FxHashSet::default(); + + for nested in attrs .iter() .filter_map(|attr| { if let Ok(syn::Meta::List(meta_list)) = attr.parse_meta() { @@ -78,31 +82,31 @@ fn parse_derives(attrs: &[syn::Attribute]) -> Option> { None }) .flatten() - .try_fold(FxHashSet::default(), |mut acc, nested| { - if let syn::NestedMeta::Meta(item) = nested { - match item { - syn::Meta::NameValue(item) => { - if item.lit == parse_quote! {"pub"} { - if item.path.is_ident("set") { - acc.insert(Derive::Setter); - } else if item.path.is_ident("get") { - acc.insert(Derive::Getter); - } else if item.path.is_ident("get_mut") { - acc.insert(Derive::MutGetter); - } + { + if let syn::NestedMeta::Meta(item) = nested { + match item { + syn::Meta::NameValue(item) => { + if item.lit == parse_quote! {"pub"} { + if item.path.is_ident("set") { + result.insert(Derive::Setter); + } else if item.path.is_ident("get") { + result.insert(Derive::Getter); + } else if item.path.is_ident("get_mut") { + result.insert(Derive::MutGetter); } } - syn::Meta::Path(path) => { - if path.is_ident("skip") { - return None; - } + } + syn::Meta::Path(path) => { + if path.is_ident("skip") { + return Ok(None); } - _ => abort!(item, "Unsupported getset attribute"), } + _ => bail!(item, "Unsupported getset attribute"), } + } + } - Some(acc) - }) + Ok(Some(result)) } fn gen_derived_method<'ast>( @@ -111,7 +115,7 @@ fn gen_derived_method<'ast>( derive: Derive, ) -> FnDescriptor<'ast> { let handle_name = Ident::new("__handle", proc_macro2::Span::call_site()); - let field_name = field.ident.as_ref().expect_or_abort("Defined").clone(); + let field_name = field.ident.as_ref().expect("Defined").clone(); let sig = gen_derived_method_sig(field, derive); let self_ty = Some(parse_quote! {#item_name}); @@ -191,7 +195,7 @@ pub fn gen_store_name(arg_name: &Ident) -> Ident { struct FfiTypeResolver<'itm>(&'itm Ident, TokenStream); impl<'itm> Visit<'itm> for FfiTypeResolver<'itm> { fn visit_trait_bound(&mut self, i: &'itm syn::TraitBound) { - let trait_ = i.path.segments.last().expect_or_abort("Defined"); + let trait_ = i.path.segments.last().expect("Defined"); let arg_name = self.0; if trait_.ident == "IntoIterator" || trait_.ident == "ExactSizeIterator" { diff --git a/ffi/derive/src/wrapper.rs b/ffi/derive/src/wrapper.rs index 9fc634143d6..af8cbc9a752 100644 --- a/ffi/derive/src/wrapper.rs +++ b/ffi/derive/src/wrapper.rs @@ -1,5 +1,5 @@ +use manyhow::{bail, Result}; use proc_macro2::{Span, TokenStream}; -use proc_macro_error::abort; use quote::quote; use syn::{parse_quote, visit_mut::VisitMut, Ident, Type}; @@ -128,7 +128,7 @@ fn impl_ord_for_opaque(name: &Ident, generics: &syn::Generics) -> TokenStream { } } -fn gen_shared_fns(input: &syn::DeriveInput) -> Vec { +fn gen_shared_fns(input: &syn::DeriveInput) -> Result> { let name = &input.ident; let mut shared_fn_impls = Vec::new(); @@ -138,7 +138,7 @@ fn gen_shared_fns(input: &syn::DeriveInput) -> Vec { let first_segment = &path.segments.first().expect("Must have one segment").ident; if path.is_ident("Copy") { - abort!(path, "Opaque type should not implement `Copy` trait"); + bail!(path, "Opaque type should not implement `Copy` trait"); } else if path.is_ident("Clone") { shared_fn_impls.push(impl_clone_for_opaque(name, &input.generics)); } else if path.is_ident("Default") { @@ -158,7 +158,7 @@ fn gen_shared_fns(input: &syn::DeriveInput) -> Vec { { // NOTE: Already expanded } else { - abort!(path, "Unsupported derive for opaque type"); + bail!(path, "Unsupported derive for opaque type"); } } } else { @@ -166,7 +166,7 @@ fn gen_shared_fns(input: &syn::DeriveInput) -> Vec { } } - shared_fn_impls + Ok(shared_fn_impls) } fn wrapper_attributes(attrs: Vec) -> Vec { @@ -194,7 +194,7 @@ fn wrapper_attributes(attrs: Vec) -> Vec { pruned } -pub fn wrap_as_opaque(mut input: syn::DeriveInput) -> TokenStream { +pub fn wrap_as_opaque(mut input: syn::DeriveInput) -> Result { let name = &input.ident; let vis = &input.vis; @@ -225,10 +225,10 @@ pub fn wrap_as_opaque(mut input: syn::DeriveInput) -> TokenStream { let ref_inner = quote!(*const iroha_ffi::Extern); let ref_mut_inner = quote!(*mut iroha_ffi::Extern); - let shared_fns = gen_shared_fns(&input); + let shared_fns = gen_shared_fns(&input)?; let attrs = wrapper_attributes(input.attrs); - quote! { + Ok(quote! { #(#attrs)* #[repr(transparent)] #vis struct #name #ty_generics(*mut iroha_ffi::Extern #(#phantom_data_type_defs)*) #handle_bounded_where_clause; @@ -291,7 +291,7 @@ pub fn wrap_as_opaque(mut input: syn::DeriveInput) -> TokenStream { #(#shared_fns)* #impl_ffi - } + }) } #[allow(clippy::too_many_lines)]