diff --git a/crates/sol-macro-expander/src/expand/contract.rs b/crates/sol-macro-expander/src/expand/contract.rs index dd7f6e067..74c8569e3 100644 --- a/crates/sol-macro-expander/src/expand/contract.rs +++ b/crates/sol-macro-expander/src/expand/contract.rs @@ -579,6 +579,12 @@ struct ExpandData { min_data_len: usize, trait_: Ident, selectors: Vec>, + /// Whether the builtin `#[sol(all_derives)]` traits can be derived on the + /// generated enum. Computed from the underlying items' parameter types, + /// because the variant type names may be synthetic (overloaded items get + /// `_N` suffixes, call variants use `*Call` structs) and thus not + /// resolvable as items. + can_derive_builtin: bool, } impl ExpandData { @@ -645,6 +651,9 @@ impl ToExpand<'_> { .unwrap(), trait_: format_ident!("SolCall"), selectors: functions.iter().map(|f| cx.function_selector(f)).collect(), + can_derive_builtin: functions + .iter() + .all(|f| f.parameters.types().all(|ty| cx.can_derive_builtin_traits(ty))), } } @@ -659,6 +668,9 @@ impl ToExpand<'_> { .unwrap(), trait_: format_ident!("SolError"), selectors: errors.iter().map(|e| cx.error_selector(e)).collect(), + can_derive_builtin: errors.iter().all(|&error| { + error.parameters.types().all(|ty| cx.can_derive_builtin_traits(ty)) + }), }, Self::Events(events) => { @@ -676,6 +688,9 @@ impl ToExpand<'_> { .unwrap(), trait_: format_ident!("SolEvent"), selectors: events.iter().map(|e| cx.event_selector(e)).collect(), + can_derive_builtin: events.iter().all(|&event| { + event.parameters.iter().all(|p| cx.can_derive_builtin_traits(&p.ty)) + }), } } } @@ -918,7 +933,7 @@ impl CallLikeExpander<'_> { assert!(selectors.iter().all(|s| s.array.len() == selector_len)); let selector_type = quote!([u8; #selector_len]); - self.cx.type_derives(&mut attrs, types.iter().cloned().map(ast::Type::custom), false); + self.cx.enum_derives(&mut attrs, data.can_derive_builtin); let trait_ = &data.trait_; let mut tokens = quote! { diff --git a/crates/sol-macro-expander/src/expand/mod.rs b/crates/sol-macro-expander/src/expand/mod.rs index b033b710f..170e7c7f2 100644 --- a/crates/sol-macro-expander/src/expand/mod.rs +++ b/crates/sol-macro-expander/src/expand/mod.rs @@ -726,6 +726,26 @@ impl<'ast> ExpCtxt<'ast> { attrs.push(parse_quote! { #[derive(#(#derives), *)] }); } + /// Like [`type_derives`](Self::type_derives), but for the generated `*Calls` + /// / `*Errors` / `*Events` enums. + /// + /// The enum variant types may be synthetic names that don't resolve as + /// items (overloaded items get `_N` suffixes; call variants use `*Call` + /// structs), so whether the builtin traits can be derived is precomputed + /// from the underlying items' parameter types. Enums never derive + /// `Default`. + fn enum_derives(&self, attrs: &mut Vec, can_derive_builtin: bool) { + if let Some(extra) = &self.attrs.extra_derives { + if !extra.is_empty() { + attrs.push(parse_quote! { #[derive(#(#extra),*)] }); + } + } + + if self.attrs.all_derives == Some(true) && can_derive_builtin { + attrs.push(parse_quote! { #[derive(Debug, PartialEq, Eq, Hash)] }); + } + } + /// Returns an error if any of the types in the parameters are unresolved. /// /// Provides a better error message than an `unwrap` or `expect` when we diff --git a/crates/sol-types/tests/derives.rs b/crates/sol-types/tests/derives.rs index 454c77e0b..1add34ffb 100644 --- a/crates/sol-types/tests/derives.rs +++ b/crates/sol-types/tests/derives.rs @@ -98,3 +98,35 @@ fn test_extra_derives() { // Should not increase size since they're equal assert_eq!(calls_set.len(), 1); } + +// Overloaded events (same name, different params) get suffixed variant names +// (e.g. `Swap_0`, `Swap_1`). The generated `*Events` enum must still receive +// the `#[sol(all_derives)]` traits. See alloy-rs/alloy#3856. +#[test] +#[allow(non_snake_case)] +fn test_all_derives_overloaded_events() { + sol! { + #[sol(all_derives)] + contract OverloadedEvents { + event Swap(address indexed sender, uint256 amount0); + event Swap(address indexed sender, address indexed to, int256 amount0, int256 amount1); + } + } + + use OverloadedEvents::*; + + let a = + OverloadedEventsEvents::Swap_0(Swap_0 { sender: Address::ZERO, amount0: U256::from(1) }); + let b = + OverloadedEventsEvents::Swap_0(Swap_0 { sender: Address::ZERO, amount0: U256::from(1) }); + + // Debug + PartialEq + assert_eq!(a, b); + let _ = format!("{a:?}"); + + // Hash + Eq + let mut set = HashSet::new(); + set.insert(a); + set.insert(b); + assert_eq!(set.len(), 1); +}