alloy_sol_macro_expander/expand/
ty.rs1use super::ExpCtxt;
4use ast::{Item, Parameters, SolIdent, Spanned, Type, TypeArray};
5use proc_macro_error3::{abort, emit_error};
6use proc_macro2::{Ident, Literal, Span, TokenStream};
7use quote::{ToTokens, quote_spanned};
8use std::{fmt, num::NonZeroU16};
9
10const MAX_SUPPORTED_ARRAY_LEN: usize = 32;
11const MAX_SUPPORTED_TUPLE_LEN: usize = 12;
12
13impl ExpCtxt<'_> {
14 pub fn expand_type(&self, ty: &Type) -> TokenStream {
17 let mut tokens = TokenStream::new();
18 self.expand_type_to(ty, &mut tokens);
19 tokens
20 }
21
22 pub fn expand_rust_type(&self, ty: &Type) -> TokenStream {
28 let mut tokens = TokenStream::new();
29 self.expand_rust_type_to(ty, &mut tokens);
30 tokens
31 }
32
33 pub fn expand_type_to(&self, ty: &Type, tokens: &mut TokenStream) {
38 let alloy_sol_types = &self.crates.sol_types;
39 let tts = match *ty {
40 Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::sol_data::Address },
41 Type::Bool(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bool },
42 Type::String(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::String },
43 Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bytes },
44
45 Type::FixedBytes(span, size) => {
46 assert!(size.get() <= 32);
47 let size = Literal::u16_unsuffixed(size.get());
48 quote_spanned! {span=> #alloy_sol_types::sol_data::FixedBytes<#size> }
49 }
50 Type::Int(span, size) | Type::Uint(span, size) => {
51 let name = match ty {
52 Type::Int(..) => "Int",
53 Type::Uint(..) => "Uint",
54 _ => unreachable!(),
55 };
56 let name = Ident::new(name, span);
57
58 let size = size.map_or(256, NonZeroU16::get);
59 assert!(size <= 256 && size % 8 == 0);
60 let size = Literal::u16_unsuffixed(size);
61
62 quote_spanned! {span=> #alloy_sol_types::sol_data::#name<#size> }
63 }
64
65 Type::Tuple(ref tuple) => {
66 return tuple.paren_token.surround(tokens, |tokens| {
67 for pair in tuple.types.pairs() {
68 let (ty, comma) = pair.into_tuple();
69 self.expand_type_to(ty, tokens);
70 comma.to_tokens(tokens);
71 }
72 });
73 }
74 Type::Array(ref array) => {
75 let ty = self.expand_type(&array.ty);
76 let span = array.span();
77 if let Some(size) = self.eval_array_size(array) {
78 quote_spanned! {span=> #alloy_sol_types::sol_data::FixedArray<#ty, #size> }
79 } else {
80 quote_spanned! {span=> #alloy_sol_types::sol_data::Array<#ty> }
81 }
82 }
83 Type::Function(ref function) => quote_spanned! {function.span()=>
84 #alloy_sol_types::sol_data::Function
85 },
86 Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=>
87 ::core::compile_error!("Mapping types are not supported here")
88 },
89
90 Type::Custom(ref custom) => {
91 if let Some(Item::Contract(c)) = self.try_item(custom) {
92 quote_spanned! {c.span()=> #alloy_sol_types::sol_data::Address }
93 } else {
94 let segments = custom.iter();
95 quote_spanned! {custom.span()=> #(#segments)::* }
96 }
97 }
98 };
99 tokens.extend(tts);
100 }
101
102 pub(crate) fn expand_rust_type_to(&self, ty: &Type, tokens: &mut TokenStream) {
107 let alloy_sol_types = &self.crates.sol_types;
108 let tts = match *ty {
109 Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::private::Address },
110 Type::Bool(span) => return Ident::new("bool", span).to_tokens(tokens),
111 Type::String(span) => quote_spanned! {span=> #alloy_sol_types::private::String },
112 Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::private::Bytes },
113
114 Type::FixedBytes(span, size) => {
115 assert!(size.get() <= 32);
116 let size = Literal::u16_unsuffixed(size.get());
117 quote_spanned! {span=> #alloy_sol_types::private::FixedBytes<#size> }
118 }
119 Type::Int(span, size) | Type::Uint(span, size) => {
120 let size = size.map_or(256, NonZeroU16::get);
121 let primitive = matches!(size, 8 | 16 | 32 | 64 | 128);
122 if primitive {
123 let prefix = match ty {
124 Type::Int(..) => "i",
125 Type::Uint(..) => "u",
126 _ => unreachable!(),
127 };
128 return Ident::new(&format!("{prefix}{size}"), span).to_tokens(tokens);
129 }
130 let prefix = match ty {
131 Type::Int(..) => "I",
132 Type::Uint(..) => "U",
133 _ => unreachable!(),
134 };
135 let name = Ident::new(&format!("{prefix}{size}"), span);
136 quote_spanned! {span=> #alloy_sol_types::private::primitives::aliases::#name }
137 }
138
139 Type::Tuple(ref tuple) => {
140 return tuple.paren_token.surround(tokens, |tokens| {
141 for pair in tuple.types.pairs() {
142 let (ty, comma) = pair.into_tuple();
143 self.expand_rust_type_to(ty, tokens);
144 comma.to_tokens(tokens);
145 }
146 });
147 }
148 Type::Array(ref array) => {
149 let ty = self.expand_rust_type(&array.ty);
150 let span = array.span();
151 if let Some(size) = self.eval_array_size(array) {
152 quote_spanned! {span=> [#ty; #size] }
153 } else {
154 quote_spanned! {span=> #alloy_sol_types::private::Vec<#ty> }
155 }
156 }
157 Type::Function(ref function) => quote_spanned! {function.span()=>
158 #alloy_sol_types::private::Function
159 },
160 Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=>
161 ::core::compile_error!("Mapping types are not supported here")
162 },
163
164 Type::Custom(_) => {
166 let span = ty.span();
167 let ty = self.expand_type(ty);
168 quote_spanned! {span=> <#ty as #alloy_sol_types::SolType>::RustType }
169 }
170 };
171 tokens.extend(tts);
172 }
173
174 pub(crate) fn params_base_data_size<P>(&self, params: &Parameters<P>) -> usize {
178 params.iter().map(|param| self.type_base_data_size(¶m.ty)).sum()
179 }
180
181 pub(crate) fn type_base_data_size(&self, ty: &Type) -> usize {
187 match ty {
188 Type::Address(..)
190 | Type::Bool(_)
191 | Type::Int(..)
192 | Type::Uint(..)
193 | Type::FixedBytes(..)
194 | Type::Function(_) => 32,
195
196 Type::String(_) | Type::Bytes(_) | Type::Array(TypeArray { size: None, .. }) => 64,
198
199 Type::Array(a @ TypeArray { ty: inner, size: Some(_), .. }) => {
201 let Some(size) = self.eval_array_size(a) else { return 0 };
202 self.type_base_data_size(inner).checked_mul(size).unwrap_or(0)
203 }
204
205 Type::Tuple(tuple) => tuple.types.iter().map(|ty| self.type_base_data_size(ty)).sum(),
207
208 Type::Custom(name) => match self.try_item(name) {
209 Some(Item::Contract(_)) | Some(Item::Enum(_)) => 32,
210 Some(Item::Error(error)) => {
211 error.parameters.types().map(|ty| self.type_base_data_size(ty)).sum()
212 }
213 Some(Item::Event(event)) => {
214 event.parameters.iter().map(|p| self.type_base_data_size(&p.ty)).sum()
215 }
216 Some(Item::Struct(strukt)) => {
217 strukt.fields.types().map(|ty| self.type_base_data_size(ty)).sum()
218 }
219 Some(Item::Udt(udt)) => self.type_base_data_size(&udt.ty),
220 Some(item) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
221 None => 0,
222 },
223
224 Type::Mapping(_) => 0,
226 }
227 }
228
229 pub(crate) fn can_derive_default(&self, ty: &Type) -> bool {
231 self.can_derive_default_in_namespace(ty, &self.current_namespace)
232 }
233
234 fn can_derive_default_in_namespace(
235 &self,
236 ty: &Type,
237 current_namespace: &Option<SolIdent>,
238 ) -> bool {
239 match ty {
240 Type::Array(a) => match self.eval_array_size(a) {
241 None => true,
243 Some(sz) => {
245 sz <= MAX_SUPPORTED_ARRAY_LEN
246 && self.can_derive_default_in_namespace(&a.ty, current_namespace)
247 }
248 },
249 Type::Tuple(tuple) => {
250 if tuple.types.len() > MAX_SUPPORTED_TUPLE_LEN {
251 false
252 } else {
253 tuple
254 .types
255 .iter()
256 .all(|ty| self.can_derive_default_in_namespace(ty, current_namespace))
257 }
258 }
259
260 Type::Custom(name) => match self.try_item_in_namespace(name, current_namespace) {
261 Some((_, Item::Contract(_))) => true,
262 Some((_, Item::Enum(_))) => false,
263 Some((namespace, Item::Error(error))) => error
264 .parameters
265 .types()
266 .all(|ty| self.can_derive_default_in_namespace(ty, namespace)),
267 Some((namespace, Item::Event(event))) => event
268 .parameters
269 .iter()
270 .all(|p| self.can_derive_default_in_namespace(&p.ty, namespace)),
271 Some((namespace, Item::Struct(strukt))) => strukt
272 .fields
273 .types()
274 .all(|ty| self.can_derive_default_in_namespace(ty, namespace)),
275 Some((namespace, Item::Udt(udt))) => {
276 self.can_derive_default_in_namespace(&udt.ty, namespace)
277 }
278 Some((_, item)) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
279 _ => false,
280 },
281
282 _ => true,
283 }
284 }
285
286 pub(crate) fn can_derive_builtin_traits(&self, ty: &Type) -> bool {
289 self.can_derive_builtin_traits_in_namespace(ty, &self.current_namespace)
290 }
291
292 fn can_derive_builtin_traits_in_namespace(
293 &self,
294 ty: &Type,
295 current_namespace: &Option<SolIdent>,
296 ) -> bool {
297 match ty {
298 Type::Array(a) => self.can_derive_builtin_traits_in_namespace(&a.ty, current_namespace),
299 Type::Tuple(tuple) => {
300 if tuple.types.len() > MAX_SUPPORTED_TUPLE_LEN {
301 false
302 } else {
303 tuple.types.iter().all(|ty| {
304 self.can_derive_builtin_traits_in_namespace(ty, current_namespace)
305 })
306 }
307 }
308
309 Type::Custom(name) => match self.try_item_in_namespace(name, current_namespace) {
310 Some((_, Item::Contract(_))) | Some((_, Item::Enum(_))) => true,
311 Some((namespace, Item::Error(error))) => error
312 .parameters
313 .types()
314 .all(|ty| self.can_derive_builtin_traits_in_namespace(ty, namespace)),
315 Some((namespace, Item::Event(event))) => event
316 .parameters
317 .iter()
318 .all(|p| self.can_derive_builtin_traits_in_namespace(&p.ty, namespace)),
319 Some((namespace, Item::Struct(strukt))) => strukt
320 .fields
321 .types()
322 .all(|ty| self.can_derive_builtin_traits_in_namespace(ty, namespace)),
323 Some((namespace, Item::Udt(udt))) => {
324 self.can_derive_builtin_traits_in_namespace(&udt.ty, namespace)
325 }
326 Some((_, item)) => abort!(item.span(), "Invalid type in struct field: {:?}", item),
327 _ => false,
328 },
329
330 _ => true,
331 }
332 }
333
334 pub fn eval_array_size(&self, array: &TypeArray) -> Option<ArraySize> {
336 let size = array.size.as_deref()?;
337 ArraySizeEvaluator::new(self).eval(size)
338 }
339}
340
341type ArraySize = usize;
342
343struct ArraySizeEvaluator<'a> {
344 cx: &'a ExpCtxt<'a>,
345 depth: usize,
346}
347
348impl<'a> ArraySizeEvaluator<'a> {
349 fn new(cx: &'a ExpCtxt<'a>) -> Self {
350 Self { cx, depth: 0 }
351 }
352
353 fn eval(&mut self, expr: &ast::Expr) -> Option<ArraySize> {
354 match self.try_eval(expr) {
355 Ok(value) => Some(value),
356 Err(err) => {
357 emit_error!(
358 expr.span(), "evaluation of constant value failed";
359 note = err.span() => err.kind.msg()
360 );
361 None
362 }
363 }
364 }
365
366 fn try_eval(&mut self, expr: &ast::Expr) -> Result<ArraySize, EvalError> {
367 self.depth += 1;
368 if self.depth > 32 {
369 return Err(EvalErrorKind::RecursionLimitReached.spanned(expr.span()));
370 }
371 let mut r = self.try_eval_expr(expr);
372 if let Err(e) = &mut r {
373 if e.span.is_none() {
374 e.span = Some(expr.span());
375 }
376 }
377 self.depth -= 1;
378 r
379 }
380
381 fn try_eval_expr(&mut self, expr: &ast::Expr) -> Result<ArraySize, EvalError> {
382 let expr = expr.peel_parens();
383 match expr {
384 ast::Expr::Lit(ast::Lit::Number(ast::LitNumber::Int(n))) => {
385 n.base10_digits().parse::<ArraySize>().map_err(|_| EE::ParseInt.into())
386 }
387 ast::Expr::Binary(bin) => {
388 let lhs = self.try_eval(&bin.left)?;
389 let rhs = self.try_eval(&bin.right)?;
390 self.eval_binop(bin.op, lhs, rhs)
391 }
392 ast::Expr::Ident(ident) => {
393 let name = ast::sol_path![ident.clone()];
394 let Some(item) = self.cx.try_item(&name) else {
395 eprintln!("{}", std::backtrace::Backtrace::force_capture());
396 eprintln!("{:#?}", self.cx.all_items);
397 return Err(EE::CouldNotResolve.into());
398 };
399 let ast::Item::Variable(var) = item else {
400 return Err(EE::NonConstantVar.into());
401 };
402 if !var.attributes.has_constant() {
403 return Err(EE::NonConstantVar.into());
404 }
405 let Some((_, expr)) = var.initializer.as_ref() else {
406 return Err(EE::NonConstantVar.into());
407 };
408 self.try_eval(expr)
409 }
410 ast::Expr::LitDenominated(ast::LitDenominated {
411 number: ast::LitNumber::Int(n),
412 denom,
413 }) => {
414 let n = n.base10_digits().parse::<ArraySize>().map_err(|_| EE::ParseInt)?;
415 let Ok(denom) = denom.value().try_into() else {
416 return Err(EE::IntTooBig.into());
417 };
418 n.checked_mul(denom).ok_or_else(|| EE::ArithmeticOverflow.into())
419 }
420 ast::Expr::Unary(unary) => {
421 let value = self.try_eval(&unary.expr)?;
422 self.eval_unop(unary.op, value)
423 }
424 _ => Err(EE::UnsupportedExpr.into()),
425 }
426 }
427
428 fn eval_binop(
429 &mut self,
430 bin: ast::BinOp,
431 lhs: ArraySize,
432 rhs: ArraySize,
433 ) -> Result<ArraySize, EvalError> {
434 match bin {
435 ast::BinOp::Shr(..) => rhs
436 .try_into()
437 .ok()
438 .and_then(|rhs| lhs.checked_shr(rhs))
439 .ok_or_else(|| EE::ArithmeticOverflow.into()),
440 ast::BinOp::Shl(..) => rhs
441 .try_into()
442 .ok()
443 .and_then(|rhs| lhs.checked_shl(rhs))
444 .ok_or_else(|| EE::ArithmeticOverflow.into()),
445 ast::BinOp::BitAnd(..) => Ok(lhs & rhs),
446 ast::BinOp::BitOr(..) => Ok(lhs | rhs),
447 ast::BinOp::BitXor(..) => Ok(lhs ^ rhs),
448 ast::BinOp::Add(..) => {
449 lhs.checked_add(rhs).ok_or_else(|| EE::ArithmeticOverflow.into())
450 }
451 ast::BinOp::Sub(..) => {
452 lhs.checked_sub(rhs).ok_or_else(|| EE::ArithmeticOverflow.into())
453 }
454 ast::BinOp::Pow(..) => rhs
455 .try_into()
456 .ok()
457 .and_then(|rhs| lhs.checked_pow(rhs))
458 .ok_or_else(|| EE::ArithmeticOverflow.into()),
459 ast::BinOp::Mul(..) => {
460 lhs.checked_mul(rhs).ok_or_else(|| EE::ArithmeticOverflow.into())
461 }
462 ast::BinOp::Div(..) => lhs.checked_div(rhs).ok_or_else(|| EE::DivisionByZero.into()),
463 ast::BinOp::Rem(..) => lhs.checked_rem(rhs).ok_or_else(|| EE::DivisionByZero.into()),
464 _ => Err(EE::UnsupportedExpr.into()),
465 }
466 }
467
468 fn eval_unop(&mut self, unop: ast::UnOp, value: ArraySize) -> Result<ArraySize, EvalError> {
469 match unop {
470 ast::UnOp::Neg(..) => value.checked_neg().ok_or_else(|| EE::ArithmeticOverflow.into()),
471 ast::UnOp::BitNot(..) | ast::UnOp::Not(..) => Ok(!value),
472 _ => Err(EE::UnsupportedUnaryOp.into()),
473 }
474 }
475}
476
477struct EvalError {
478 kind: EvalErrorKind,
479 span: Option<Span>,
480}
481
482impl From<EvalErrorKind> for EvalError {
483 fn from(kind: EvalErrorKind) -> Self {
484 Self { kind, span: None }
485 }
486}
487
488impl EvalError {
489 fn span(&self) -> Span {
490 self.span.unwrap_or_else(Span::call_site)
491 }
492}
493
494enum EvalErrorKind {
495 RecursionLimitReached,
496 ArithmeticOverflow,
497 ParseInt,
498 IntTooBig,
499 DivisionByZero,
500 UnsupportedUnaryOp,
501 UnsupportedExpr,
502 CouldNotResolve,
503 NonConstantVar,
504}
505use EvalErrorKind as EE;
506
507impl EvalErrorKind {
508 fn spanned(self, span: Span) -> EvalError {
509 EvalError { kind: self, span: Some(span) }
510 }
511
512 fn msg(&self) -> &'static str {
513 match self {
514 Self::RecursionLimitReached => "recursion limit reached",
515 Self::ArithmeticOverflow => "arithmetic overflow",
516 Self::ParseInt => "failed to parse integer",
517 Self::IntTooBig => "integer value is too big",
518 Self::DivisionByZero => "division by zero",
519 Self::UnsupportedUnaryOp => "unsupported unary operation",
520 Self::UnsupportedExpr => "unsupported expression",
521 Self::CouldNotResolve => "could not resolve identifier",
522 Self::NonConstantVar => "only constant variables are allowed",
523 }
524 }
525}
526
527pub(crate) struct TypePrinter<'ast> {
531 cx: &'ast ExpCtxt<'ast>,
532 ty: &'ast Type,
533}
534
535impl<'ast> TypePrinter<'ast> {
536 pub(crate) fn new(cx: &'ast ExpCtxt<'ast>, ty: &'ast Type) -> Self {
537 Self { cx, ty }
538 }
539}
540
541impl fmt::Display for TypePrinter<'_> {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 match self.ty {
544 Type::Int(_, None) => f.write_str("int256"),
545 Type::Uint(_, None) => f.write_str("uint256"),
546
547 Type::Array(array) => {
548 Self::new(self.cx, &array.ty).fmt(f)?;
549 f.write_str("[")?;
550 if let Some(size) = self.cx.eval_array_size(array) {
551 size.fmt(f)?;
552 }
553 f.write_str("]")
554 }
555 Type::Tuple(tuple) => {
556 f.write_str("(")?;
557 for (i, ty) in tuple.types.iter().enumerate() {
558 if i > 0 {
559 f.write_str(",")?;
560 }
561 Self::new(self.cx, ty).fmt(f)?;
562 }
563 f.write_str(")")
564 }
565
566 Type::Custom(name) => Self::new(self.cx, self.cx.custom_type(name)).fmt(f),
567
568 ty => ty.fmt(f),
569 }
570 }
571}