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) -> Result<Self> {
232 let span = ident.span();
233 let s = ident.to_string();
234 let ret = match s.as_str() {
235 "address" => Self::Address(span, None),
236 "bool" => Self::Bool(span),
237 "string" => Self::String(span),
238 s => {
239 if let Some(s) = s.strip_prefix("bytes") {
240 match parse_size(s, span)? {
241 None => Self::custom(ident),
242 Some(Some(size)) if size.get() > 32 => {
243 return Err(Error::new(span, "fixed bytes range is 1-32"))
244 }
245 Some(Some(size)) => Self::FixedBytes(span, size),
246 Some(None) => Self::Bytes(span),
247 }
248 } else if let Some(s) = s.strip_prefix("int") {
249 match parse_size(s, span)? {
250 None => Self::custom(ident),
251 Some(Some(size)) if size.get() > 256 || size.get() % 8 != 0 => {
252 return Err(Error::new(span, "intX must be a multiple of 8 up to 256"))
253 }
254 Some(size) => Self::Int(span, size),
255 }
256 } else if let Some(s) = s.strip_prefix("uint") {
257 match parse_size(s, span)? {
258 None => Self::custom(ident),
259 Some(Some(size)) if size.get() > 256 || size.get() % 8 != 0 => {
260 return Err(Error::new(span, "uintX must be a multiple of 8 up to 256"))
261 }
262 Some(size) => Self::Uint(span, size),
263 }
264 } else {
265 Self::custom(ident)
266 }
267 }
268 };
269 Ok(ret)
270 }
271
272 pub fn parse_payable(mut self, input: ParseStream<'_>) -> Result<Self> {
275 if let Self::Address(_, opt @ None) = &mut self {
276 *opt = input.parse()?;
277 }
278 Ok(self)
279 }
280
281 #[deprecated = "use `is_value_type` instead"]
285 pub fn is_one_word(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
286 self.is_value_type(custom_is_value_type)
287 }
288
289 pub fn is_abi_dynamic(&self) -> bool {
293 match self {
294 Self::Bool(_)
295 | Self::Int(..)
296 | Self::Uint(..)
297 | Self::FixedBytes(..)
298 | Self::Address(..)
299 | Self::Function(_) => false,
300
301 Self::String(_) | Self::Bytes(_) | Self::Custom(_) => true,
302
303 Self::Array(array) => array.is_abi_dynamic(),
304 Self::Tuple(tuple) => tuple.is_abi_dynamic(),
305
306 Self::Mapping(_) => true,
308 }
309 }
310
311 pub fn is_value_type(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
319 match self {
320 Self::Custom(custom) => custom_is_value_type(custom),
321 _ => self.is_value_type_simple(),
322 }
323 }
324
325 pub fn is_value_type_simple(&self) -> bool {
329 matches!(
330 self,
331 Self::Bool(_)
332 | Self::Int(..)
333 | Self::Uint(..)
334 | Self::FixedBytes(..)
335 | Self::Address(..)
336 | Self::Function(_)
337 )
338 }
339
340 pub const fn is_array(&self) -> bool {
341 matches!(self, Self::Array(_))
342 }
343
344 pub const fn is_tuple(&self) -> bool {
345 matches!(self, Self::Tuple(_))
346 }
347
348 pub const fn is_custom(&self) -> bool {
349 matches!(self, Self::Custom(_))
350 }
351
352 pub fn has_custom(&self) -> bool {
354 match self {
355 Self::Custom(_) => true,
356 Self::Array(a) => a.ty.has_custom(),
357 Self::Tuple(t) => t.types.iter().any(Self::has_custom),
358 Self::Function(f) => {
359 f.arguments.iter().any(|arg| arg.ty.has_custom())
360 || f.returns
361 .as_ref()
362 .map_or(false, |ret| ret.returns.iter().any(|arg| arg.ty.has_custom()))
363 }
364 Self::Mapping(m) => m.key.has_custom() || m.value.has_custom(),
365 Self::Bool(_)
366 | Self::Int(..)
367 | Self::Uint(..)
368 | Self::FixedBytes(..)
369 | Self::Address(..)
370 | Self::String(_)
371 | Self::Bytes(_) => false,
372 }
373 }
374
375 pub fn has_custom_simple(&self) -> bool {
378 match self {
379 Self::Custom(_) => true,
380 Self::Array(a) => a.ty.has_custom_simple(),
381 Self::Tuple(t) => t.types.iter().any(Self::has_custom_simple),
382 Self::Mapping(m) => m.key.has_custom_simple() || m.value.has_custom_simple(),
383 Self::Bool(_)
384 | Self::Int(..)
385 | Self::Uint(..)
386 | Self::FixedBytes(..)
387 | Self::Address(..)
388 | Self::Function(_)
389 | Self::String(_)
390 | Self::Bytes(_) => false,
391 }
392 }
393
394 pub fn peel_arrays(&self) -> &Self {
396 let mut this = self;
397 while let Self::Array(array) = this {
398 this = &array.ty;
399 }
400 this
401 }
402
403 pub fn abi_name(&self) -> String {
406 let mut s = String::new();
407 self.abi_name_raw(&mut s);
408 s
409 }
410
411 pub fn abi_name_raw(&self, s: &mut String) {
414 match self {
415 Self::Custom(_) => s.push_str("tuple"),
416 Self::Array(array) => {
417 array.ty.abi_name_raw(s);
418 if let Some(size) = array.size() {
419 write!(s, "[{size}]").unwrap();
420 } else {
421 s.push_str("[]");
422 }
423 }
424 _ => write!(s, "{self}").unwrap(),
425 }
426 }
427
428 #[cfg(feature = "visit")]
430 pub fn visit(&self, f: impl FnMut(&Self)) {
431 use crate::Visit;
432 struct VisitType<F>(F);
433 impl<F: FnMut(&Type)> Visit<'_> for VisitType<F> {
434 fn visit_type(&mut self, ty: &Type) {
435 (self.0)(ty);
436 crate::visit::visit_type(self, ty);
437 }
438 fn visit_block(&mut self, _block: &crate::Block) {}
440 fn visit_expr(&mut self, _expr: &crate::Expr) {}
441 fn visit_stmt(&mut self, _stmt: &crate::Stmt) {}
442 fn visit_file(&mut self, _file: &crate::File) {}
443 fn visit_item(&mut self, _item: &crate::Item) {}
444 }
445 VisitType(f).visit_type(self);
446 }
447
448 #[cfg(feature = "visit-mut")]
450 pub fn visit_mut(&mut self, f: impl FnMut(&mut Self)) {
451 use crate::VisitMut;
452 struct VisitTypeMut<F>(F);
453 impl<F: FnMut(&mut Type)> VisitMut<'_> for VisitTypeMut<F> {
454 fn visit_type(&mut self, ty: &mut Type) {
455 (self.0)(ty);
456 crate::visit_mut::visit_type(self, ty);
457 }
458 fn visit_block(&mut self, _block: &mut crate::Block) {}
460 fn visit_expr(&mut self, _expr: &mut crate::Expr) {}
461 fn visit_stmt(&mut self, _stmt: &mut crate::Stmt) {}
462 fn visit_file(&mut self, _file: &mut crate::File) {}
463 fn visit_item(&mut self, _item: &mut crate::Item) {}
464 }
465 VisitTypeMut(f).visit_type(self);
466 }
467
468 #[inline]
470 fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
471 if input.peek(Paren) || input.peek(kw::tuple) {
472 input.parse().map(Self::Tuple)
473 } else if input.peek(kw::function) {
474 input.parse().map(Self::Function)
475 } else if input.peek(kw::mapping) {
476 input.parse().map(Self::Mapping)
477 } else if input.peek2(Token![.]) {
478 input.parse().map(Self::Custom)
479 } else if input.peek(Ident::peek_any) {
480 let ident = input.call(Ident::parse_any)?;
481 Self::parse_ident(ident)?.parse_payable(input)
482 } else {
483 Err(input.error(
484 "expected a Solidity type: \
485 `address`, `bool`, `string`, `bytesN`, `intN`, `uintN`, \
486 `tuple`, `function`, `mapping`, or a custom type name",
487 ))
488 }
489 }
490}
491
492fn write_opt(f: &mut fmt::Formatter<'_>, name: &str, size: Option<NonZeroU16>) -> fmt::Result {
493 f.write_str(name)?;
494 if let Some(size) = size {
495 write!(f, "{size}")?;
496 }
497 Ok(())
498}
499
500fn parse_size(s: &str, span: Span) -> Result<Option<Option<NonZeroU16>>> {
503 let opt = match s.parse::<NonZeroU16>() {
504 Ok(size) => Some(Some(size)),
505 Err(e) => match e.kind() {
506 IntErrorKind::Empty => Some(None),
508 IntErrorKind::InvalidDigit => None,
510 _ => return Err(Error::new(span, format_args!("invalid size: {e}"))),
512 },
513 };
514 Ok(opt)
515}