fix(contract): report all errors during parsing (#2149)

* refactor: use Result<_, Error>

* fix: report both errors during parsing
This commit is contained in:
DaniPopes 2023-02-14 02:08:46 +01:00 committed by GitHub
parent adefb6c2f6
commit fefaf7433f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 83 deletions

View File

@ -17,77 +17,72 @@ use hex::FromHex;
use crate::{abi_ty, utils}; use crate::{abi_ty, utils};
/// Generates the `EthEvent` trait support /// Generates the `EthEvent` trait support
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream { pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
// the ethers crates to use
let core_crate = ethers_core_crate();
let contract_crate = ethers_contract_crate();
let name = &input.ident; let name = &input.ident;
let attributes = match parse_event_attributes(&input) { let attributes = parse_event_attributes(&input)?;
Ok(attributes) => attributes,
Err(errors) => return errors,
};
let event_name = attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string()); let event_name = attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
let mut event = if let Some((src, span)) = attributes.abi { let mut event = if let Some((src, span)) = attributes.abi {
// try to parse as solidity event // try to parse as a Solidity event
if let Ok(event) = HumanReadableParser::parse_event(&src) { match HumanReadableParser::parse_event(&src) {
event Ok(event) => Ok(event),
} else { Err(parse_err) => {
match src.parse::<Source>().and_then(|s| s.get()) { match src.parse::<Source>().and_then(|s| s.get()) {
Ok(abi) => { Ok(abi) => {
// try to derive the signature from the abi from the parsed abi // try to derive the signature from the abi from the parsed abi
// TODO(mattsse): this will fail for events that contain other non // TODO(mattsse): this will fail for events that contain other non
// elementary types in their abi because the parser // elementary types in their abi because the parser
// doesn't know how to substitute the types // doesn't know how to substitute the types.
// this could be mitigated by getting the ABI of each non elementary type // This could be mitigated by getting the ABI of each non elementary type
// at runtime and computing the the signature as // at runtime and computing the the signature as a Lazy static.
// `static Lazy::...`
match HumanReadableParser::parse_event(&abi) { match HumanReadableParser::parse_event(&abi) {
Ok(event) => event, Ok(event) => Ok(event),
Err(err) => return Error::new(span, err).to_compile_error(), // Ignore parse_err since this is a valid [Source]
Err(err) => Err(Error::new(span, err)),
}
}
Err(source_err) => {
// Return both error messages
let message = format!("Failed parsing ABI: {parse_err} ({source_err})");
Err(Error::new(span, message))
} }
} }
Err(err) => return Error::new(span, err).to_compile_error(),
} }
} }
} else { } else {
// try to determine the abi from the fields // try to determine the abi from the fields
match derive_abi_event_from_fields(&input) { derive_abi_event_from_fields(&input)
Ok(event) => event, }?;
Err(err) => return err.to_compile_error(),
}
};
event.name = event_name.clone(); event.name = event_name.clone();
if let Some((anon, _)) = attributes.anonymous.as_ref() { if let Some((anon, _)) = attributes.anonymous.as_ref() {
event.anonymous = *anon; event.anonymous = *anon;
} }
let decode_log_impl = match derive_decode_from_log_impl(&input, &event) { let decode_log_impl = derive_decode_from_log_impl(&input, &event)?;
Ok(log) => log,
Err(err) => return err.to_compile_error(),
};
let (abi, hash) = (event.abi_signature(), event.signature()); let (abi, event_sig) = (event.abi_signature(), event.signature());
let signature = if let Some((hash, _)) = attributes.signature_hash { let signature = if let Some((hash, _)) = attributes.signature_hash {
utils::signature(&hash) utils::signature(&hash)
} else { } else {
utils::signature(hash.as_bytes()) utils::signature(event_sig.as_bytes())
}; };
let anon = attributes.anonymous.map(|(b, _)| b).unwrap_or_default(); let anon = attributes.anonymous.map(|(b, _)| b).unwrap_or_default();
let ethers_core = ethers_core_crate();
let ethers_contract = ethers_contract_crate();
let ethevent_impl = quote! { let ethevent_impl = quote! {
impl #contract_crate::EthEvent for #name { impl #ethers_contract::EthEvent for #name {
fn name() -> ::std::borrow::Cow<'static, str> { fn name() -> ::std::borrow::Cow<'static, str> {
#event_name.into() #event_name.into()
} }
fn signature() -> #core_crate::types::H256 { fn signature() -> #ethers_core::types::H256 {
#signature #signature
} }
@ -95,7 +90,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
#abi.into() #abi.into()
} }
fn decode_log(log: &#core_crate::abi::RawLog) -> ::std::result::Result<Self, #core_crate::abi::Error> where Self: Sized { fn decode_log(log: &#ethers_core::abi::RawLog) -> ::std::result::Result<Self, #ethers_core::abi::Error> where Self: Sized {
#decode_log_impl #decode_log_impl
} }
@ -107,10 +102,10 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
let tokenize_impl = abi_ty::derive_tokenizeable_impl(&input); let tokenize_impl = abi_ty::derive_tokenizeable_impl(&input);
quote! { Ok(quote! {
#tokenize_impl #tokenize_impl
#ethevent_impl #ethevent_impl
} })
} }
/// Internal helper type for an event/log /// Internal helper type for an event/log
@ -130,7 +125,7 @@ fn derive_decode_from_log_impl(
input: &DeriveInput, input: &DeriveInput,
event: &Event, event: &Event,
) -> Result<proc_macro2::TokenStream, Error> { ) -> Result<proc_macro2::TokenStream, Error> {
let core_crate = ethers_core_crate(); let ethers_core = ethers_core_crate();
let fields: Vec<_> = match input.data { let fields: Vec<_> = match input.data {
Data::Struct(ref data) => match data.fields { Data::Struct(ref data) => match data.fields {
@ -213,16 +208,16 @@ fn derive_decode_from_log_impl(
}, },
quote! { quote! {
if topic_tokens.len() != topics.len() { if topic_tokens.len() != topics.len() {
return Err(#core_crate::abi::Error::InvalidData); return Err(#ethers_core::abi::Error::InvalidData);
} }
}, },
) )
} else { } else {
( (
quote! { quote! {
let event_signature = topics.get(0).ok_or(#core_crate::abi::Error::InvalidData)?; let event_signature = topics.get(0).ok_or(#ethers_core::abi::Error::InvalidData)?;
if event_signature != &Self::signature() { if event_signature != &Self::signature() {
return Err(#core_crate::abi::Error::InvalidData); return Err(#ethers_core::abi::Error::InvalidData);
} }
}, },
quote! { quote! {
@ -230,7 +225,7 @@ fn derive_decode_from_log_impl(
}, },
quote! { quote! {
if topic_tokens.len() != topics.len() - 1 { if topic_tokens.len() != topics.len() - 1 {
return Err(#core_crate::abi::Error::InvalidData); return Err(#ethers_core::abi::Error::InvalidData);
} }
}, },
) )
@ -244,9 +239,9 @@ fn derive_decode_from_log_impl(
.all(|(idx, f)| f.index == idx) .all(|(idx, f)| f.index == idx)
{ {
quote! { quote! {
let topic_tokens = #core_crate::abi::decode(&topic_types, &flat_topics)?; let topic_tokens = #ethers_core::abi::decode(&topic_types, &flat_topics)?;
#topic_tokens_len_check #topic_tokens_len_check
let data_tokens = #core_crate::abi::decode(&data_types, data)?; let data_tokens = #ethers_core::abi::decode(&data_types, data)?;
let tokens:Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect(); let tokens:Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect();
} }
} else { } else {
@ -259,16 +254,16 @@ fn derive_decode_from_log_impl(
}); });
quote! { quote! {
let mut topic_tokens = #core_crate::abi::decode(&topic_types, &flat_topics)?; let mut topic_tokens = #ethers_core::abi::decode(&topic_types, &flat_topics)?;
#topic_tokens_len_check #topic_tokens_len_check
let mut data_tokens = #core_crate::abi::decode(&data_types, &data)?; let mut data_tokens = #ethers_core::abi::decode(&data_types, &data)?;
let mut tokens = Vec::with_capacity(topics.len() + data_tokens.len()); let mut tokens = Vec::with_capacity(topics.len() + data_tokens.len());
#( tokens.push(#swap_tokens); )* #( tokens.push(#swap_tokens); )*
} }
}; };
Ok(quote! { Ok(quote! {
let #core_crate::abi::RawLog {data, topics} = log; let #ethers_core::abi::RawLog {data, topics} = log;
#signature_check #signature_check
@ -279,7 +274,7 @@ fn derive_decode_from_log_impl(
#tokens_init #tokens_init
#core_crate::abi::Tokenizable::from_token(#core_crate::abi::Token::Tuple(tokens)).map_err(|_|#core_crate::abi::Error::InvalidData) #ethers_core::abi::Tokenizable::from_token(#ethers_core::abi::Token::Tuple(tokens)).map_err(|_|#ethers_core::abi::Error::InvalidData)
}) })
} }
@ -355,9 +350,7 @@ struct EthEventAttributes {
} }
/// extracts the attributes from the struct annotated with `EthEvent` /// extracts the attributes from the struct annotated with `EthEvent`
fn parse_event_attributes( fn parse_event_attributes(input: &DeriveInput) -> Result<EthEventAttributes, Error> {
input: &DeriveInput,
) -> Result<EthEventAttributes, proc_macro2::TokenStream> {
let mut result = EthEventAttributes::default(); let mut result = EthEventAttributes::default();
for a in input.attrs.iter() { for a in input.attrs.iter() {
if let AttrStyle::Outer = a.style { if let AttrStyle::Outer = a.style {
@ -376,23 +369,20 @@ fn parse_event_attributes(
return Err(Error::new( return Err(Error::new(
name.span(), name.span(),
"anonymous already specified", "anonymous already specified",
) ))
.to_compile_error())
} }
} }
} }
return Err(Error::new( return Err(Error::new(
path.span(), path.span(),
"unrecognized ethevent parameter", "unrecognized ethevent parameter",
) ))
.to_compile_error())
} }
Meta::List(meta) => { Meta::List(meta) => {
return Err(Error::new( return Err(Error::new(
meta.path.span(), meta.path.span(),
"unrecognized ethevent parameter", "unrecognized ethevent parameter",
) ))
.to_compile_error())
} }
Meta::NameValue(meta) => { Meta::NameValue(meta) => {
if meta.path.is_ident("anonymous") { if meta.path.is_ident("anonymous") {
@ -404,15 +394,13 @@ fn parse_event_attributes(
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"anonymous already specified", "anonymous already specified",
) ))
.to_compile_error())
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"name must be a string", "name must be a string",
) ))
.to_compile_error())
} }
} else if meta.path.is_ident("name") { } else if meta.path.is_ident("name") {
if let Lit::Str(ref lit_str) = meta.lit { if let Lit::Str(ref lit_str) = meta.lit {
@ -423,15 +411,13 @@ fn parse_event_attributes(
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"name already specified", "name already specified",
) ))
.to_compile_error())
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"name must be a string", "name must be a string",
) ))
.to_compile_error())
} }
} else if meta.path.is_ident("abi") { } else if meta.path.is_ident("abi") {
if let Lit::Str(ref lit_str) = meta.lit { if let Lit::Str(ref lit_str) = meta.lit {
@ -442,15 +428,13 @@ fn parse_event_attributes(
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"abi already specified", "abi already specified",
) ))
.to_compile_error())
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"abi must be a string", "abi must be a string",
) ))
.to_compile_error())
} }
} else if meta.path.is_ident("signature") { } else if meta.path.is_ident("signature") {
if let Lit::Str(ref lit_str) = meta.lit { if let Lit::Str(ref lit_str) = meta.lit {
@ -466,30 +450,26 @@ fn parse_event_attributes(
format!( format!(
"Expected hex signature: {err:?}" "Expected hex signature: {err:?}"
), ),
) ))
.to_compile_error())
} }
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"signature already specified", "signature already specified",
) ))
.to_compile_error())
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"signature must be a hex string", "signature must be a hex string",
) ))
.to_compile_error())
} }
} else { } else {
return Err(Error::new( return Err(Error::new(
meta.span(), meta.span(),
"unrecognized ethevent parameter", "unrecognized ethevent parameter",
) ))
.to_compile_error())
} }
} }
} }

View File

@ -217,7 +217,10 @@ pub fn derive_eth_display(input: TokenStream) -> TokenStream {
#[proc_macro_derive(EthEvent, attributes(ethevent))] #[proc_macro_derive(EthEvent, attributes(ethevent))]
pub fn derive_abi_event(input: TokenStream) -> TokenStream { pub fn derive_abi_event(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
TokenStream::from(event::derive_eth_event_impl(input)) match event::derive_eth_event_impl(input) {
Ok(tokens) => TokenStream::from(tokens),
Err(err) => err.to_compile_error().into(),
}
} }
/// Derives the `EthCall` and `Tokenizeable` trait for the labeled type. /// Derives the `EthCall` and `Tokenizeable` trait for the labeled type.