1use crate::utils::{self, ExprArray};
4use alloy_sol_macro_input::{ContainsSolAttrs, SolAttrs};
5use ast::{
6 EventParameter, File, Item, ItemError, ItemEvent, ItemFunction, Parameters, SolIdent, SolPath,
7 Spanned, Type, VariableDeclaration, Visit, VisitMut, visit_mut,
8};
9use indexmap::IndexMap;
10use proc_macro_error3::{abort, emit_error};
11use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
12use quote::{TokenStreamExt, format_ident, quote};
13use std::{
14 borrow::Borrow,
15 collections::HashMap,
16 fmt,
17 fmt::Write,
18 sync::atomic::{AtomicBool, Ordering},
19};
20use syn::{Attribute, Error, Result, ext::IdentExt, parse_quote};
21
22#[macro_use]
23mod macros;
24
25mod contract;
26mod r#enum;
27mod error;
28mod event;
29mod function;
30mod r#struct;
31mod ty;
32mod udt;
33mod var_def;
34
35#[cfg(feature = "json")]
36mod to_abi;
37
38const RESOLVE_LIMIT: usize = 128;
40
41pub fn expand(ast: File) -> Result<TokenStream> {
45 utils::pme_compat_result(|| ExpCtxt::new(&ast).expand())
46}
47
48pub fn expand_type(ty: &Type, crates: &ExternCrates) -> TokenStream {
50 utils::pme_compat(|| {
51 let dummy_file = File { attrs: Vec::new(), items: Vec::new() };
52 let mut cx = ExpCtxt::new(&dummy_file);
53 cx.crates = crates.clone();
54 cx.expand_type(ty)
55 })
56}
57
58#[derive(Debug, Clone)]
63pub struct NamespacedMap<T>(pub IndexMap<Option<SolIdent>, IndexMap<SolIdent, T>>);
64
65impl<T> Default for NamespacedMap<T> {
66 fn default() -> Self {
67 Self(Default::default())
68 }
69}
70
71impl<T> NamespacedMap<T> {
72 pub fn insert(&mut self, namespace: Option<SolIdent>, name: SolIdent, value: T) {
74 self.0.entry(namespace).or_default().insert(name, value);
75 }
76
77 pub fn resolve(&self, path: &SolPath, current_namespace: &Option<SolIdent>) -> Option<&T> {
79 self.resolve_entry(path, current_namespace).map(|(_, value)| value)
80 }
81
82 fn resolve_entry(
84 &self,
85 path: &SolPath,
86 current_namespace: &Option<SolIdent>,
87 ) -> Option<(&Option<SolIdent>, &T)> {
88 if path.len() == 2 {
90 self.get_entry_by_name_and_namespace(&Some(path.first().clone()), path.last())
91 } else {
92 self.get_entry_by_name_and_namespace(&None, path.last())
97 .or_else(|| self.get_entry_by_name_and_namespace(current_namespace, path.last()))
98 }
99 }
100
101 fn get_entry_by_name_and_namespace(
102 &self,
103 namespace: &Option<SolIdent>,
104 name: &SolIdent,
105 ) -> Option<(&Option<SolIdent>, &T)> {
106 let (namespace, values) = self.0.get_key_value(namespace)?;
107 values.get(name).map(|value| (namespace, value))
108 }
109}
110
111impl<T: Default> NamespacedMap<T> {
112 pub fn get_or_insert_default(&mut self, namespace: Option<SolIdent>, name: SolIdent) -> &mut T {
114 self.0.entry(namespace).or_default().entry(name).or_default()
115 }
116}
117
118#[derive(Debug)]
120pub struct ExpCtxt<'ast> {
121 all_items: NamespacedMap<&'ast Item>,
123 custom_types: NamespacedMap<Type>,
124
125 overloaded_items: NamespacedMap<Vec<OverloadedItem<'ast>>>,
127 overloads: IndexMap<Option<SolIdent>, IndexMap<String, String>>,
129
130 attrs: SolAttrs,
131 crates: ExternCrates,
132 ast: &'ast File,
133
134 current_namespace: Option<SolIdent>,
136}
137
138impl<'ast> ExpCtxt<'ast> {
140 fn new(ast: &'ast File) -> Self {
141 Self {
142 all_items: Default::default(),
143 custom_types: Default::default(),
144 overloaded_items: Default::default(),
145 overloads: IndexMap::new(),
146 attrs: SolAttrs::default(),
147 crates: ExternCrates::default(),
148 ast,
149 current_namespace: None,
150 }
151 }
152
153 fn with_namespace<O>(
155 &mut self,
156 namespace: Option<SolIdent>,
157 mut f: impl FnMut(&mut Self) -> O,
158 ) -> O {
159 let prev = std::mem::replace(&mut self.current_namespace, namespace);
160 let res = f(self);
161 self.current_namespace = prev;
162 res
163 }
164
165 fn expand(mut self) -> Result<TokenStream> {
166 let mut abort = false;
167 let mut tokens = TokenStream::new();
168
169 if let Err(e) = self.parse_file_attributes() {
170 tokens.extend(e.into_compile_error());
171 }
172
173 self.visit_file(self.ast);
174
175 if !self.all_items.0.is_empty() {
176 self.resolve_custom_types();
177 if self.mk_overloads_map().is_err() || self.check_selector_collisions().is_err() {
179 abort = true;
180 }
181 }
182
183 if abort {
184 return Ok(tokens);
185 }
186
187 for item in &self.ast.items {
188 let t = match self.expand_item(item) {
190 Ok(t) => t,
191 Err(e) => e.into_compile_error(),
192 };
193 tokens.extend(t);
194 }
195 Ok(tokens)
196 }
197
198 fn expand_item(&mut self, item: &Item) -> Result<TokenStream> {
199 match item {
200 Item::Contract(contract) => self.with_namespace(Some(contract.name.clone()), |this| {
201 contract::expand(this, contract)
202 }),
203 Item::Enum(enumm) => r#enum::expand(self, enumm),
204 Item::Error(error) => error::expand(self, error),
205 Item::Event(event) => event::expand(self, event),
206 Item::Function(function) => function::expand(self, function),
207 Item::Struct(strukt) => r#struct::expand(self, strukt),
208 Item::Udt(udt) => udt::expand(self, udt),
209 Item::Variable(var_def) => var_def::expand(self, var_def),
210 Item::Import(_) | Item::Pragma(_) | Item::Using(_) => Ok(TokenStream::new()),
211 }
212 }
213}
214
215impl ExpCtxt<'_> {
217 fn parse_file_attributes(&mut self) -> Result<()> {
218 let (attrs, others) = self.ast.split_attrs()?;
219 self.attrs = attrs;
220 self.crates.fill(&self.attrs);
221
222 let errs = others.iter().map(|attr| Error::new_spanned(attr, "unexpected attribute"));
223 utils::combine_errors(errs)
224 }
225
226 fn mk_types_map(&mut self) {
227 let mut map = std::mem::take(&mut self.custom_types);
228 for (namespace, items) in &self.all_items.0 {
229 for (name, item) in items {
230 let ty = match item {
231 Item::Contract(c) => c.as_type(),
232 Item::Enum(e) => e.as_type(),
233 Item::Struct(s) => s.as_type(),
234 Item::Udt(u) => u.ty.clone(),
235 _ => continue,
236 };
237
238 map.insert(namespace.clone(), name.clone(), ty);
239 }
240 }
241 self.custom_types = map;
242 }
243
244 fn resolve_custom_types(&mut self) {
245 struct Resolver<'a> {
248 map: &'a NamespacedMap<Type>,
249 cnt: usize,
250 namespace: Option<SolIdent>,
251 }
252 impl VisitMut<'_> for Resolver<'_> {
253 fn visit_type(&mut self, ty: &mut Type) {
254 if self.cnt >= RESOLVE_LIMIT {
255 return;
256 }
257 let prev_namespace = self.namespace.clone();
258 if let Type::Custom(name) = ty {
259 let Some(resolved) = self.map.resolve(name, &self.namespace) else {
260 return;
261 };
262 if name.len() == 2 {
264 self.namespace = Some(name.first().clone());
265 }
266 ty.clone_from(resolved);
267 self.cnt += 1;
268 }
269
270 visit_mut::visit_type(self, ty);
271
272 self.namespace = prev_namespace;
273 }
274 }
275
276 self.mk_types_map();
277 let map = self.custom_types.clone();
278 for (namespace, custom_types) in &mut self.custom_types.0 {
279 for ty in custom_types.values_mut() {
280 let mut resolver = Resolver { map: &map, cnt: 0, namespace: namespace.clone() };
281 resolver.visit_type(ty);
282 if resolver.cnt >= RESOLVE_LIMIT {
283 abort!(
284 ty.span(),
285 "failed to resolve types.\n\
286 This is likely due to an infinitely recursive type definition.\n\
287 If you believe this is a bug, please file an issue at \
288 https://github.com/alloy-rs/core/issues/new/choose"
289 );
290 }
291 }
292 }
293 }
294
295 fn check_selector_collisions(&mut self) -> std::result::Result<(), ()> {
297 #[derive(Clone, Copy)]
298 enum SelectorKind {
299 Function,
300 Error,
301 }
305
306 impl fmt::Display for SelectorKind {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 match self {
309 Self::Function => "function",
310 Self::Error => "error",
311 }
313 .fmt(f)
314 }
315 }
316
317 let mut result = Ok(());
318
319 let mut selectors = vec![HashMap::new(); 3];
320 for (namespace, items) in &self.all_items.clone().0 {
321 self.with_namespace(namespace.clone(), |this| {
322 selectors.iter_mut().for_each(|s| s.clear());
323 for (_, &item) in items {
324 let (kind, selector) = match item {
325 Item::Function(function) => {
326 (SelectorKind::Function, this.function_selector(function))
327 }
328 Item::Error(error) => (SelectorKind::Error, this.error_selector(error)),
329 _ => continue,
331 };
332 let selector: [u8; 4] = selector.array.try_into().unwrap();
333 if matches!(kind, SelectorKind::Error)
335 && (selector == [0, 0, 0, 0] || selector == [0xff, 0xff, 0xff, 0xff])
336 {
337 emit_error!(
338 item.span(),
339 "{kind} selector `{}` is reserved",
340 hex::encode_prefixed(selector),
341 );
342 result = Err(());
343 continue;
344 }
345 match selectors[kind as usize].entry(selector) {
346 std::collections::hash_map::Entry::Vacant(entry) => {
347 entry.insert(item);
348 }
349 std::collections::hash_map::Entry::Occupied(entry) => {
350 result = Err(());
351 let other = *entry.get();
352 emit_error!(
353 item.span(),
354 "{kind} selector `{}` collides with `{}`",
355 hex::encode_prefixed(selector),
356 other.name().unwrap();
357
358 note = other.span() => "other declaration is here";
359 );
360 }
361 }
362 }
363 })
364 }
365
366 result
367 }
368
369 fn mk_overloads_map(&mut self) -> std::result::Result<(), ()> {
370 let mut overloads_map = std::mem::take(&mut self.overloads);
371
372 for namespace in &self.overloaded_items.0.keys().cloned().collect::<Vec<_>>() {
373 let mut failed = false;
374
375 self.with_namespace(namespace.clone(), |this| {
376 let overloaded_items = this.overloaded_items.0.get(namespace).unwrap();
377 let all_orig_names: Vec<_> =
378 overloaded_items.values().flatten().filter_map(|f| f.name()).collect();
379
380 for functions in overloaded_items.values().filter(|fs| fs.len() >= 2) {
381 for (i, &a) in functions.iter().enumerate() {
383 for &b in functions.iter().skip(i + 1) {
384 if a.eq_by_types(b) {
385 failed = true;
386 emit_error!(
387 a.span(),
388 "{} with same name and parameter types defined twice",
389 a.desc();
390
391 note = b.span() => "other declaration is here";
392 );
393 }
394 }
395 }
396
397 for (i, &item) in functions.iter().enumerate() {
398 let Some(old_name) = item.name() else {
399 continue;
400 };
401 let new_name = format!("{old_name}_{i}");
402 if let Some(other) = all_orig_names.iter().find(|x| x.0 == new_name) {
403 failed = true;
404 emit_error!(
405 old_name.span(),
406 "{} `{old_name}` is overloaded, \
407 but the generated name `{new_name}` is already in use",
408 item.desc();
409
410 note = other.span() => "other declaration is here";
411 )
412 }
413
414 overloads_map
415 .entry(namespace.clone())
416 .or_default()
417 .insert(item.signature(this), new_name);
418 }
419 }
420 });
421
422 if failed {
423 return Err(());
424 }
425 }
426
427 self.overloads = overloads_map;
428 Ok(())
429 }
430}
431
432impl<'ast> Visit<'ast> for ExpCtxt<'ast> {
433 fn visit_item(&mut self, item: &'ast Item) {
434 if let Some(name) = item.name() {
435 self.all_items.insert(self.current_namespace.clone(), name.clone(), item)
436 }
437
438 if let Item::Contract(contract) = item {
439 self.with_namespace(Some(contract.name.clone()), |this| {
440 ast::visit::visit_item(this, item);
441 });
442 } else {
443 ast::visit::visit_item(self, item);
444 }
445 }
446
447 fn visit_item_function(&mut self, function: &'ast ItemFunction) {
448 if let Some(name) = &function.name {
449 self.overloaded_items
450 .get_or_insert_default(self.current_namespace.clone(), name.clone())
451 .push(OverloadedItem::Function(function));
452 }
453 ast::visit::visit_item_function(self, function);
454 }
455
456 fn visit_item_event(&mut self, event: &'ast ItemEvent) {
457 self.overloaded_items
458 .get_or_insert_default(self.current_namespace.clone(), event.name.clone())
459 .push(OverloadedItem::Event(event));
460 ast::visit::visit_item_event(self, event);
461 }
462
463 fn visit_item_error(&mut self, error: &'ast ItemError) {
464 self.overloaded_items
465 .get_or_insert_default(self.current_namespace.clone(), error.name.clone())
466 .push(OverloadedItem::Error(error));
467 ast::visit::visit_item_error(self, error);
468 }
469}
470
471#[derive(Clone, Copy, Debug)]
472enum OverloadedItem<'a> {
473 Function(&'a ItemFunction),
474 Event(&'a ItemEvent),
475 Error(&'a ItemError),
476}
477
478impl<'ast> From<&'ast ItemFunction> for OverloadedItem<'ast> {
479 fn from(f: &'ast ItemFunction) -> Self {
480 Self::Function(f)
481 }
482}
483
484impl<'ast> From<&'ast ItemEvent> for OverloadedItem<'ast> {
485 fn from(e: &'ast ItemEvent) -> Self {
486 Self::Event(e)
487 }
488}
489
490impl<'ast> From<&'ast ItemError> for OverloadedItem<'ast> {
491 fn from(e: &'ast ItemError) -> Self {
492 Self::Error(e)
493 }
494}
495
496impl<'a> OverloadedItem<'a> {
497 fn name(self) -> Option<&'a SolIdent> {
498 match self {
499 Self::Function(f) => f.name.as_ref(),
500 Self::Event(e) => Some(&e.name),
501 Self::Error(e) => Some(&e.name),
502 }
503 }
504
505 fn desc(&self) -> &'static str {
506 match self {
507 Self::Function(_) => "function",
508 Self::Event(_) => "event",
509 Self::Error(_) => "error",
510 }
511 }
512
513 fn eq_by_types(self, other: Self) -> bool {
514 match (self, other) {
515 (Self::Function(a), Self::Function(b)) => a.parameters.types().eq(b.parameters.types()),
516 (Self::Event(a), Self::Event(b)) => a.param_types().eq(b.param_types()),
517 (Self::Error(a), Self::Error(b)) => a.parameters.types().eq(b.parameters.types()),
518 _ => false,
519 }
520 }
521
522 fn span(self) -> Span {
523 match self {
524 Self::Function(f) => f.span(),
525 Self::Event(e) => e.span(),
526 Self::Error(e) => e.span(),
527 }
528 }
529
530 fn signature(self, cx: &ExpCtxt<'a>) -> String {
531 match self {
532 Self::Function(f) => cx.function_signature(f),
533 Self::Event(e) => cx.event_signature(e),
534 Self::Error(e) => cx.error_signature(e),
535 }
536 }
537}
538
539impl<'ast> ExpCtxt<'ast> {
541 #[allow(dead_code)]
542 fn item(&self, name: &SolPath) -> &Item {
543 match self.try_item(name) {
544 Some(item) => item,
545 None => abort!(name.span(), "unresolved item: {}", name),
546 }
547 }
548
549 fn try_item(&self, name: &SolPath) -> Option<&Item> {
550 self.try_item_in_namespace(name, &self.current_namespace).map(|(_, item)| item)
551 }
552
553 fn try_item_in_namespace(
554 &self,
555 name: &SolPath,
556 current_namespace: &Option<SolIdent>,
557 ) -> Option<(&Option<SolIdent>, &Item)> {
558 self.all_items
559 .resolve_entry(name, current_namespace)
560 .map(|(namespace, item)| (namespace, *item))
561 }
562
563 fn custom_type(&self, name: &SolPath) -> &Type {
564 match self.try_custom_type(name) {
565 Some(item) => item,
566 None => abort!(name.span(), "unresolved custom type: {}", name),
567 }
568 }
569
570 fn try_custom_type(&self, name: &SolPath) -> Option<&Type> {
571 self.custom_types.resolve(name, &self.current_namespace).inspect(|&ty| {
572 if ty.is_custom() {
573 abort!(
574 ty.span(),
575 "unresolved custom type in map";
576 note = name.span() => "name span";
577 );
578 }
579 })
580 }
581
582 fn indexed_as_hash(&self, param: &EventParameter) -> bool {
583 param.indexed_as_hash(self.custom_is_value_type())
584 }
585
586 fn expand_event_param_type(&self, param: &EventParameter) -> TokenStream {
589 if self.indexed_as_hash(param) {
590 let bytes32 =
591 Type::FixedBytes(param.ty.span(), core::num::NonZeroU16::new(32).unwrap());
592 self.expand_rust_type(&bytes32)
593 } else {
594 self.expand_rust_type(¶m.ty)
595 }
596 }
597
598 fn custom_is_value_type(&self) -> impl Fn(&SolPath) -> bool + '_ {
599 move |ty| self.custom_type(ty).is_value_type(self.custom_is_value_type())
600 }
601
602 fn function_name(&self, function: &ItemFunction) -> SolIdent {
604 self.overloaded_name(function.into())
605 }
606
607 fn overloaded_name(&self, item: OverloadedItem<'ast>) -> SolIdent {
611 let original_ident = item.name().expect("item has no name");
612 let sig = item.signature(self);
613 match self.overloads.get(&self.current_namespace).and_then(|m| m.get(&sig)) {
614 Some(name) => SolIdent::new_spanned(name, original_ident.span()),
615 None => original_ident.clone(),
616 }
617 }
618
619 fn call_name(&self, function: &ItemFunction) -> Ident {
621 self.raw_call_name(&self.function_name(function).0)
622 }
623
624 fn raw_call_name(&self, function_name: &Ident) -> Ident {
626 let new_ident = format!("{}Call", function_name.unraw());
629 Ident::new(&new_ident, function_name.span())
630 }
631
632 fn return_name(&self, function: &ItemFunction) -> Ident {
634 self.raw_return_name(&self.function_name(function).0)
635 }
636
637 fn raw_return_name(&self, function_name: &Ident) -> Ident {
639 let new_ident = format!("{}Return", function_name.unraw());
642 Ident::new(&new_ident, function_name.span())
643 }
644
645 fn function_signature(&self, function: &ItemFunction) -> String {
646 self.signature(function.name().as_string(), &function.parameters)
647 }
648
649 fn function_selector(&self, function: &ItemFunction) -> ExprArray<u8> {
650 utils::selector(self.function_signature(function)).with_span(function.span())
651 }
652
653 fn error_signature(&self, error: &ItemError) -> String {
654 self.signature(error.name.as_string(), &error.parameters)
655 }
656
657 fn error_selector(&self, error: &ItemError) -> ExprArray<u8> {
658 utils::selector(self.error_signature(error)).with_span(error.span())
659 }
660
661 fn event_signature(&self, event: &ItemEvent) -> String {
662 self.signature(event.name.as_string(), &event.params())
663 }
664
665 fn event_selector(&self, event: &ItemEvent) -> ExprArray<u8> {
666 utils::event_selector(self.event_signature(event)).with_span(event.span())
667 }
668
669 fn signature<'a, I: IntoIterator<Item = &'a VariableDeclaration>>(
671 &self,
672 mut name: String,
673 params: I,
674 ) -> String {
675 name.push('(');
676 let mut first = true;
677 for param in params {
678 if !first {
679 name.push(',');
680 }
681 write!(name, "{}", ty::TypePrinter::new(self, ¶m.ty)).unwrap();
682 first = false;
683 }
684 name.push(')');
685 name
686 }
687
688 fn derives<'a, I>(&self, attrs: &mut Vec<Attribute>, params: I, derive_default: bool)
710 where
711 I: IntoIterator<Item = &'a VariableDeclaration>,
712 {
713 self.type_derives(attrs, params.into_iter().map(|p| &p.ty), derive_default);
714 }
715
716 fn type_derives<T, I>(&self, attrs: &mut Vec<Attribute>, types: I, mut derive_default: bool)
718 where
719 I: IntoIterator<Item = T>,
720 T: Borrow<Type>,
721 {
722 if let Some(extra) = &self.attrs.extra_derives {
723 if !extra.is_empty() {
724 attrs.push(parse_quote! { #[derive(#(#extra),*)] });
725 }
726 }
727
728 let Some(true) = self.attrs.all_derives else {
729 return;
730 };
731
732 let mut derives = Vec::with_capacity(5);
733 let mut derive_others = true;
734 for ty in types {
735 let ty = ty.borrow();
736 derive_default = derive_default && self.can_derive_default(ty);
737 derive_others = derive_others && self.can_derive_builtin_traits(ty);
738 }
739 if derive_default {
740 derives.push("Default");
741 }
742 if derive_others {
743 derives.extend(["Debug", "PartialEq", "Eq", "Hash"]);
744 }
745 let derives = derives.iter().map(|s| Ident::new(s, Span::call_site()));
746 attrs.push(parse_quote! { #[derive(#(#derives), *)] });
747 }
748
749 fn enum_derives(&self, attrs: &mut Vec<Attribute>, can_derive_builtin: bool) {
758 if let Some(extra) = &self.attrs.extra_derives {
759 if !extra.is_empty() {
760 attrs.push(parse_quote! { #[derive(#(#extra),*)] });
761 }
762 }
763
764 if self.attrs.all_derives == Some(true) && can_derive_builtin {
765 attrs.push(parse_quote! { #[derive(Debug, PartialEq, Eq, Hash)] });
766 }
767 }
768
769 fn assert_resolved<'a, I>(&self, params: I) -> Result<()>
774 where
775 I: IntoIterator<Item = &'a VariableDeclaration>,
776 {
777 let mut errored = false;
778 for param in params {
779 param.ty.visit(|ty| {
780 if let Type::Custom(name) = ty {
781 if self.try_custom_type(name).is_none() {
782 let note = (!errored).then(|| {
783 errored = true;
784 "Custom types must be declared inside of the same scope they are referenced in,\n\
785 or \"imported\" as a UDT with `type ... is (...);`"
786 });
787 emit_error!(name.span(), "unresolved type"; help =? note);
788 }
789 }
790 });
791 }
792 Ok(())
793 }
794}
795
796#[derive(Clone, Debug)]
801pub struct ExternCrates {
802 pub sol_types: syn::Path,
804 pub contract: syn::Path,
806}
807
808impl Default for ExternCrates {
809 fn default() -> Self {
810 Self {
811 sol_types: parse_quote!(::alloy_sol_types),
812 contract: parse_quote!(::alloy_contract),
813 }
814 }
815}
816
817impl ExternCrates {
818 pub fn fill(&mut self, attrs: &SolAttrs) {
820 if let Some(sol_types) = &attrs.alloy_sol_types {
821 self.sol_types = sol_types.clone();
822 }
823 if let Some(alloy_contract) = &attrs.alloy_contract {
824 self.contract = alloy_contract.clone();
825 }
826 }
827}
828
829fn expand_fields<'a, P>(
833 params: &'a Parameters<P>,
834 cx: &'a ExpCtxt<'_>,
835) -> impl Iterator<Item = TokenStream> + 'a {
836 params.iter().enumerate().map(|(i, var)| {
837 let name = anon_name((i, var.name.as_ref()));
838 let ty = cx.expand_rust_type(&var.ty);
839 let attrs = &var.attrs;
840 quote! {
841 #(#attrs)*
842 #[allow(missing_docs)]
843 pub #name: #ty
844 }
845 })
846}
847
848#[inline]
850pub fn generate_name(i: usize) -> Ident {
851 format_ident!("_{i}")
852}
853
854pub fn anon_name<T: Into<Ident> + Clone>((i, name): (usize, Option<&T>)) -> Ident {
856 match name {
857 Some(name) => name.clone().into(),
858 None => generate_name(i),
859 }
860}
861
862enum FieldKind {
864 Original,
880 Deconstruct,
898}
899
900impl FieldKind {
901 fn is_deconstruct(&self) -> bool {
903 matches!(self, Self::Deconstruct)
904 }
905}
906
907fn expand_from_into_tuples<P>(
909 name: &Ident,
910 fields: &Parameters<P>,
911 cx: &ExpCtxt<'_>,
912 field_kind: FieldKind,
913) -> TokenStream {
914 let names = fields.names().enumerate().map(anon_name);
915
916 let names2 = names.clone();
917 let idxs = (0..fields.len()).map(syn::Index::from);
918
919 let (sol_tuple, rust_tuple) = expand_tuple_types(fields.types(), cx);
920
921 let (from_sol_type, from_rust_tuple) = if fields.is_empty() && field_kind.is_deconstruct() {
922 (quote!(()), quote!(Self))
923 } else if fields.len() == 1 && fields[0].name.is_none() && field_kind.is_deconstruct() {
924 let idxs2 = (0..fields.len()).map(syn::Index::from);
925 (quote!((#(value.#idxs),*,)), quote!(Self(#(tuple.#idxs2),*)))
926 } else {
927 (quote!((#(value.#names,)*)), quote!(Self { #(#names2: tuple.#idxs),* }))
928 };
929
930 quote! {
931 #[doc(hidden)]
932 #[allow(dead_code)]
933 type UnderlyingSolTuple<'a> = #sol_tuple;
934 #[doc(hidden)]
935 type UnderlyingRustTuple<'a> = #rust_tuple;
936
937 #[cfg(test)]
938 #[allow(dead_code, unreachable_patterns)]
939 fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
940 match _t {
941 alloy_sol_types::private::AssertTypeEq::<<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType>(_) => {}
942 }
943 }
944
945 #[automatically_derived]
946 #[doc(hidden)]
947 impl ::core::convert::From<#name> for UnderlyingRustTuple<'_> {
948 fn from(value: #name) -> Self {
949 #from_sol_type
950 }
951 }
952
953 #[automatically_derived]
954 #[doc(hidden)]
955 impl ::core::convert::From<UnderlyingRustTuple<'_>> for #name {
956 fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
957 #from_rust_tuple
958 }
959 }
960 }
961}
962
963fn expand_tuple_types<'a, I: IntoIterator<Item = &'a Type>>(
965 types: I,
966 cx: &ExpCtxt<'_>,
967) -> (TokenStream, TokenStream) {
968 let mut sol = TokenStream::new();
969 let mut rust = TokenStream::new();
970 let comma = Punct::new(',', Spacing::Alone);
971 for ty in types {
972 cx.expand_type_to(ty, &mut sol);
973 sol.append(comma.clone());
974
975 cx.expand_rust_type_to(ty, &mut rust);
976 rust.append(comma.clone());
977 }
978 let wrap_in_parens =
979 |stream| TokenStream::from(TokenTree::Group(Group::new(Delimiter::Parenthesis, stream)));
980 (wrap_in_parens(sol), wrap_in_parens(rust))
981}
982
983fn expand_tokenize<P>(
985 params: &Parameters<P>,
986 cx: &ExpCtxt<'_>,
987 field_kind: FieldKind,
988) -> TokenStream {
989 tokenize_(
990 params.iter().enumerate().map(|(i, p)| (i, &p.ty, p.name.as_ref())),
991 cx,
992 params.len(),
993 field_kind,
994 )
995}
996
997fn expand_event_tokenize<'a>(
999 params: impl IntoIterator<Item = &'a EventParameter>,
1000 cx: &ExpCtxt<'_>,
1001 params_len: usize,
1002 field_kind: FieldKind,
1003) -> TokenStream {
1004 tokenize_(
1005 params
1006 .into_iter()
1007 .enumerate()
1008 .filter(|(_, p)| !p.is_indexed())
1009 .map(|(i, p)| (i, &p.ty, p.name.as_ref())),
1010 cx,
1011 params_len,
1012 field_kind,
1013 )
1014}
1015
1016fn tokenize_<'a>(
1017 iter: impl Iterator<Item = (usize, &'a Type, Option<&'a SolIdent>)>,
1018 cx: &'a ExpCtxt<'_>,
1019 params_len: usize,
1020 field_kind: FieldKind,
1021) -> TokenStream {
1022 let statements = iter.into_iter().map(|(i, ty, name)| {
1023 let ty = cx.expand_type(ty);
1024 if params_len == 1 && name.is_none() && field_kind.is_deconstruct() {
1025 quote! {
1026 <#ty as alloy_sol_types::SolType>::tokenize(&self.0)
1027 }
1028 } else {
1029 let name = name.cloned().unwrap_or_else(|| generate_name(i).into());
1030 quote! {
1031 <#ty as alloy_sol_types::SolType>::tokenize(&self.#name)
1032 }
1033 }
1034 });
1035 quote! {
1036 (#(#statements,)*)
1037 }
1038}
1039
1040#[allow(dead_code)]
1041fn emit_json_error() {
1042 static EMITTED: AtomicBool = AtomicBool::new(false);
1043 if !EMITTED.swap(true, Ordering::Relaxed) {
1044 emit_error!(
1045 Span::call_site(),
1046 "the `#[sol(abi)]` attribute requires the `\"json\"` feature"
1047 );
1048 }
1049}