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 #[deprecated = "use `is_value_type` instead"]
292 pub fn is_one_word(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
293 self.is_value_type(custom_is_value_type)
294 }
295
296 pub fn is_abi_dynamic(&self) -> bool {
300 match self {
301 Self::Bool(_)
302 | Self::Int(..)
303 | Self::Uint(..)
304 | Self::FixedBytes(..)
305 | Self::Address(..)
306 | Self::Function(_) => false,
307
308 Self::String(_) | Self::Bytes(_) | Self::Custom(_) => true,
309
310 Self::Array(array) => array.is_abi_dynamic(),
311 Self::Tuple(tuple) => tuple.is_abi_dynamic(),
312
313 Self::Mapping(_) => true,
315 }
316 }
317
318 pub fn is_value_type(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
326 match self {
327 Self::Custom(custom) => custom_is_value_type(custom),
328 _ => self.is_value_type_simple(),
329 }
330 }
331
332 pub fn is_value_type_simple(&self) -> bool {
336 matches!(
337 self,
338 Self::Bool(_)
339 | Self::Int(..)
340 | Self::Uint(..)
341 | Self::FixedBytes(..)
342 | Self::Address(..)
343 | Self::Function(_)
344 )
345 }
346
347 pub const fn is_array(&self) -> bool {
348 matches!(self, Self::Array(_))
349 }
350
351 pub const fn is_tuple(&self) -> bool {
352 matches!(self, Self::Tuple(_))
353 }
354
355 pub const fn is_custom(&self) -> bool {
356 matches!(self, Self::Custom(_))
357 }
358
359 pub fn has_custom(&self) -> bool {
361 match self {
362 Self::Custom(_) => true,
363 Self::Array(a) => a.ty.has_custom(),
364 Self::Tuple(t) => t.types.iter().any(Self::has_custom),
365 Self::Function(f) => {
366 f.arguments.iter().any(|arg| arg.ty.has_custom())
367 || f.returns
368 .as_ref()
369 .is_some_and(|ret| ret.returns.iter().any(|arg| arg.ty.has_custom()))
370 }
371 Self::Mapping(m) => m.key.has_custom() || m.value.has_custom(),
372 Self::Bool(_)
373 | Self::Int(..)
374 | Self::Uint(..)
375 | Self::FixedBytes(..)
376 | Self::Address(..)
377 | Self::String(_)
378 | Self::Bytes(_) => false,
379 }
380 }
381
382 pub fn has_custom_simple(&self) -> bool {
385 match self {
386 Self::Custom(_) => true,
387 Self::Array(a) => a.ty.has_custom_simple(),
388 Self::Tuple(t) => t.types.iter().any(Self::has_custom_simple),
389 Self::Mapping(m) => m.key.has_custom_simple() || m.value.has_custom_simple(),
390 Self::Bool(_)
391 | Self::Int(..)
392 | Self::Uint(..)
393 | Self::FixedBytes(..)
394 | Self::Address(..)
395 | Self::Function(_)
396 | Self::String(_)
397 | Self::Bytes(_) => false,
398 }
399 }
400
401 pub fn peel_arrays(&self) -> &Self {
403 let mut this = self;
404 while let Self::Array(array) = this {
405 this = &array.ty;
406 }
407 this
408 }
409
410 pub fn abi_name(&self) -> String {
413 let mut s = String::new();
414 self.abi_name_raw(&mut s);
415 s
416 }
417
418 pub fn abi_name_raw(&self, s: &mut String) {
421 match self {
422 Self::Custom(_) => s.push_str("tuple"),
423 Self::Array(array) => {
424 array.ty.abi_name_raw(s);
425 if let Some(size) = array.size() {
426 write!(s, "[{size}]").unwrap();
427 } else {
428 s.push_str("[]");
429 }
430 }
431 _ => write!(s, "{self}").unwrap(),
432 }
433 }
434
435 #[cfg(feature = "visit")]
437 pub fn visit(&self, f: impl FnMut(&Self)) {
438 use crate::Visit;
439 struct VisitType<F>(F);
440 impl<F: FnMut(&Type)> Visit<'_> for VisitType<F> {
441 fn visit_type(&mut self, ty: &Type) {
442 (self.0)(ty);
443 crate::visit::visit_type(self, ty);
444 }
445 fn visit_block(&mut self, _block: &crate::Block) {}
447 fn visit_expr(&mut self, _expr: &crate::Expr) {}
448 fn visit_stmt(&mut self, _stmt: &crate::Stmt) {}
449 fn visit_file(&mut self, _file: &crate::File) {}
450 fn visit_item(&mut self, _item: &crate::Item) {}
451 }
452 VisitType(f).visit_type(self);
453 }
454
455 #[cfg(feature = "visit-mut")]
457 pub fn visit_mut(&mut self, f: impl FnMut(&mut Self)) {
458 use crate::VisitMut;
459 struct VisitTypeMut<F>(F);
460 impl<F: FnMut(&mut Type)> VisitMut<'_> for VisitTypeMut<F> {
461 fn visit_type(&mut self, ty: &mut Type) {
462 (self.0)(ty);
463 crate::visit_mut::visit_type(self, ty);
464 }
465 fn visit_block(&mut self, _block: &mut crate::Block) {}
467 fn visit_expr(&mut self, _expr: &mut crate::Expr) {}
468 fn visit_stmt(&mut self, _stmt: &mut crate::Stmt) {}
469 fn visit_file(&mut self, _file: &mut crate::File) {}
470 fn visit_item(&mut self, _item: &mut crate::Item) {}
471 }
472 VisitTypeMut(f).visit_type(self);
473 }
474
475 #[inline]
477 fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
478 if input.peek(Paren) || input.peek(kw::tuple) {
479 input.parse().map(Self::Tuple)
480 } else if input.peek(kw::function) {
481 input.parse().map(Self::Function)
482 } else if input.peek(kw::mapping) {
483 input.parse().map(Self::Mapping)
484 } else if input.peek2(Token![.]) {
485 input.parse().map(Self::Custom)
486 } else if input.peek(Ident::peek_any) {
487 let ident = input.call(Ident::parse_any)?;
488 Ok(Self::parse_ident(ident).parse_payable(input))
489 } else {
490 Err(input.error(
491 "expected a Solidity type: \
492 `address`, `bool`, `string`, `bytesN`, `intN`, `uintN`, \
493 `tuple`, `function`, `mapping`, or a custom type name",
494 ))
495 }
496 }
497}
498
499fn write_opt(f: &mut fmt::Formatter<'_>, name: &str, size: Option<NonZeroU16>) -> fmt::Result {
500 f.write_str(name)?;
501 if let Some(size) = size {
502 write!(f, "{size}")?;
503 }
504 Ok(())
505}
506
507fn parse_size(s: &str, span: Span) -> Result<Option<Option<NonZeroU16>>> {
510 let opt = match s.parse::<NonZeroU16>() {
511 Ok(size) => Some(Some(size)),
512 Err(e) => match e.kind() {
513 IntErrorKind::Empty => Some(None),
515 IntErrorKind::InvalidDigit => None,
517 _ => return Err(Error::new(span, format_args!("invalid size: {e}"))),
519 },
520 };
521 Ok(opt)
522}