fix(contract): report all errors during parsing (#2149)
* refactor: use Result<_, Error> * fix: report both errors during parsing
This commit is contained in:
parent
adefb6c2f6
commit
fefaf7433f
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue