1use crate::{kw, sol_path, SolPath, Spanned};
2use proc_macro2::Span;
3use std::{
4 fmt,
5 fmt::Write,
6 hash::{Hash, Hasher},
7 num::{IntErrorKind, NonZeroU16},
8};
9use syn::{
10 ext::IdentExt,
11 parse::{Lookahead1, Parse, ParseStream},
12 token::{Bracket, Paren},
13 Error, Ident, Result, Token,
14};
15
16mod array;
17pub use array::TypeArray;
18
19mod function;
20pub use function::TypeFunction;
21
22mod mapping;
23pub use mapping::TypeMapping;
24
25mod tuple;
26pub use tuple::TypeTuple;
27
28#[derive(Clone)]
33pub enum Type {
34 Address(Span, Option<kw::payable>),
37 Bool(Span),
39 String(Span),
41
42 Bytes(Span),
44 FixedBytes(Span, NonZeroU16),
46
47 Int(Span, Option<NonZeroU16>),
49 Uint(Span, Option<NonZeroU16>),
51
52 Array(TypeArray),
54 Tuple(TypeTuple),
56 Function(TypeFunction),
58 Mapping(TypeMapping),
60
61 Custom(SolPath),
63}
64
65impl PartialEq for Type {
66 fn eq(&self, other: &Self) -> bool {
67 match (self, other) {
68 (Self::Address(..), Self::Address(..)) => true,
69 (Self::Bool(_), Self::Bool(_)) => true,
70 (Self::String(_), Self::String(_)) => true,
71 (Self::Bytes { .. }, Self::Bytes { .. }) => true,
72
73 (Self::FixedBytes(_, a), Self::FixedBytes(_, b)) => a == b,
74 (Self::Int(_, a), Self::Int(_, b)) => a == b,
75 (Self::Uint(_, a), Self::Uint(_, b)) => a == b,
76
77 (Self::Tuple(a), Self::Tuple(b)) => a == b,
78 (Self::Array(a), Self::Array(b)) => a == b,
79 (Self::Function(a), Self::Function(b)) => a == b,
80 (Self::Mapping(a), Self::Mapping(b)) => a == b,
81 (Self::Custom(a), Self::Custom(b)) => a == b,
82
83 _ => false,
84 }
85 }
86}
87
88impl Eq for Type {}
89
90impl Hash for Type {
91 fn hash<H: Hasher>(&self, state: &mut H) {
92 std::mem::discriminant(self).hash(state);
93 match self {
94 Self::Address(..) | Self::Bool(_) | Self::String(_) | Self::Bytes(_) => {}
95
96 Self::FixedBytes(_, size) => size.hash(state),
97 Self::Int(_, size) => size.hash(state),
98 Self::Uint(_, size) => size.hash(state),
99
100 Self::Tuple(tuple) => tuple.hash(state),
101 Self::Array(array) => array.hash(state),
102 Self::Function(function) => function.hash(state),
103 Self::Mapping(mapping) => mapping.hash(state),
104 Self::Custom(custom) => custom.hash(state),
105 }
106 }
107}
108
109impl fmt::Debug for Type {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 f.write_str("Type::")?;
112 match self {
113 Self::Address(_, None) => f.write_str("Address"),
114 Self::Address(_, Some(_)) => f.write_str("AddressPayable"),
115 Self::Bool(_) => f.write_str("Bool"),
116 Self::String(_) => f.write_str("String"),
117 Self::Bytes(_) => f.write_str("Bytes"),
118
119 Self::FixedBytes(_, size) => f.debug_tuple("FixedBytes").field(size).finish(),
120 Self::Int(_, size) => f.debug_tuple("Int").field(size).finish(),
121 Self::Uint(_, size) => f.debug_tuple("Uint").field(size).finish(),
122
123 Self::Tuple(tuple) => tuple.fmt(f),
124 Self::Array(array) => array.fmt(f),
125 Self::Function(function) => function.fmt(f),
126 Self::Mapping(mapping) => mapping.fmt(f),
127 Self::Custom(custom) => f.debug_tuple("Custom").field(custom).finish(),
128 }
129 }
130}
131
132impl fmt::Display for Type {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self {
136 Self::Address(_, _) => f.write_str("address"),
137 Self::Bool(_) => f.write_str("bool"),
138 Self::String(_) => f.write_str("string"),
139 Self::Bytes(_) => f.write_str("bytes"),
140
141 Self::FixedBytes(_, size) => write!(f, "bytes{size}"),
142 Self::Int(_, size) => write_opt(f, "int", *size),
143 Self::Uint(_, size) => write_opt(f, "uint", *size),
144
145 Self::Tuple(tuple) => tuple.fmt(f),
146 Self::Array(array) => array.fmt(f),
147 Self::Function(_) => f.write_str("function"),
148 Self::Mapping(mapping) => mapping.fmt(f),
149 Self::Custom(custom) => custom.fmt(f),
150 }
151 }
152}
153
154impl Parse for Type {
155 fn parse(input: ParseStream<'_>) -> Result<Self> {
156 let mut candidate = Self::parse_simple(input)?;
157
158 while input.peek(Bracket) {
161 candidate = Self::Array(TypeArray::parse_nested(Box::new(candidate), input)?);
162 }
163
164 Ok(candidate)
165 }
166}
167
168impl Spanned for Type {
169 fn span(&self) -> Span {
170 match self {
171 &Self::Address(span, payable) => {
172 payable.and_then(|kw| span.join(kw.span)).unwrap_or(span)
173 }
174 Self::Bool(span)
175 | Self::String(span)
176 | Self::Bytes(span)
177 | Self::FixedBytes(span, _)
178 | Self::Int(span, _)
179 | Self::Uint(span, _) => *span,
180 Self::Tuple(tuple) => tuple.span(),
181 Self::Array(array) => array.span(),
182 Self::Function(function) => function.span(),
183 Self::Mapping(mapping) => mapping.span(),
184 Self::Custom(custom) => custom.span(),
185 }
186 }
187
188 fn set_span(&mut self, new_span: Span) {
189 match self {
190 Self::Address(span, payable) => {
191 *span = new_span;
192 if let Some(kw) = payable {
193 kw.span = new_span;
194 }
195 }
196 Self::Bool(span)
197 | Self::String(span)
198 | Self::Bytes(span)
199 | Self::FixedBytes(span, _)
200 | Self::Int(span, _)
201 | Self::Uint(span, _) => *span = new_span,
202
203 Self::Tuple(tuple) => tuple.set_span(new_span),
204 Self::Array(array) => array.set_span(new_span),
205 Self::Function(function) => function.set_span(new_span),
206 Self::Mapping(mapping) => mapping.set_span(new_span),
207 Self::Custom(custom) => custom.set_span(new_span),
208 }
209 }
210}
211
212impl Type {
213 pub fn custom(ident: Ident) -> Self {
214 Self::Custom(sol_path![ident])
215 }
216
217 pub fn peek(lookahead: &Lookahead1<'_>) -> bool {
218 lookahead.peek(syn::token::Paren)
219 || lookahead.peek(kw::tuple)
220 || lookahead.peek(kw::function)
221 || lookahead.peek(kw::mapping)
222 || lookahead.peek(Ident::peek_any)
223 }
224
225 pub fn parse_ident(ident: Ident) -> Self {
232 Self::try_parse_ident(ident.clone()).unwrap_or_else(|_| Self::custom(ident))
233 }
234
235 pub fn try_parse_ident(ident: Ident) -> Result<Self> {
236 let span = ident.span();
237 let s = ident.to_string();
238 let ret = match s.as_str() {
239 "address" => Self::Address(span, None),
240 "bool" => Self::Bool(span),
241 "string" => Self::String(span),
242 s => {
243 if let Some(s) = s.strip_prefix("bytes") {
244 match parse_size(s, span)? {
245 None => Self::custom(ident),
246 Some(Some(size)) if size.get() > 32 => {
247 return Err(Error::new(span, "fixed bytes range is 1-32"))
248 }
249 Some(Some(size)) => Self::FixedBytes(span, size),
250 Some(None) => Self::Bytes(span),
251 }
252 } else if let Some(s) = s.strip_prefix("int") {
253 match parse_size(s, span)? {
254 None => Self::custom(ident),
255 Some(Some(size)) if size.get() > 256 || size.get() % 8 != 0 => {
256 return Err(Error::new(span, "intX must be a multiple of 8 up to 256"))
257 }
258 Some(size) => Self::Int(span, size),
259 }
260 } else if let Some(s) = s.strip_prefix("uint") {
261 match parse_size(s, span)? {
262 None => Self::custom(ident),
263 Some(Some(size)) if size.get() > 256 || size.get() % 8 != 0 => {
264 return Err(Error::new(span, "uintX must be a multiple of 8 up to 256"))
265 }
266 Some(size) => Self::Uint(span, size),
267 }
268 } else {
269 Self::custom(ident)
270 }
271 }
272 };
273 Ok(ret)
274 }
275
276 pub fn parse_payable(mut self, input: ParseStream<'_>) -> Self {
279 if let Self::Address(_, opt @ None) = &mut self {
280 *opt = input.parse().unwrap();
281 }
282 self
283 }
284
285 #[deprecated = "use `is_value_type` instead"]
289 pub fn is_one_word(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
290 self.is_value_type(custom_is_value_type)
291 }
292
293 pub fn is_abi_dynamic(&self) -> bool {
297 match self {
298 Self::Bool(_)
299 | Self::Int(..)
300 | Self::Uint(..)
301 | Self::FixedBytes(..)
302 | Self::Address(..)
303 | Self::Function(_) => false,
304
305 Self::String(_) | Self::Bytes(_) | Self::Custom(_) => true,
306
307 Self::Array(array) => array.is_abi_dynamic(),
308 Self::Tuple(tuple) => tuple.is_abi_dynamic(),
309
310 Self::Mapping(_) => true,
312 }
313 }
314
315 pub fn is_value_type(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
323 match self {
324 Self::Custom(custom) => custom_is_value_type(custom),
325 _ => self.is_value_type_simple(),
326 }
327 }
328
329 pub fn is_value_type_simple(&self) -> bool {
333 matches!(
334 self,
335 Self::Bool(_)
336 | Self::Int(..)
337 | Self::Uint(..)
338 | Self::FixedBytes(..)
339 | Self::Address(..)
340 | Self::Function(_)
341 )
342 }
343
344 pub const fn is_array(&self) -> bool {
345 matches!(self, Self::Array(_))
346 }
347
348 pub const fn is_tuple(&self) -> bool {
349 matches!(self, Self::Tuple(_))
350 }
351
352 pub const fn is_custom(&self) -> bool {
353 matches!(self, Self::Custom(_))
354 }
355
356 pub fn has_custom(&self) -> bool {
358 match self {
359 Self::Custom(_) => true,
360 Self::Array(a) => a.ty.has_custom(),
361 Self::Tuple(t) => t.types.iter().any(Self::has_custom),
362 Self::Function(f) => {
363 f.arguments.iter().any(|arg| arg.ty.has_custom())
364 || f.returns
365 .as_ref()
366 .is_some_and(|ret| ret.returns.iter().any(|arg| arg.ty.has_custom()))
367 }
368 Self::Mapping(m) => m.key.has_custom() || m.value.has_custom(),
369 Self::Bool(_)
370 | Self::Int(..)
371 | Self::Uint(..)
372 | Self::FixedBytes(..)
373 | Self::Address(..)
374 | Self::String(_)
375 | Self::Bytes(_) => false,
376 }
377 }
378
379 pub fn has_custom_simple(&self) -> bool {
382 match self {
383 Self::Custom(_) => true,
384 Self::Array(a) => a.ty.has_custom_simple(),
385 Self::Tuple(t) => t.types.iter().any(Self::has_custom_simple),
386 Self::Mapping(m) => m.key.has_custom_simple() || m.value.has_custom_simple(),
387 Self::Bool(_)
388 | Self::Int(..)
389 | Self::Uint(..)
390 | Self::FixedBytes(..)
391 | Self::Address(..)
392 | Self::Function(_)
393 | Self::String(_)
394 | Self::Bytes(_) => false,
395 }
396 }
397
398 pub fn peel_arrays(&self) -> &Self {
400 let mut this = self;
401 while let Self::Array(array) = this {
402 this = &array.ty;
403 }
404 this
405 }
406
407 pub fn abi_name(&self) -> String {
410 let mut s = String::new();
411 self.abi_name_raw(&mut s);
412 s
413 }
414
415 pub fn abi_name_raw(&self, s: &mut String) {
418 match self {
419 Self::Custom(_) => s.push_str("tuple"),
420 Self::Array(array) => {
421 array.ty.abi_name_raw(s);
422 if let Some(size) = array.size() {
423 write!(s, "[{size}]").unwrap();
424 } else {
425 s.push_str("[]");
426 }
427 }
428 _ => write!(s, "{self}").unwrap(),
429 }
430 }
431
432 #[cfg(feature = "visit")]
434 pub fn visit(&self, f: impl FnMut(&Self)) {
435 use crate::Visit;
436 struct VisitType<F>(F);
437 impl<F: FnMut(&Type)> Visit<'_> for VisitType<F> {
438 fn visit_type(&mut self, ty: &Type) {
439 (self.0)(ty);
440 crate::visit::visit_type(self, ty);
441 }
442 fn visit_block(&mut self, _block: &crate::Block) {}
444 fn visit_expr(&mut self, _expr: &crate::Expr) {}
445 fn visit_stmt(&mut self, _stmt: &crate::Stmt) {}
446 fn visit_file(&mut self, _file: &crate::File) {}
447 fn visit_item(&mut self, _item: &crate::Item) {}
448 }
449 VisitType(f).visit_type(self);
450 }
451
452 #[cfg(feature = "visit-mut")]
454 pub fn visit_mut(&mut self, f: impl FnMut(&mut Self)) {
455 use crate::VisitMut;
456 struct VisitTypeMut<F>(F);
457 impl<F: FnMut(&mut Type)> VisitMut<'_> for VisitTypeMut<F> {
458 fn visit_type(&mut self, ty: &mut Type) {
459 (self.0)(ty);
460 crate::visit_mut::visit_type(self, ty);
461 }
462 fn visit_block(&mut self, _block: &mut crate::Block) {}
464 fn visit_expr(&mut self, _expr: &mut crate::Expr) {}
465 fn visit_stmt(&mut self, _stmt: &mut crate::Stmt) {}
466 fn visit_file(&mut self, _file: &mut crate::File) {}
467 fn visit_item(&mut self, _item: &mut crate::Item) {}
468 }
469 VisitTypeMut(f).visit_type(self);
470 }
471
472 #[inline]
474 fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
475 if input.peek(Paren) || input.peek(kw::tuple) {
476 input.parse().map(Self::Tuple)
477 } else if input.peek(kw::function) {
478 input.parse().map(Self::Function)
479 } else if input.peek(kw::mapping) {
480 input.parse().map(Self::Mapping)
481 } else if input.peek2(Token![.]) {
482 input.parse().map(Self::Custom)
483 } else if input.peek(Ident::peek_any) {
484 let ident = input.call(Ident::parse_any)?;
485 Ok(Self::parse_ident(ident).parse_payable(input))
486 } else {
487 Err(input.error(
488 "expected a Solidity type: \
489 `address`, `bool`, `string`, `bytesN`, `intN`, `uintN`, \
490 `tuple`, `function`, `mapping`, or a custom type name",
491 ))
492 }
493 }
494}
495
496fn write_opt(f: &mut fmt::Formatter<'_>, name: &str, size: Option<NonZeroU16>) -> fmt::Result {
497 f.write_str(name)?;
498 if let Some(size) = size {
499 write!(f, "{size}")?;
500 }
501 Ok(())
502}
503
504fn parse_size(s: &str, span: Span) -> Result<Option<Option<NonZeroU16>>> {
507 let opt = match s.parse::<NonZeroU16>() {
508 Ok(size) => Some(Some(size)),
509 Err(e) => match e.kind() {
510 IntErrorKind::Empty => Some(None),
512 IntErrorKind::InvalidDigit => None,
514 _ => return Err(Error::new(span, format_args!("invalid size: {e}"))),
516 },
517 };
518 Ok(opt)
519}