alloy_sol_macro_expander/expand/
ty.rsuse super::{ExpCtxt, ExternCrates};
use ast::{Item, Parameters, Spanned, Type, TypeArray};
use proc_macro2::{Ident, Literal, TokenStream};
use proc_macro_error2::abort;
use quote::{quote_spanned, ToTokens};
use std::{fmt, num::NonZeroU16};
pub fn expand_type(ty: &Type, crates: &ExternCrates) -> TokenStream {
let mut tokens = TokenStream::new();
rec_expand_type(ty, crates, &mut tokens);
tokens
}
pub fn expand_rust_type(ty: &Type, crates: &ExternCrates) -> TokenStream {
let mut tokens = TokenStream::new();
rec_expand_rust_type(ty, crates, &mut tokens);
tokens
}
pub(super) fn rec_expand_type(ty: &Type, crates: &ExternCrates, tokens: &mut TokenStream) {
let alloy_sol_types = &crates.sol_types;
let tts = match *ty {
Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::sol_data::Address },
Type::Bool(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bool },
Type::String(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::String },
Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bytes },
Type::FixedBytes(span, size) => {
assert!(size.get() <= 32);
let size = Literal::u16_unsuffixed(size.get());
quote_spanned! {span=> #alloy_sol_types::sol_data::FixedBytes<#size> }
}
Type::Int(span, size) | Type::Uint(span, size) => {
let name = match ty {
Type::Int(..) => "Int",
Type::Uint(..) => "Uint",
_ => unreachable!(),
};
let name = Ident::new(name, span);
let size = size.map_or(256, NonZeroU16::get);
assert!(size <= 256 && size % 8 == 0);
let size = Literal::u16_unsuffixed(size);
quote_spanned! {span=> #alloy_sol_types::sol_data::#name<#size> }
}
Type::Tuple(ref tuple) => {
return tuple.paren_token.surround(tokens, |tokens| {
for pair in tuple.types.pairs() {
let (ty, comma) = pair.into_tuple();
rec_expand_type(ty, crates, tokens);
comma.to_tokens(tokens);
}
})
}
Type::Array(ref array) => {
let ty = expand_type(&array.ty, crates);
let span = array.span();
if let Some(size) = array.size() {
quote_spanned! {span=> #alloy_sol_types::sol_data::FixedArray<#ty, #size> }
} else {
quote_spanned! {span=> #alloy_sol_types::sol_data::Array<#ty> }
}
}
Type::Function(ref function) => quote_spanned! {function.span()=>
#alloy_sol_types::sol_data::Function
},
Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=>
::core::compile_error!("Mapping types are not supported here")
},
Type::Custom(ref custom) => {
let segments = custom.iter();
quote_spanned! {custom.span()=> #(#segments)::* }
}
};
tokens.extend(tts);
}
pub(super) fn rec_expand_rust_type(ty: &Type, crates: &ExternCrates, tokens: &mut TokenStream) {
let alloy_sol_types = &crates.sol_types;
let tts = match *ty {
Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::private::Address },
Type::Bool(span) => return Ident::new("bool", span).to_tokens(tokens),
Type::String(span) => quote_spanned! {span=> #alloy_sol_types::private::String },
Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::private::Bytes },
Type::FixedBytes(span, size) => {
assert!(size.get() <= 32);
let size = Literal::u16_unsuffixed(size.get());
quote_spanned! {span=> #alloy_sol_types::private::FixedBytes<#size> }
}
Type::Int(span, size) | Type::Uint(span, size) => {
let size = size.map_or(256, NonZeroU16::get);
let primitive = matches!(size, 8 | 16 | 32 | 64 | 128);
if primitive {
let prefix = match ty {
Type::Int(..) => "i",
Type::Uint(..) => "u",
_ => unreachable!(),
};
return Ident::new(&format!("{prefix}{size}"), span).to_tokens(tokens);
}
let prefix = match ty {
Type::Int(..) => "I",
Type::Uint(..) => "U",
_ => unreachable!(),
};
let name = Ident::new(&format!("{prefix}{size}"), span);
quote_spanned! {span=> #alloy_sol_types::private::primitives::aliases::#name }
}
Type::Tuple(ref tuple) => {
return tuple.paren_token.surround(tokens, |tokens| {
for pair in tuple.types.pairs() {
let (ty, comma) = pair.into_tuple();
rec_expand_rust_type(ty, crates, tokens);
comma.to_tokens(tokens);
}
})
}
Type::Array(ref array) => {
let ty = expand_rust_type(&array.ty, crates);
let span = array.span();
if let Some(size) = array.size() {
quote_spanned! {span=> [#ty; #size] }
} else {
quote_spanned! {span=> #alloy_sol_types::private::Vec<#ty> }
}
}
Type::Function(ref function) => quote_spanned! {function.span()=>
#alloy_sol_types::private::Function
},
Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=>
::core::compile_error!("Mapping types are not supported here")
},
Type::Custom(_) => {
let span = ty.span();
let ty = expand_type(ty, crates);
quote_spanned! {span=> <#ty as #alloy_sol_types::SolType>::RustType }
}
};
tokens.extend(tts);
}
pub(super) fn params_base_data_size<P>(cx: &ExpCtxt<'_>, params: &Parameters<P>) -> usize {
params.iter().map(|param| type_base_data_size(cx, ¶m.ty)).sum()
}
pub(super) fn type_base_data_size(cx: &ExpCtxt<'_>, ty: &Type) -> usize {
match ty {
Type::Address(..)
| Type::Bool(_)
| Type::Int(..)
| Type::Uint(..)
| Type::FixedBytes(..)
| Type::Function(_) => 32,
Type::String(_) | Type::Bytes(_) | Type::Array(TypeArray { size: None, .. }) => 64,
Type::Array(a @ TypeArray { ty: inner, size: Some(_), .. }) => {
type_base_data_size(cx, inner) * a.size().unwrap()
}
Type::Tuple(tuple) => tuple.types.iter().map(|ty| type_base_data_size(cx, ty)).sum(),
Type::Custom(name) => match cx.try_item(name) {
Some(Item::Contract(_)) | Some(Item::Enum(_)) => 32,
Some(Item::Error(error)) => {
error.parameters.types().map(|ty| type_base_data_size(cx, ty)).sum()
}
Some(Item::Event(event)) => {
event.parameters.iter().map(|p| type_base_data_size(cx, &p.ty)).sum()
}
Some(Item::Struct(strukt)) => {
strukt.fields.types().map(|ty| type_base_data_size(cx, ty)).sum()
}
Some(Item::Udt(udt)) => type_base_data_size(cx, &udt.ty),
Some(item) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
None => 0,
},
Type::Mapping(_) => 0,
}
}
const MAX_SUPPORTED_ARRAY_LEN: usize = 32;
const MAX_SUPPORTED_TUPLE_LEN: usize = 12;
pub(super) fn can_derive_default(cx: &ExpCtxt<'_>, ty: &Type) -> bool {
match ty {
Type::Array(a) => {
a.size().map_or(true, |sz| sz <= MAX_SUPPORTED_ARRAY_LEN)
&& can_derive_default(cx, &a.ty)
}
Type::Tuple(tuple) => {
if tuple.types.len() > MAX_SUPPORTED_TUPLE_LEN {
false
} else {
tuple.types.iter().all(|ty| can_derive_default(cx, ty))
}
}
Type::Custom(name) => match cx.try_item(name) {
Some(Item::Contract(_)) | Some(Item::Enum(_)) => false,
Some(Item::Error(error)) => {
error.parameters.types().all(|ty| can_derive_default(cx, ty))
}
Some(Item::Event(event)) => {
event.parameters.iter().all(|p| can_derive_default(cx, &p.ty))
}
Some(Item::Struct(strukt)) => {
strukt.fields.types().all(|ty| can_derive_default(cx, ty))
}
Some(Item::Udt(udt)) => can_derive_default(cx, &udt.ty),
Some(item) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
_ => false,
},
_ => true,
}
}
pub(super) fn can_derive_builtin_traits(cx: &ExpCtxt<'_>, ty: &Type) -> bool {
match ty {
Type::Array(a) => can_derive_builtin_traits(cx, &a.ty),
Type::Tuple(tuple) => {
if tuple.types.len() > MAX_SUPPORTED_TUPLE_LEN {
false
} else {
tuple.types.iter().all(|ty| can_derive_builtin_traits(cx, ty))
}
}
Type::Custom(name) => match cx.try_item(name) {
Some(Item::Contract(_)) | Some(Item::Enum(_)) => true,
Some(Item::Error(error)) => {
error.parameters.types().all(|ty| can_derive_builtin_traits(cx, ty))
}
Some(Item::Event(event)) => {
event.parameters.iter().all(|p| can_derive_builtin_traits(cx, &p.ty))
}
Some(Item::Struct(strukt)) => {
strukt.fields.types().all(|ty| can_derive_builtin_traits(cx, ty))
}
Some(Item::Udt(udt)) => can_derive_builtin_traits(cx, &udt.ty),
Some(item) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
_ => false,
},
_ => true,
}
}
pub(super) struct TypePrinter<'ast> {
cx: &'ast ExpCtxt<'ast>,
ty: &'ast Type,
}
impl<'ast> TypePrinter<'ast> {
pub(super) fn new(cx: &'ast ExpCtxt<'ast>, ty: &'ast Type) -> Self {
Self { cx, ty }
}
}
impl fmt::Display for TypePrinter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.ty {
Type::Int(_, None) => f.write_str("int256"),
Type::Uint(_, None) => f.write_str("uint256"),
Type::Array(array) => {
Self::new(self.cx, &array.ty).fmt(f)?;
f.write_str("[")?;
if let Some(size) = array.size() {
size.fmt(f)?;
}
f.write_str("]")
}
Type::Tuple(tuple) => {
f.write_str("(")?;
for (i, ty) in tuple.types.iter().enumerate() {
if i > 0 {
f.write_str(",")?;
}
Self::new(self.cx, ty).fmt(f)?;
}
f.write_str(")")
}
Type::Custom(name) => Self::new(self.cx, self.cx.custom_type(name)).fmt(f),
ty => ty.fmt(f),
}
}
}