git.delta.rocks / unique-network / refs/commits / aea07737fd8a

difftreelog

Merge pull request #806 from UniqueNetwork/fix/macro_check_enum_fields

Yaroslav Bolyukin2022-12-23parents: #d6d6d43 #3e69aff.patch.diff
in: master

2 files changed

modifiedcrates/evm-coder/procedural/src/abi_derive/derive_enum.rsdiffbeforeafterboth
before · crates/evm-coder/procedural/src/abi_derive/derive_enum.rs
1use quote::quote;23use super::extract_docs;45pub fn impl_solidity_option<'a>(6	docs: Vec<String>,7	name: &proc_macro2::Ident,8	enum_options: impl Iterator<Item = &'a syn::Variant> + Clone,9) -> proc_macro2::TokenStream {10	let variant_names = enum_options.clone().map(|opt| {11		let opt = &opt.ident;12		let s = name.to_string() + "." + opt.to_string().as_str();13		let as_string = proc_macro2::Literal::string(s.as_str());14		quote!(#name::#opt => #as_string,)15	});16	let solidity_name = name.to_string();1718	let solidity_fields = enum_options.map(|v| {19		let docs = extract_docs(&v.attrs).expect("TODO: handle bad docs");20		let name = v.ident.to_string();21		quote! {22			SolidityEnumVariant {23				docs: &[#(#docs),*],24				name: #name,25			}26		}27	});2829	quote!(30		#[cfg(feature = "stubgen")]31		impl ::evm_coder::solidity::SolidityEnumTy for #name {32			fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String {33				use evm_coder::solidity::*;34				use core::fmt::Write;35				let interface = SolidityEnum {36					docs: &[#(#docs),*],37					name: #solidity_name,38					fields: &[#(39						#solidity_fields,40					)*],41				};42				let mut out = String::new();43				let _ = interface.format(&mut out, tc);44				tc.collect(out);45				#solidity_name.to_string()46			}47			fn solidity_option(&self) -> &str {48				match self {49					#(#variant_names)*50				}51			}52		}53	)54}5556pub fn impl_enum_from_u8<'a>(57	name: &proc_macro2::Ident,58	enum_options: impl Iterator<Item = &'a syn::Variant>,59) -> proc_macro2::TokenStream {60	let error_str = format!("Value not convertible into enum \"{name}\"");61	let error_str = proc_macro2::Literal::string(&error_str);62	let enum_options = enum_options.enumerate().map(|(i, opt)| {63		let opt = &opt.ident;64		let n = proc_macro2::Literal::u8_suffixed(i as u8);65		quote! {#n => Ok(#name::#opt),}66	});6768	quote!(69		impl TryFrom<u8> for #name {70			type Error = &'static str;7172			fn try_from(value: u8) -> ::core::result::Result<Self, Self::Error> {73				const err: &'static str = #error_str;74				match value {75					#(#enum_options)*76					_ => Err(err)77				}78			}79		}80	)81}8283pub fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream {84	quote! {85		impl ::evm_coder::abi::AbiType for #name {86			const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <u8 as ::evm_coder::abi::AbiType>::SIGNATURE;8788			fn is_dynamic() -> bool {89				<u8 as ::evm_coder::abi::AbiType>::is_dynamic()90			}91			fn size() -> usize {92				<u8 as ::evm_coder::abi::AbiType>::size()93			}94		}95	}96}9798pub fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream {99	quote!(100		impl ::evm_coder::abi::AbiRead for #name {101			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {102				Ok(103					<u8 as ::evm_coder::abi::AbiRead>::abi_read(reader)?104						.try_into()?105				)106			}107		}108	)109}110111pub fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream {112	quote!(113		impl ::evm_coder::abi::AbiWrite for #name {114			fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {115				::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer);116			}117		}118	)119}120121pub fn impl_enum_solidity_type_name(name: &syn::Ident) -> proc_macro2::TokenStream {122	quote!(123		#[cfg(feature = "stubgen")]124		impl ::evm_coder::solidity::SolidityTypeName for #name {125			fn solidity_name(126				writer: &mut impl ::core::fmt::Write,127				tc: &::evm_coder::solidity::TypeCollector,128			) -> ::core::fmt::Result {129				write!(writer, "{}", tc.collect_enum::<Self>())130			}131132			fn is_simple() -> bool {133				true134			}135136			fn solidity_default(137				writer: &mut impl ::core::fmt::Write,138				tc: &::evm_coder::solidity::TypeCollector,139			) -> ::core::fmt::Result {140				write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnumTy>::solidity_option(&<#name>::default()))141			}142		}143	)144}145146pub fn check_and_count_options(de: &syn::DataEnum) -> syn::Result<usize> {147	let mut count = 0;148	for v in de.variants.iter() {149		if !v.fields.is_empty() {150			return Err(syn::Error::new(151				v.ident.span(),152				"Enumeration parameters should not have fields",153			));154		} else if v.discriminant.is_some() {155			return Err(syn::Error::new(156				v.ident.span(),157				"Enumeration options should not have an explicit specified value",158			));159		} else {160			count += 1;161		}162	}163164	Ok(count)165}166167pub fn check_repr_u8(name: &syn::Ident, attrs: &[syn::Attribute]) -> syn::Result<()> {168	let mut has_repr = false;169	for attr in attrs.iter() {170		if attr.path.is_ident("repr") {171			has_repr = true;172			let meta = attr.parse_meta()?;173			check_meta_u8(&meta)?;174		}175	}176177	if !has_repr {178		return Err(syn::Error::new(name.span(), "Enum is not \"repr(u8)\""));179	}180181	Ok(())182}183184fn check_meta_u8(meta: &syn::Meta) -> Result<(), syn::Error> {185	if let syn::Meta::List(p) = meta {186		for nm in p.nested.iter() {187			if let syn::NestedMeta::Meta(syn::Meta::Path(p)) = nm {188				if !p.is_ident("u8") {189					return Err(syn::Error::new(190						p.segments191							.first()192							.expect("repr segments are empty")193							.ident194							.span(),195						"Enum is not \"repr(u8)\"",196					));197				}198			}199		}200	}201	Ok(())202}
after · crates/evm-coder/procedural/src/abi_derive/derive_enum.rs
1use quote::quote;23use super::extract_docs;45pub fn impl_solidity_option<'a>(6	docs: Vec<String>,7	name: &proc_macro2::Ident,8	enum_options: impl Iterator<Item = &'a syn::Variant> + Clone,9) -> proc_macro2::TokenStream {10	let variant_names = enum_options.clone().map(|opt| {11		let opt = &opt.ident;12		let s = name.to_string() + "." + opt.to_string().as_str();13		let as_string = proc_macro2::Literal::string(s.as_str());14		quote!(#name::#opt => #as_string,)15	});16	let solidity_name = name.to_string();1718	let solidity_fields = enum_options.map(|v| {19		let docs = extract_docs(&v.attrs).expect("TODO: handle bad docs");20		let name = v.ident.to_string();21		quote! {22			SolidityEnumVariant {23				docs: &[#(#docs),*],24				name: #name,25			}26		}27	});2829	quote!(30		#[cfg(feature = "stubgen")]31		impl ::evm_coder::solidity::SolidityEnumTy for #name {32			fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String {33				use evm_coder::solidity::*;34				use core::fmt::Write;35				let interface = SolidityEnum {36					docs: &[#(#docs),*],37					name: #solidity_name,38					fields: &[#(39						#solidity_fields,40					)*],41				};42				let mut out = String::new();43				let _ = interface.format(&mut out, tc);44				tc.collect(out);45				#solidity_name.to_string()46			}47			fn solidity_option(&self) -> &str {48				match self {49					#(#variant_names)*50				}51			}52		}53	)54}5556pub fn impl_enum_from_u8<'a>(57	name: &proc_macro2::Ident,58	enum_options: impl Iterator<Item = &'a syn::Variant>,59) -> proc_macro2::TokenStream {60	let error_str = format!("Value not convertible into enum \"{name}\"");61	let error_str = proc_macro2::Literal::string(&error_str);62	let enum_options = enum_options.enumerate().map(|(i, opt)| {63		let opt = &opt.ident;64		let n = proc_macro2::Literal::u8_suffixed(i as u8);65		quote! {#n => Ok(#name::#opt),}66	});6768	quote!(69		impl TryFrom<u8> for #name {70			type Error = &'static str;7172			fn try_from(value: u8) -> ::core::result::Result<Self, Self::Error> {73				const err: &'static str = #error_str;74				match value {75					#(#enum_options)*76					_ => Err(err)77				}78			}79		}80	)81}8283pub fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream {84	quote! {85		impl ::evm_coder::abi::AbiType for #name {86			const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <u8 as ::evm_coder::abi::AbiType>::SIGNATURE;8788			fn is_dynamic() -> bool {89				<u8 as ::evm_coder::abi::AbiType>::is_dynamic()90			}91			fn size() -> usize {92				<u8 as ::evm_coder::abi::AbiType>::size()93			}94		}95	}96}9798pub fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream {99	quote!(100		impl ::evm_coder::abi::AbiRead for #name {101			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {102				Ok(103					<u8 as ::evm_coder::abi::AbiRead>::abi_read(reader)?104						.try_into()?105				)106			}107		}108	)109}110111pub fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream {112	quote!(113		impl ::evm_coder::abi::AbiWrite for #name {114			fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {115				::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer);116			}117		}118	)119}120121pub fn impl_enum_solidity_type_name(name: &syn::Ident) -> proc_macro2::TokenStream {122	quote!(123		#[cfg(feature = "stubgen")]124		impl ::evm_coder::solidity::SolidityTypeName for #name {125			fn solidity_name(126				writer: &mut impl ::core::fmt::Write,127				tc: &::evm_coder::solidity::TypeCollector,128			) -> ::core::fmt::Result {129				write!(writer, "{}", tc.collect_enum::<Self>())130			}131132			fn is_simple() -> bool {133				true134			}135136			fn solidity_default(137				writer: &mut impl ::core::fmt::Write,138				tc: &::evm_coder::solidity::TypeCollector,139			) -> ::core::fmt::Result {140				write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnumTy>::solidity_option(&<#name>::default()))141			}142		}143	)144}145146pub fn check_enum_fields(de: &syn::DataEnum) -> syn::Result<()> {147	for v in de.variants.iter() {148		if !v.fields.is_empty() {149			return Err(syn::Error::new(150				v.ident.span(),151				"Enumeration parameters should not have fields",152			));153		} else if v.discriminant.is_some() {154			return Err(syn::Error::new(155				v.ident.span(),156				"Enumeration options should not have an explicit specified value",157			));158		}159	}160161	Ok(())162}163164pub fn check_repr_u8(name: &syn::Ident, attrs: &[syn::Attribute]) -> syn::Result<()> {165	let mut has_repr = false;166	for attr in attrs.iter() {167		if attr.path.is_ident("repr") {168			has_repr = true;169			let meta = attr.parse_meta()?;170			check_meta_u8(&meta)?;171		}172	}173174	if !has_repr {175		return Err(syn::Error::new(name.span(), "Enum is not \"repr(u8)\""));176	}177178	Ok(())179}180181fn check_meta_u8(meta: &syn::Meta) -> Result<(), syn::Error> {182	if let syn::Meta::List(p) = meta {183		for nm in p.nested.iter() {184			if let syn::NestedMeta::Meta(syn::Meta::Path(p)) = nm {185				if !p.is_ident("u8") {186					return Err(syn::Error::new(187						p.segments188							.first()189							.expect("repr segments are empty")190							.ident191							.span(),192						"Enum is not \"repr(u8)\"",193					));194				}195			}196		}197	}198	Ok(())199}
modifiedcrates/evm-coder/procedural/src/abi_derive/mod.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/abi_derive/mod.rs
+++ b/crates/evm-coder/procedural/src/abi_derive/mod.rs
@@ -70,6 +70,7 @@
 ) -> syn::Result<proc_macro2::TokenStream> {
 	let name = &ast.ident;
 	check_repr_u8(name, &ast.attrs)?;
+	check_enum_fields(de)?;
 	let docs = extract_docs(&ast.attrs)?;
 	let enum_options = de.variants.iter();