git.delta.rocks / unique-network / refs/commits / 3ea185a77d36

difftreelog

misk: improve error report

Trubnikov Sergey2022-11-30parent: #8adc410.patch.diff
in: master

1 file changed

modifiedcrates/evm-coder/procedural/src/abi_derive.rsdiffbeforeafterboth
before · crates/evm-coder/procedural/src/abi_derive.rs
1use quote::quote;23pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {4	let name = &ast.ident;5	match &ast.data {6		syn::Data::Struct(ds) => expand_struct(ds, ast),7		syn::Data::Enum(de) => expand_enum(de, ast),8		syn::Data::Union(_) => Err(syn::Error::new(name.span(), "Unions not supported")),9	}10}1112fn expand_struct(13	ds: &syn::DataStruct,14	ast: &syn::DeriveInput,15) -> syn::Result<proc_macro2::TokenStream> {16	let name = &ast.ident;17	let docs = extract_docs(&ast.attrs, true)?;18	let (is_named_fields, field_names, field_types, field_docs, params_count) = match ds.fields {19		syn::Fields::Named(ref fields) => Ok((20			true,21			fields.named.iter().enumerate().map(map_field_to_name),22			fields.named.iter().map(map_field_to_type),23			fields.named.iter().map(map_field_to_doc),24			fields.named.len(),25		)),26		syn::Fields::Unnamed(ref fields) => Ok((27			false,28			fields.unnamed.iter().enumerate().map(map_field_to_name),29			fields.unnamed.iter().map(map_field_to_type),30			fields.unnamed.iter().map(map_field_to_doc),31			fields.unnamed.len(),32		)),33		syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")),34	}?;3536	if params_count == 0 {37		return Err(syn::Error::new(name.span(), "Empty structs not supported"));38	};3940	let tuple_type = tuple_type(field_types.clone());41	let tuple_ref_type = tuple_ref_type(field_types.clone());42	let tuple_data = tuple_data_as_ref(is_named_fields, field_names.clone());43	let tuple_names = tuple_names(is_named_fields, field_names.clone());44	let struct_from_tuple = struct_from_tuple(name, is_named_fields, field_names.clone());4546	let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);47	let abi_type = impl_struct_abi_type(name, tuple_type.clone());48	let abi_read = impl_struct_abi_read(name, tuple_type, tuple_names, struct_from_tuple);49	let abi_write = impl_struct_abi_write(name, is_named_fields, tuple_ref_type, tuple_data);50	let solidity_type = impl_struct_solidity_type(name, field_types.clone(), params_count);51	let solidity_type_name =52		impl_struct_solidity_type_name(name, field_types.clone(), params_count);53	let solidity_struct_collect =54		impl_struct_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?;5556	Ok(quote! {57		#can_be_plcaed_in_vec58		#abi_type59		#abi_read60		#abi_write61		#solidity_type62		#solidity_type_name63		#solidity_struct_collect64	})65}6667fn expand_enum(68	de: &syn::DataEnum,69	ast: &syn::DeriveInput,70) -> syn::Result<proc_macro2::TokenStream> {71	let name = &ast.ident;72	check_repr_u8(name, &ast.attrs)?;73	let docs = extract_docs(&ast.attrs, true)?;74	let option_count = check_and_count_option(de)?;75	let enum_options = de.variants.iter().map(|v| &v.ident);76	let enum_options_docs = de.variants.iter().map(|v| extract_docs(&v.attrs, false));7778	let from = impl_enum_from_u8(name, enum_options.clone());79	let solidity_option = impl_solidity_option(name, enum_options.clone());80	let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);81	let abi_type = impl_enum_abi_type(name);82	let abi_read = impl_enum_abi_read(name);83	let abi_write = impl_enum_abi_write(name);84	let solidity_type_name = impl_enum_solidity_type_name(name);85	let solidity_struct_collect = impl_enum_solidity_struct_collect(86		name,87		enum_options,88		option_count,89		enum_options_docs,90		&docs,91	);9293	let tt = quote! {94		#from95		#solidity_option96		#can_be_plcaed_in_vec97		#abi_type98		#abi_read99		#abi_write100		#solidity_type_name101		#solidity_struct_collect102	};103104	println!(105		"!!!!!!!!!!!!!!!!!!!!!!!!!\n{}\n!!!!!!!!!!!!!!!!!!!!!!!!!",106		tt107	);108109	Ok(tt)110}111112fn impl_solidity_option<'a>(113	name: &proc_macro2::Ident,114	enum_options: impl Iterator<Item = &'a syn::Ident>,115) -> proc_macro2::TokenStream {116	let enum_options = enum_options.map(|opt| {117		let s = name.to_string() + "." + opt.to_string().as_str();118		let as_string = proc_macro2::Literal::string(s.as_str());119		quote!(#name::#opt => #as_string,)120	});121	quote!(122		#[cfg(feature = "stubgen")]123		impl ::evm_coder::solidity::SolidityEnum for #name {124			fn solidity_option(&self) -> &str {125				match <#name>::default() {126					#(#enum_options)*127				}128			}129		}130	)131}132133fn impl_enum_from_u8<'a>(134	name: &proc_macro2::Ident,135	enum_options: impl Iterator<Item = &'a syn::Ident>,136) -> proc_macro2::TokenStream {137	let enum_options = enum_options.enumerate().map(|(i, opt)| {138		let n = proc_macro2::Literal::u8_suffixed(i as u8);139		quote! {#n => Ok(#name::#opt),}140	});141	quote!(142		impl TryFrom<u8> for #name {143			type Error = &'static str;144145			fn try_from(value: u8) -> ::core::result::Result<Self, Self::Error> {146				const err: &'static str = "Not convertible";147				match value {148					#(#enum_options)*149					_ => Err(err)150				}151			}152		}153	)154}155156fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream {157	quote! {158		impl ::evm_coder::abi::AbiType for #name {159			const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <u8 as ::evm_coder::abi::AbiType>::SIGNATURE;160161			fn is_dynamic() -> bool {162				<u8 as ::evm_coder::abi::AbiType>::is_dynamic()163			}164			fn size() -> usize {165				<u8 as ::evm_coder::abi::AbiType>::size()166			}167		}168	}169}170171fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream {172	quote!(173		impl ::evm_coder::abi::AbiRead for #name {174			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {175				Ok(176					<u8 as ::evm_coder::abi::AbiRead>::abi_read(reader)?177						.try_into()?178				)179			}180		}181	)182}183184fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream {185	quote!(186		impl ::evm_coder::abi::AbiWrite for #name {187			fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {188				::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer);189			}190		}191	)192}193194fn impl_enum_solidity_type_name<'a>(name: &syn::Ident) -> proc_macro2::TokenStream {195	quote!(196		#[cfg(feature = "stubgen")]197		impl ::evm_coder::solidity::SolidityTypeName for #name {198			fn solidity_name(199				writer: &mut impl ::core::fmt::Write,200				tc: &::evm_coder::solidity::TypeCollector,201			) -> ::core::fmt::Result {202				write!(writer, "{}", tc.collect_struct::<Self>())203			}204205			fn is_simple() -> bool {206				true207			}208209			fn solidity_default(210				writer: &mut impl ::core::fmt::Write,211				tc: &::evm_coder::solidity::TypeCollector,212			) -> ::core::fmt::Result {213				write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnum>::solidity_option(&<#name>::default()))214			}215		}216	)217}218219fn impl_enum_solidity_struct_collect<'a>(220	name: &syn::Ident,221	enum_options: impl Iterator<Item = &'a syn::Ident>,222	option_count: usize,223	enum_options_docs: impl Iterator<Item = syn::Result<Vec<proc_macro2::TokenStream>>>,224	docs: &[proc_macro2::TokenStream],225) -> proc_macro2::TokenStream {226	let string_name = name.to_string();227	let enum_options = enum_options228		.zip(enum_options_docs)229		.enumerate()230		.map(|(i, (opt, doc))| {231			let opt = proc_macro2::Literal::string(opt.to_string().as_str());232			let doc = doc.expect("Doc parsing error");233			let comma = if i != option_count - 1 { "," } else { "" };234			quote! {235				#(#doc)*236				writeln!(str, "\t{}{}", #opt, #comma).expect("Enum format option");237			}238		});239240	quote!(241		#[cfg(feature = "stubgen")]242		impl ::evm_coder::solidity::StructCollect for #name {243			fn name() -> String {244				#string_name.into()245			}246247			fn declaration() -> String {248				use std::fmt::Write;249250				let mut str = String::new();251				#(#docs)*252				writeln!(str, "enum {} {{", <Self as ::evm_coder::solidity::StructCollect>::name()).unwrap();253				#(#enum_options)*254				writeln!(str, "}}").unwrap();255				str256			}257		}258	)259}260261fn check_and_count_option(de: &syn::DataEnum) -> syn::Result<usize> {262	let mut count = 0;263	for error in de.variants.iter().filter_map(|v| {264		if !v.fields.is_empty() {265			Some(Err(syn::Error::new(266				v.ident.span(),267				"Enumeration parameters should not have fields",268			)))269		} else if v.discriminant.is_some() {270			Some(Err(syn::Error::new(271				v.ident.span(),272				"Enumeration options should not have an explicit specified value",273			)))274		} else {275			count += 1;276			None277		}278	}) {279		return error;280	}281282	Ok(count)283}284285fn check_repr_u8(name: &syn::Ident, attrs: &Vec<syn::Attribute>) -> syn::Result<()> {286	let mut has_repr = false;287	for error in attrs.iter().filter_map(|attr| {288		if let Some(ps) = attr.path.segments.first() {289			if ps.ident == "repr" {290				has_repr = true;291				let meta = match attr.parse_meta() {292					Ok(meta) => meta,293					Err(e) => return Some(Err(e)),294				};295				match meta {296					syn::Meta::List(p) => {297						for error in p.nested.iter().filter_map(|nm| match nm {298							syn::NestedMeta::Meta(m) => match m {299								syn::Meta::Path(p) => {300									for i in p.segments.iter().filter_map(|ps| {301										if ps.ident != "u8" {302											Some(Err(syn::Error::new(303												ps.ident.span(),304												"Enum is not \"repr(u8)\"",305											)))306										} else {307											None308										}309									}) {310										return Some(i);311									}312									None313								}314								_ => None,315							},316							_ => None,317						}) {318							return Some(error);319						}320						None::<syn::Result<()>>321					}322					_ => None,323				};324			}325		}326		None327	}) {328		return error;329	}330331	if !has_repr {332		return Err(syn::Error::new(name.span(), "Enum is not \"repr(u8)\""));333	}334335	Ok(())336}337338fn tuple_type<'a>(339	field_types: impl Iterator<Item = &'a syn::Type> + Clone,340) -> proc_macro2::TokenStream {341	let field_types = field_types.map(|ty| quote!(#ty,));342	quote! {(#(#field_types)*)}343}344345fn tuple_ref_type<'a>(346	field_types: impl Iterator<Item = &'a syn::Type> + Clone,347) -> proc_macro2::TokenStream {348	let field_types = field_types.map(|ty| quote!(&#ty,));349	quote! {(#(#field_types)*)}350}351352fn tuple_data_as_ref(353	is_named_fields: bool,354	field_names: impl Iterator<Item = syn::Ident> + Clone,355) -> proc_macro2::TokenStream {356	let field_names = field_names.enumerate().map(|(i, field)| {357		if is_named_fields {358			quote!(&self.#field,)359		} else {360			let field = proc_macro2::Literal::usize_unsuffixed(i);361			quote!(&self.#field,)362		}363	});364	quote! {(#(#field_names)*)}365}366367fn tuple_names(368	is_named_fields: bool,369	field_names: impl Iterator<Item = syn::Ident> + Clone,370) -> proc_macro2::TokenStream {371	let field_names = field_names.enumerate().map(|(i, field)| {372		if is_named_fields {373			quote!(#field,)374		} else {375			let field = proc_macro2::Ident::new(376				format!("field{}", i).as_str(),377				proc_macro2::Span::call_site(),378			);379			quote!(#field,)380		}381	});382	quote! {(#(#field_names)*)}383}384385fn struct_from_tuple(386	name: &syn::Ident,387	is_named_fields: bool,388	field_names: impl Iterator<Item = syn::Ident> + Clone,389) -> proc_macro2::TokenStream {390	let field_names = field_names.enumerate().map(|(i, field)| {391		if is_named_fields {392			quote!(#field,)393		} else {394			let field = proc_macro2::Ident::new(395				format!("field{}", i).as_str(),396				proc_macro2::Span::call_site(),397			);398			quote!(#field,)399		}400	});401402	if is_named_fields {403		quote! {#name {#(#field_names)*}}404	} else {405		quote! {#name (#(#field_names)*)}406	}407}408409fn extract_docs(410	attrs: &[syn::Attribute],411	is_general: bool,412) -> syn::Result<Vec<proc_macro2::TokenStream>> {413	attrs414		.iter()415		.filter_map(|attr| {416			if let Some(ps) = attr.path.segments.first() {417				if ps.ident == "doc" {418					let meta = match attr.parse_meta() {419						Ok(meta) => meta,420						Err(e) => return Some(Err(e)),421					};422					match meta {423						syn::Meta::NameValue(mnv) => match &mnv.lit {424							syn::Lit::Str(ls) => return Some(Ok(ls.value())),425							_ => unreachable!(),426						},427						_ => unreachable!(),428					}429				}430			}431			None432		})433		.enumerate()434		.map(|(i, doc)| {435			let doc = doc?;436			let doc = doc.trim();437			let dev = if i == 0 { " @dev" } else { "" };438			let tab = if is_general { "" } else { "\t" };439			Ok(quote! {440				writeln!(str, "{}///{} {}", #tab, #dev, #doc).unwrap();441			})442		})443		.collect()444}445446fn map_field_to_name(field: (usize, &syn::Field)) -> syn::Ident {447	match field.1.ident.as_ref() {448		Some(name) => name.clone(),449		None => {450			let mut name = "field".to_string();451			name.push_str(field.0.to_string().as_str());452			syn::Ident::new(name.as_str(), proc_macro2::Span::call_site())453		}454	}455}456457fn map_field_to_type(field: &syn::Field) -> &syn::Type {458	&field.ty459}460461fn map_field_to_doc(field: &syn::Field) -> syn::Result<Vec<proc_macro2::TokenStream>> {462	extract_docs(&field.attrs, false)463}464465fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream {466	quote! {467		impl ::evm_coder::sealed::CanBePlacedInVec for #ident {}468	}469}470471fn impl_struct_abi_type(472	name: &syn::Ident,473	tuple_type: proc_macro2::TokenStream,474) -> proc_macro2::TokenStream {475	quote! {476		impl ::evm_coder::abi::AbiType for #name {477			const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <#tuple_type as ::evm_coder::abi::AbiType>::SIGNATURE;478			fn is_dynamic() -> bool {479				<#tuple_type as ::evm_coder::abi::AbiType>::is_dynamic()480			}481			fn size() -> usize {482				<#tuple_type as ::evm_coder::abi::AbiType>::size()483			}484		}485	}486}487488fn impl_struct_abi_read(489	name: &syn::Ident,490	tuple_type: proc_macro2::TokenStream,491	tuple_names: proc_macro2::TokenStream,492	struct_from_tuple: proc_macro2::TokenStream,493) -> proc_macro2::TokenStream {494	quote!(495		impl ::evm_coder::abi::AbiRead for #name {496			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {497				let #tuple_names = <#tuple_type as ::evm_coder::abi::AbiRead>::abi_read(reader)?;498				Ok(#struct_from_tuple)499			}500		}501	)502}503504fn impl_struct_abi_write(505	name: &syn::Ident,506	_is_named_fields: bool,507	tuple_type: proc_macro2::TokenStream,508	tuple_data: proc_macro2::TokenStream,509) -> proc_macro2::TokenStream {510	quote!(511		impl ::evm_coder::abi::AbiWrite for #name {512			fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {513				<#tuple_type as ::evm_coder::abi::AbiWrite>::abi_write(&#tuple_data, writer)514			}515		}516	)517}518519fn impl_struct_solidity_type<'a>(520	name: &syn::Ident,521	field_types: impl Iterator<Item = &'a syn::Type> + Clone,522	params_count: usize,523) -> proc_macro2::TokenStream {524	let len = proc_macro2::Literal::usize_suffixed(params_count);525	quote! {526		#[cfg(feature = "stubgen")]527		impl ::evm_coder::solidity::SolidityType for #name {528			fn names(tc: &::evm_coder::solidity::TypeCollector) -> Vec<String> {529				let mut collected =530					Vec::with_capacity(<Self as ::evm_coder::solidity::SolidityType>::len());531				#({532					let mut out = String::new();533					<#field_types as ::evm_coder::solidity::SolidityTypeName>::solidity_name(&mut out, tc)534						.expect("no fmt error");535					collected.push(out);536				})*537				collected538			}539540			fn len() -> usize {541				#len542			}543		}544	}545}546547fn impl_struct_solidity_type_name<'a>(548	name: &syn::Ident,549	field_types: impl Iterator<Item = &'a syn::Type> + Clone,550	params_count: usize,551) -> proc_macro2::TokenStream {552	let arg_dafaults = field_types.enumerate().map(|(i, ty)| {553		let mut defult_value = quote!(<#ty as ::evm_coder::solidity::SolidityTypeName554			>::solidity_default(writer, tc)?;);555		let last_item = params_count - 1;556		if i != last_item {557			defult_value.extend(quote! {write!(writer, ",")?;})558		}559		defult_value560	});561562	quote! {563		#[cfg(feature = "stubgen")]564		impl ::evm_coder::solidity::SolidityTypeName for #name {565			fn solidity_name(566				writer: &mut impl ::core::fmt::Write,567				tc: &::evm_coder::solidity::TypeCollector,568			) -> ::core::fmt::Result {569				write!(writer, "{}", tc.collect_struct::<Self>())570			}571572			fn is_simple() -> bool {573				false574			}575576			fn solidity_default(577				writer: &mut impl ::core::fmt::Write,578				tc: &::evm_coder::solidity::TypeCollector,579			) -> ::core::fmt::Result {580				write!(writer, "{}(", tc.collect_struct::<Self>())?;581582				#(#arg_dafaults)*583584				write!(writer, ")")585			}586		}587	}588}589590fn impl_struct_solidity_struct_collect<'a>(591	name: &syn::Ident,592	field_names: impl Iterator<Item = proc_macro2::Ident> + Clone,593	field_types: impl Iterator<Item = &'a syn::Type> + Clone,594	field_docs: impl Iterator<Item = syn::Result<Vec<proc_macro2::TokenStream>>> + Clone,595	docs: &[proc_macro2::TokenStream],596) -> syn::Result<proc_macro2::TokenStream> {597	let string_name = name.to_string();598	let name_type = field_names599		.into_iter()600		.zip(field_types)601		.zip(field_docs)602		.map(|((name, ty), doc)| {603			let field_docs = doc.expect("Doc parse error");604			let name = format!("{}", name);605			quote!(606				#(#field_docs)*607				write!(str, "\t{} ", <#ty as ::evm_coder::solidity::StructCollect>::name()).unwrap();608				writeln!(str, "{};", #name).unwrap();609			)610		});611612	Ok(quote! {613		#[cfg(feature = "stubgen")]614		impl ::evm_coder::solidity::StructCollect for #name {615			fn name() -> String {616				#string_name.into()617			}618619			fn declaration() -> String {620				use std::fmt::Write;621622				let mut str = String::new();623				#(#docs)*624				writeln!(str, "struct {} {{", Self::name()).unwrap();625				#(#name_type)*626				writeln!(str, "}}").unwrap();627				str628			}629		}630	})631}