1use crate::{SolPath, Spanned, kw, sol_path};
2use proc_macro2::Span;
3use std::{
4 fmt,
5 fmt::Write,
6 hash::{Hash, Hasher},
7 num::{IntErrorKind, NonZeroU16},
8};
9use syn::{
10 Error, Ident, Result, Token,
11 ext::IdentExt,
12 parse::{Lookahead1, Parse, ParseStream},
13 token::{Bracket, Paren},
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(
265 span,
266 "uintX must be a multiple of 8 up to 256",
267 ));
268 }
269 Some(size) => Self::Uint(span, size),
270 }
271 } else {
272 Self::custom(ident)
273 }
274 }
275 };
276 Ok(ret)
277 }
278
279 pub fn parse_payable(mut self, input: ParseStream<'_>) -> Self {
282 if let Self::Address(_, opt @ None) = &mut self {
283 *opt = input.parse().unwrap();
284 }
285 self
286 }
287
288 pub fn is_abi_dynamic(&self) -> bool {
292 match self {
293 Self::Bool(_)
294 | Self::Int(..)
295 | Self::Uint(..)
296 | Self::FixedBytes(..)
297 | Self::Address(..)
298 | Self::Function(_) => false,
299
300 Self::String(_) | Self::Bytes(_) | Self::Custom(_) => true,
301
302 Self::Array(array) => array.is_abi_dynamic(),
303 Self::Tuple(tuple) => tuple.is_abi_dynamic(),
304
305 Self::Mapping(_) => true,
307 }
308 }
309
310 pub fn is_value_type(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
318 match self {
319 Self::Custom(custom) => custom_is_value_type(custom),
320 _ => self.is_value_type_simple(),
321 }
322 }
323
324 pub fn is_value_type_simple(&self) -> bool {
328 matches!(
329 self,
330 Self::Bool(_)
331 | Self::Int(..)
332 | Self::Uint(..)
333 | Self::FixedBytes(..)
334 | Self::Address(..)
335 | Self::Function(_)
336 )
337 }
338
339 pub const fn is_array(&self) -> bool {
340 matches!(self, Self::Array(_))
341 }
342
343 pub const fn is_tuple(&self) -> bool {
344 matches!(self, Self::Tuple(_))
345 }
346
347 pub const fn is_custom(&self) -> bool {
348 matches!(self, Self::Custom(_))
349 }
350
351 pub fn has_custom(&self) -> bool {
353 match self {
354 Self::Custom(_) => true,
355 Self::Array(a) => a.ty.has_custom(),
356 Self::Tuple(t) => t.types.iter().any(Self::has_custom),
357 Self::Function(f) => {
358 f.arguments.iter().any(|arg| arg.ty.has_custom())
359 || f.returns
360 .as_ref()
361 .is_some_and(|ret| ret.returns.iter().any(|arg| arg.ty.has_custom()))
362 }
363 Self::Mapping(m) => m.key.has_custom() || m.value.has_custom(),
364 Self::Bool(_)
365 | Self::Int(..)
366 | Self::Uint(..)
367 | Self::FixedBytes(..)
368 | Self::Address(..)
369 | Self::String(_)
370 | Self::Bytes(_) => false,
371 }
372 }
373
374 pub fn has_custom_simple(&self) -> bool {
377 match self {
378 Self::Custom(_) => true,
379 Self::Array(a) => a.ty.has_custom_simple(),
380 Self::Tuple(t) => t.types.iter().any(Self::has_custom_simple),
381 Self::Mapping(m) => m.key.has_custom_simple() || m.value.has_custom_simple(),
382 Self::Bool(_)
383 | Self::Int(..)
384 | Self::Uint(..)
385 | Self::FixedBytes(..)
386 | Self::Address(..)
387 | Self::Function(_)
388 | Self::String(_)
389 | Self::Bytes(_) => false,
390 }
391 }
392
393 pub fn peel_arrays(&self) -> &Self {
395 let mut this = self;
396 while let Self::Array(array) = this {
397 this = &array.ty;
398 }
399 this
400 }
401
402 pub fn abi_name(&self) -> String {
405 let mut s = String::new();
406 self.abi_name_raw(&mut s);
407 s
408 }
409
410 pub fn abi_name_raw(&self, s: &mut String) {
413 match self {
414 Self::Custom(_) => s.push_str("tuple"),
415 Self::Array(array) => {
416 array.ty.abi_name_raw(s);
417 if let Some(size) = array.size() {
418 write!(s, "[{size}]").unwrap();
419 } else {
420 s.push_str("[]");
421 }
422 }
423 _ => write!(s, "{self}").unwrap(),
424 }
425 }
426
427 #[cfg(feature = "visit")]
429 pub fn visit(&self, f: impl FnMut(&Self)) {
430 use crate::Visit;
431 struct VisitType<F>(F);
432 impl<F: FnMut(&Type)> Visit<'_> for VisitType<F> {
433 fn visit_type(&mut self, ty: &Type) {
434 (self.0)(ty);
435 crate::visit::visit_type(self, ty);
436 }
437 fn visit_block(&mut self, _block: &crate::Block) {}
439 fn visit_expr(&mut self, _expr: &crate::Expr) {}
440 fn visit_stmt(&mut self, _stmt: &crate::Stmt) {}
441 fn visit_file(&mut self, _file: &crate::File) {}
442 fn visit_item(&mut self, _item: &crate::Item) {}
443 }
444 VisitType(f).visit_type(self);
445 }
446
447 #[cfg(feature = "visit-mut")]
449 pub fn visit_mut(&mut self, f: impl FnMut(&mut Self)) {
450 use crate::VisitMut;
451 struct VisitTypeMut<F>(F);
452 impl<F: FnMut(&mut Type)> VisitMut<'_> for VisitTypeMut<F> {
453 fn visit_type(&mut self, ty: &mut Type) {
454 (self.0)(ty);
455 crate::visit_mut::visit_type(self, ty);
456 }
457 fn visit_block(&mut self, _block: &mut crate::Block) {}
459 fn visit_expr(&mut self, _expr: &mut crate::Expr) {}
460 fn visit_stmt(&mut self, _stmt: &mut crate::Stmt) {}
461 fn visit_file(&mut self, _file: &mut crate::File) {}
462 fn visit_item(&mut self, _item: &mut crate::Item) {}
463 }
464 VisitTypeMut(f).visit_type(self);
465 }
466
467 #[inline]
469 fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
470 if input.peek(Paren) || input.peek(kw::tuple) {
471 input.parse().map(Self::Tuple)
472 } else if input.peek(kw::function) {
473 input.parse().map(Self::Function)
474 } else if input.peek(kw::mapping) {
475 input.parse().map(Self::Mapping)
476 } else if input.peek2(Token![.]) {
477 input.parse().map(Self::Custom)
478 } else if input.peek(Ident::peek_any) {
479 let ident = input.call(Ident::parse_any)?;
480 Ok(Self::parse_ident(ident).parse_payable(input))
481 } else {
482 Err(input.error(
483 "expected a Solidity type: \
484 `address`, `bool`, `string`, `bytesN`, `intN`, `uintN`, \
485 `tuple`, `function`, `mapping`, or a custom type name",
486 ))
487 }
488 }
489}
490
491fn write_opt(f: &mut fmt::Formatter<'_>, name: &str, size: Option<NonZeroU16>) -> fmt::Result {
492 f.write_str(name)?;
493 if let Some(size) = size {
494 write!(f, "{size}")?;
495 }
496 Ok(())
497}
498
499fn parse_size(s: &str, span: Span) -> Result<Option<Option<NonZeroU16>>> {
502 let opt = match s.parse::<NonZeroU16>() {
503 Ok(size) => Some(Some(size)),
504 Err(e) => match e.kind() {
505 IntErrorKind::Empty => Some(None),
507 IntErrorKind::InvalidDigit => None,
509 _ => return Err(Error::new(span, format_args!("invalid size: {e}"))),
511 },
512 };
513 Ok(opt)
514}