difftreelog
Merge pull request #806 from UniqueNetwork/fix/macro_check_enum_fields
in: master
2 files changed
crates/evm-coder/procedural/src/abi_derive/derive_enum.rsdiffbeforeafterboth1use 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}crates/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();