1use crate::utils::{self, ExprArray};
4use alloy_sol_macro_input::{ContainsSolAttrs, SolAttrs};
5use ast::{
6 visit_mut, EventParameter, File, Item, ItemError, ItemEvent, ItemFunction, Parameters,
7 SolIdent, SolPath, Spanned, Type, VariableDeclaration, Visit, VisitMut,
8};
9use indexmap::IndexMap;
10use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
11use proc_macro_error2::{abort, emit_error};
12use quote::{format_ident, quote, TokenStreamExt};
13use std::{
14 borrow::Borrow,
15 collections::HashMap,
16 fmt,
17 fmt::Write,
18 sync::atomic::{AtomicBool, Ordering},
19};
20use syn::{ext::IdentExt, parse_quote, Attribute, Error, Result};
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 if path.len() == 2 {
81 self.get_by_name_and_namespace(&Some(path.first().clone()), path.last())
82 } else {
83 self.get_by_name_and_namespace(&None, path.last())
88 .or_else(|| self.get_by_name_and_namespace(current_namespace, path.last()))
89 }
90 }
91
92 fn get_by_name_and_namespace(
93 &self,
94 namespace: &Option<SolIdent>,
95 name: &SolIdent,
96 ) -> Option<&T> {
97 self.0.get(namespace).and_then(|vals| vals.get(name))
98 }
99}
100
101impl<T: Default> NamespacedMap<T> {
102 pub fn get_or_insert_default(&mut self, namespace: Option<SolIdent>, name: SolIdent) -> &mut T {
104 self.0.entry(namespace).or_default().entry(name).or_default()
105 }
106}
107
108#[derive(Debug)]
110pub struct ExpCtxt<'ast> {
111 all_items: NamespacedMap<&'ast Item>,
113 custom_types: NamespacedMap<Type>,
114
115 overloaded_items: NamespacedMap<Vec<OverloadedItem<'ast>>>,
117 overloads: IndexMap<Option<SolIdent>, IndexMap<String, String>>,
119
120 attrs: SolAttrs,
121 crates: ExternCrates,
122 ast: &'ast File,
123
124 current_namespace: Option<SolIdent>,
126}
127
128impl<'ast> ExpCtxt<'ast> {
130 fn new(ast: &'ast File) -> Self {
131 Self {
132 all_items: Default::default(),
133 custom_types: Default::default(),
134 overloaded_items: Default::default(),
135 overloads: IndexMap::new(),
136 attrs: SolAttrs::default(),
137 crates: ExternCrates::default(),
138 ast,
139 current_namespace: None,
140 }
141 }
142
143 fn with_namespace<O>(
145 &mut self,
146 namespace: Option<SolIdent>,
147 mut f: impl FnMut(&mut Self) -> O,
148 ) -> O {
149 let prev = std::mem::replace(&mut self.current_namespace, namespace);
150 let res = f(self);
151 self.current_namespace = prev;
152 res
153 }
154
155 fn expand(mut self) -> Result<TokenStream> {
156 let mut abort = false;
157 let mut tokens = TokenStream::new();
158
159 if let Err(e) = self.parse_file_attributes() {
160 tokens.extend(e.into_compile_error());
161 }
162
163 self.visit_file(self.ast);
164
165 if !self.all_items.0.is_empty() {
166 self.resolve_custom_types();
167 if self.mk_overloads_map().is_err() || self.check_selector_collisions().is_err() {
169 abort = true;
170 }
171 }
172
173 if abort {
174 return Ok(tokens);
175 }
176
177 for item in &self.ast.items {
178 let t = match self.expand_item(item) {
180 Ok(t) => t,
181 Err(e) => e.into_compile_error(),
182 };
183 tokens.extend(t);
184 }
185 Ok(tokens)
186 }
187
188 fn expand_item(&mut self, item: &Item) -> Result<TokenStream> {
189 match item {
190 Item::Contract(contract) => self.with_namespace(Some(contract.name.clone()), |this| {
191 contract::expand(this, contract)
192 }),
193 Item::Enum(enumm) => r#enum::expand(self, enumm),
194 Item::Error(error) => error::expand(self, error),
195 Item::Event(event) => event::expand(self, event),
196 Item::Function(function) => function::expand(self, function),
197 Item::Struct(strukt) => r#struct::expand(self, strukt),
198 Item::Udt(udt) => udt::expand(self, udt),
199 Item::Variable(var_def) => var_def::expand(self, var_def),
200 Item::Import(_) | Item::Pragma(_) | Item::Using(_) => Ok(TokenStream::new()),
201 }
202 }
203}
204
205impl ExpCtxt<'_> {
207 fn parse_file_attributes(&mut self) -> Result<()> {
208 let (attrs, others) = self.ast.split_attrs()?;
209 self.attrs = attrs;
210 self.crates.fill(&self.attrs);
211
212 let errs = others.iter().map(|attr| Error::new_spanned(attr, "unexpected attribute"));
213 utils::combine_errors(errs)
214 }
215
216 fn mk_types_map(&mut self) {
217 let mut map = std::mem::take(&mut self.custom_types);
218 for (namespace, items) in &self.all_items.0 {
219 for (name, item) in items {
220 let ty = match item {
221 Item::Contract(c) => c.as_type(),
222 Item::Enum(e) => e.as_type(),
223 Item::Struct(s) => s.as_type(),
224 Item::Udt(u) => u.ty.clone(),
225 _ => continue,
226 };
227
228 map.insert(namespace.clone(), name.clone(), ty);
229 }
230 }
231 self.custom_types = map;
232 }
233
234 fn resolve_custom_types(&mut self) {
235 struct Resolver<'a> {
238 map: &'a NamespacedMap<Type>,
239 cnt: usize,
240 namespace: Option<SolIdent>,
241 }
242 impl VisitMut<'_> for Resolver<'_> {
243 fn visit_type(&mut self, ty: &mut Type) {
244 if self.cnt >= RESOLVE_LIMIT {
245 return;
246 }
247 let prev_namespace = self.namespace.clone();
248 if let Type::Custom(name) = ty {
249 let Some(resolved) = self.map.resolve(name, &self.namespace) else {
250 return;
251 };
252 if name.len() == 2 {
254 self.namespace = Some(name.first().clone());
255 }
256 ty.clone_from(resolved);
257 self.cnt += 1;
258 }
259
260 visit_mut::visit_type(self, ty);
261
262 self.namespace = prev_namespace;
263 }
264 }
265
266 self.mk_types_map();
267 let map = self.custom_types.clone();
268 for (namespace, custom_types) in &mut self.custom_types.0 {
269 for ty in custom_types.values_mut() {
270 let mut resolver = Resolver { map: &map, cnt: 0, namespace: namespace.clone() };
271 resolver.visit_type(ty);
272 if resolver.cnt >= RESOLVE_LIMIT {
273 abort!(
274 ty.span(),
275 "failed to resolve types.\n\
276 This is likely due to an infinitely recursive type definition.\n\
277 If you believe this is a bug, please file an issue at \
278 https://github.com/alloy-rs/core/issues/new/choose"
279 );
280 }
281 }
282 }
283 }
284
285 fn check_selector_collisions(&mut self) -> std::result::Result<(), ()> {
287 #[derive(Clone, Copy)]
288 enum SelectorKind {
289 Function,
290 Error,
291 }
295
296 impl fmt::Display for SelectorKind {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 match self {
299 Self::Function => "function",
300 Self::Error => "error",
301 }
303 .fmt(f)
304 }
305 }
306
307 let mut result = Ok(());
308
309 let mut selectors = vec![HashMap::new(); 3];
310 for (namespace, items) in &self.all_items.clone().0 {
311 self.with_namespace(namespace.clone(), |this| {
312 selectors.iter_mut().for_each(|s| s.clear());
313 for (_, &item) in items {
314 let (kind, selector) = match item {
315 Item::Function(function) => {
316 (SelectorKind::Function, this.function_selector(function))
317 }
318 Item::Error(error) => (SelectorKind::Error, this.error_selector(error)),
319 _ => continue,
321 };
322 let selector: [u8; 4] = selector.array.try_into().unwrap();
323 if matches!(kind, SelectorKind::Error)
325 && (selector == [0, 0, 0, 0] || selector == [0xff, 0xff, 0xff, 0xff])
326 {
327 emit_error!(
328 item.span(),
329 "{kind} selector `{}` is reserved",
330 hex::encode_prefixed(selector),
331 );
332 result = Err(());
333 continue;
334 }
335 match selectors[kind as usize].entry(selector) {
336 std::collections::hash_map::Entry::Vacant(entry) => {
337 entry.insert(item);
338 }
339 std::collections::hash_map::Entry::Occupied(entry) => {
340 result = Err(());
341 let other = *entry.get();
342 emit_error!(
343 item.span(),
344 "{kind} selector `{}` collides with `{}`",
345 hex::encode_prefixed(selector),
346 other.name().unwrap();
347
348 note = other.span() => "other declaration is here";
349 );
350 }
351 }
352 }
353 })
354 }
355
356 result
357 }
358
359 fn mk_overloads_map(&mut self) -> std::result::Result<(), ()> {
360 let mut overloads_map = std::mem::take(&mut self.overloads);
361
362 for namespace in &self.overloaded_items.0.keys().cloned().collect::<Vec<_>>() {
363 let mut failed = false;
364
365 self.with_namespace(namespace.clone(), |this| {
366 let overloaded_items = this.overloaded_items.0.get(namespace).unwrap();
367 let all_orig_names: Vec<_> =
368 overloaded_items.values().flatten().filter_map(|f| f.name()).collect();
369
370 for functions in overloaded_items.values().filter(|fs| fs.len() >= 2) {
371 for (i, &a) in functions.iter().enumerate() {
373 for &b in functions.iter().skip(i + 1) {
374 if a.eq_by_types(b) {
375 failed = true;
376 emit_error!(
377 a.span(),
378 "{} with same name and parameter types defined twice",
379 a.desc();
380
381 note = b.span() => "other declaration is here";
382 );
383 }
384 }
385 }
386
387 for (i, &item) in functions.iter().enumerate() {
388 let Some(old_name) = item.name() else {
389 continue;
390 };
391 let new_name = format!("{old_name}_{i}");
392 if let Some(other) = all_orig_names.iter().find(|x| x.0 == new_name) {
393 failed = true;
394 emit_error!(
395 old_name.span(),
396 "{} `{old_name}` is overloaded, \
397 but the generated name `{new_name}` is already in use",
398 item.desc();
399
400 note = other.span() => "other declaration is here";
401 )
402 }
403
404 overloads_map
405 .entry(namespace.clone())
406 .or_default()
407 .insert(item.signature(this), new_name);
408 }
409 }
410 });
411
412 if failed {
413 return Err(());
414 }
415 }
416
417 self.overloads = overloads_map;
418 Ok(())
419 }
420}
421
422impl<'ast> Visit<'ast> for ExpCtxt<'ast> {
423 fn visit_item(&mut self, item: &'ast Item) {
424 if let Some(name) = item.name() {
425 self.all_items.insert(self.current_namespace.clone(), name.clone(), item)
426 }
427
428 if let Item::Contract(contract) = item {
429 self.with_namespace(Some(contract.name.clone()), |this| {
430 ast::visit::visit_item(this, item);
431 });
432 } else {
433 ast::visit::visit_item(self, item);
434 }
435 }
436
437 fn visit_item_function(&mut self, function: &'ast ItemFunction) {
438 if let Some(name) = &function.name {
439 self.overloaded_items
440 .get_or_insert_default(self.current_namespace.clone(), name.clone())
441 .push(OverloadedItem::Function(function));
442 }
443 ast::visit::visit_item_function(self, function);
444 }
445
446 fn visit_item_event(&mut self, event: &'ast ItemEvent) {
447 self.overloaded_items
448 .get_or_insert_default(self.current_namespace.clone(), event.name.clone())
449 .push(OverloadedItem::Event(event));
450 ast::visit::visit_item_event(self, event);
451 }
452
453 fn visit_item_error(&mut self, error: &'ast ItemError) {
454 self.overloaded_items
455 .get_or_insert_default(self.current_namespace.clone(), error.name.clone())
456 .push(OverloadedItem::Error(error));
457 ast::visit::visit_item_error(self, error);
458 }
459}
460
461#[derive(Clone, Copy, Debug)]
462enum OverloadedItem<'a> {
463 Function(&'a ItemFunction),
464 Event(&'a ItemEvent),
465 Error(&'a ItemError),
466}
467
468impl<'ast> From<&'ast ItemFunction> for OverloadedItem<'ast> {
469 fn from(f: &'ast ItemFunction) -> Self {
470 Self::Function(f)
471 }
472}
473
474impl<'ast> From<&'ast ItemEvent> for OverloadedItem<'ast> {
475 fn from(e: &'ast ItemEvent) -> Self {
476 Self::Event(e)
477 }
478}
479
480impl<'ast> From<&'ast ItemError> for OverloadedItem<'ast> {
481 fn from(e: &'ast ItemError) -> Self {
482 Self::Error(e)
483 }
484}
485
486impl<'a> OverloadedItem<'a> {
487 fn name(self) -> Option<&'a SolIdent> {
488 match self {
489 Self::Function(f) => f.name.as_ref(),
490 Self::Event(e) => Some(&e.name),
491 Self::Error(e) => Some(&e.name),
492 }
493 }
494
495 fn desc(&self) -> &'static str {
496 match self {
497 Self::Function(_) => "function",
498 Self::Event(_) => "event",
499 Self::Error(_) => "error",
500 }
501 }
502
503 fn eq_by_types(self, other: Self) -> bool {
504 match (self, other) {
505 (Self::Function(a), Self::Function(b)) => a.parameters.types().eq(b.parameters.types()),
506 (Self::Event(a), Self::Event(b)) => a.param_types().eq(b.param_types()),
507 (Self::Error(a), Self::Error(b)) => a.parameters.types().eq(b.parameters.types()),
508 _ => false,
509 }
510 }
511
512 fn span(self) -> Span {
513 match self {
514 Self::Function(f) => f.span(),
515 Self::Event(e) => e.span(),
516 Self::Error(e) => e.span(),
517 }
518 }
519
520 fn signature(self, cx: &ExpCtxt<'a>) -> String {
521 match self {
522 Self::Function(f) => cx.function_signature(f),
523 Self::Event(e) => cx.event_signature(e),
524 Self::Error(e) => cx.error_signature(e),
525 }
526 }
527}
528
529impl<'ast> ExpCtxt<'ast> {
531 #[allow(dead_code)]
532 fn item(&self, name: &SolPath) -> &Item {
533 match self.try_item(name) {
534 Some(item) => item,
535 None => abort!(name.span(), "unresolved item: {}", name),
536 }
537 }
538
539 fn try_item(&self, name: &SolPath) -> Option<&Item> {
540 self.all_items.resolve(name, &self.current_namespace).copied()
541 }
542
543 fn custom_type(&self, name: &SolPath) -> &Type {
544 match self.try_custom_type(name) {
545 Some(item) => item,
546 None => abort!(name.span(), "unresolved custom type: {}", name),
547 }
548 }
549
550 fn try_custom_type(&self, name: &SolPath) -> Option<&Type> {
551 self.custom_types.resolve(name, &self.current_namespace).inspect(|&ty| {
552 if ty.is_custom() {
553 abort!(
554 ty.span(),
555 "unresolved custom type in map";
556 note = name.span() => "name span";
557 );
558 }
559 })
560 }
561
562 fn indexed_as_hash(&self, param: &EventParameter) -> bool {
563 param.indexed_as_hash(self.custom_is_value_type())
564 }
565
566 fn custom_is_value_type(&self) -> impl Fn(&SolPath) -> bool + '_ {
567 move |ty| self.custom_type(ty).is_value_type(self.custom_is_value_type())
568 }
569
570 fn function_name(&self, function: &ItemFunction) -> SolIdent {
572 self.overloaded_name(function.into())
573 }
574
575 fn overloaded_name(&self, item: OverloadedItem<'ast>) -> SolIdent {
579 let original_ident = item.name().expect("item has no name");
580 let sig = item.signature(self);
581 match self.overloads.get(&self.current_namespace).and_then(|m| m.get(&sig)) {
582 Some(name) => SolIdent::new_spanned(name, original_ident.span()),
583 None => original_ident.clone(),
584 }
585 }
586
587 fn call_name(&self, function: &ItemFunction) -> Ident {
589 self.raw_call_name(&self.function_name(function).0)
590 }
591
592 fn raw_call_name(&self, function_name: &Ident) -> Ident {
594 let new_ident = format!("{}Call", function_name.unraw());
597 Ident::new(&new_ident, function_name.span())
598 }
599
600 fn return_name(&self, function: &ItemFunction) -> Ident {
602 self.raw_return_name(&self.function_name(function).0)
603 }
604
605 fn raw_return_name(&self, function_name: &Ident) -> Ident {
607 let new_ident = format!("{}Return", function_name.unraw());
610 Ident::new(&new_ident, function_name.span())
611 }
612
613 fn function_signature(&self, function: &ItemFunction) -> String {
614 self.signature(function.name().as_string(), &function.parameters)
615 }
616
617 fn function_selector(&self, function: &ItemFunction) -> ExprArray<u8> {
618 utils::selector(self.function_signature(function)).with_span(function.span())
619 }
620
621 fn error_signature(&self, error: &ItemError) -> String {
622 self.signature(error.name.as_string(), &error.parameters)
623 }
624
625 fn error_selector(&self, error: &ItemError) -> ExprArray<u8> {
626 utils::selector(self.error_signature(error)).with_span(error.span())
627 }
628
629 fn event_signature(&self, event: &ItemEvent) -> String {
630 self.signature(event.name.as_string(), &event.params())
631 }
632
633 fn event_selector(&self, event: &ItemEvent) -> ExprArray<u8> {
634 utils::event_selector(self.event_signature(event)).with_span(event.span())
635 }
636
637 fn signature<'a, I: IntoIterator<Item = &'a VariableDeclaration>>(
639 &self,
640 mut name: String,
641 params: I,
642 ) -> String {
643 name.push('(');
644 let mut first = true;
645 for param in params {
646 if !first {
647 name.push(',');
648 }
649 write!(name, "{}", ty::TypePrinter::new(self, ¶m.ty)).unwrap();
650 first = false;
651 }
652 name.push(')');
653 name
654 }
655
656 fn derives<'a, I>(&self, attrs: &mut Vec<Attribute>, params: I, derive_default: bool)
678 where
679 I: IntoIterator<Item = &'a VariableDeclaration>,
680 {
681 self.type_derives(attrs, params.into_iter().map(|p| &p.ty), derive_default);
682 }
683
684 fn type_derives<T, I>(&self, attrs: &mut Vec<Attribute>, types: I, mut derive_default: bool)
686 where
687 I: IntoIterator<Item = T>,
688 T: Borrow<Type>,
689 {
690 if let Some(extra) = &self.attrs.extra_derives {
691 if !extra.is_empty() {
692 attrs.push(parse_quote! { #[derive(#(#extra),*)] });
693 }
694 }
695
696 let Some(true) = self.attrs.all_derives else {
697 return;
698 };
699
700 let mut derives = Vec::with_capacity(5);
701 let mut derive_others = true;
702 for ty in types {
703 let ty = ty.borrow();
704 derive_default = derive_default && self.can_derive_default(ty);
705 derive_others = derive_others && self.can_derive_builtin_traits(ty);
706 }
707 if derive_default {
708 derives.push("Default");
709 }
710 if derive_others {
711 derives.extend(["Debug", "PartialEq", "Eq", "Hash"]);
712 }
713 let derives = derives.iter().map(|s| Ident::new(s, Span::call_site()));
714 attrs.push(parse_quote! { #[derive(#(#derives), *)] });
715 }
716
717 fn assert_resolved<'a, I>(&self, params: I) -> Result<()>
722 where
723 I: IntoIterator<Item = &'a VariableDeclaration>,
724 {
725 let mut errored = false;
726 for param in params {
727 param.ty.visit(|ty| {
728 if let Type::Custom(name) = ty {
729 if self.try_custom_type(name).is_none() {
730 let note = (!errored).then(|| {
731 errored = true;
732 "Custom types must be declared inside of the same scope they are referenced in,\n\
733 or \"imported\" as a UDT with `type ... is (...);`"
734 });
735 emit_error!(name.span(), "unresolved type"; help =? note);
736 }
737 }
738 });
739 }
740 Ok(())
741 }
742}
743
744#[derive(Clone, Debug)]
749pub struct ExternCrates {
750 pub sol_types: syn::Path,
752 pub contract: syn::Path,
754}
755
756impl Default for ExternCrates {
757 fn default() -> Self {
758 Self {
759 sol_types: parse_quote!(::alloy_sol_types),
760 contract: parse_quote!(::alloy_contract),
761 }
762 }
763}
764
765impl ExternCrates {
766 pub fn fill(&mut self, attrs: &SolAttrs) {
768 if let Some(sol_types) = &attrs.alloy_sol_types {
769 self.sol_types = sol_types.clone();
770 }
771 if let Some(alloy_contract) = &attrs.alloy_contract {
772 self.contract = alloy_contract.clone();
773 }
774 }
775}
776
777fn expand_fields<'a, P>(
781 params: &'a Parameters<P>,
782 cx: &'a ExpCtxt<'_>,
783) -> impl Iterator<Item = TokenStream> + 'a {
784 params.iter().enumerate().map(|(i, var)| {
785 let name = anon_name((i, var.name.as_ref()));
786 let ty = cx.expand_rust_type(&var.ty);
787 let attrs = &var.attrs;
788 quote! {
789 #(#attrs)*
790 #[allow(missing_docs)]
791 pub #name: #ty
792 }
793 })
794}
795
796#[inline]
798pub fn generate_name(i: usize) -> Ident {
799 format_ident!("_{i}")
800}
801
802pub fn anon_name<T: Into<Ident> + Clone>((i, name): (usize, Option<&T>)) -> Ident {
804 match name {
805 Some(name) => name.clone().into(),
806 None => generate_name(i),
807 }
808}
809
810fn expand_from_into_tuples<P>(
812 name: &Ident,
813 fields: &Parameters<P>,
814 cx: &ExpCtxt<'_>,
815) -> TokenStream {
816 let names = fields.names().enumerate().map(anon_name);
817
818 let names2 = names.clone();
819 let idxs = (0..fields.len()).map(syn::Index::from);
820
821 let (sol_tuple, rust_tuple) = expand_tuple_types(fields.types(), cx);
822
823 quote! {
824 #[doc(hidden)]
825 type UnderlyingSolTuple<'a> = #sol_tuple;
826 #[doc(hidden)]
827 type UnderlyingRustTuple<'a> = #rust_tuple;
828
829 #[cfg(test)]
830 #[allow(dead_code, unreachable_patterns)]
831 fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
832 match _t {
833 alloy_sol_types::private::AssertTypeEq::<<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType>(_) => {}
834 }
835 }
836
837 #[automatically_derived]
838 #[doc(hidden)]
839 impl ::core::convert::From<#name> for UnderlyingRustTuple<'_> {
840 fn from(value: #name) -> Self {
841 (#(value.#names,)*)
842 }
843 }
844
845 #[automatically_derived]
846 #[doc(hidden)]
847 impl ::core::convert::From<UnderlyingRustTuple<'_>> for #name {
848 fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
849 Self {
850 #(#names2: tuple.#idxs),*
851 }
852 }
853 }
854 }
855}
856
857fn expand_tuple_types<'a, I: IntoIterator<Item = &'a Type>>(
859 types: I,
860 cx: &ExpCtxt<'_>,
861) -> (TokenStream, TokenStream) {
862 let mut sol = TokenStream::new();
863 let mut rust = TokenStream::new();
864 let comma = Punct::new(',', Spacing::Alone);
865 for ty in types {
866 cx.expand_type_to(ty, &mut sol);
867 sol.append(comma.clone());
868
869 cx.expand_rust_type_to(ty, &mut rust);
870 rust.append(comma.clone());
871 }
872 let wrap_in_parens =
873 |stream| TokenStream::from(TokenTree::Group(Group::new(Delimiter::Parenthesis, stream)));
874 (wrap_in_parens(sol), wrap_in_parens(rust))
875}
876
877fn expand_tokenize<P>(params: &Parameters<P>, cx: &ExpCtxt<'_>) -> TokenStream {
879 tokenize_(params.iter().enumerate().map(|(i, p)| (i, &p.ty, p.name.as_ref())), cx)
880}
881
882fn expand_event_tokenize<'a>(
884 params: impl IntoIterator<Item = &'a EventParameter>,
885 cx: &ExpCtxt<'_>,
886) -> TokenStream {
887 tokenize_(
888 params
889 .into_iter()
890 .enumerate()
891 .filter(|(_, p)| !p.is_indexed())
892 .map(|(i, p)| (i, &p.ty, p.name.as_ref())),
893 cx,
894 )
895}
896
897fn tokenize_<'a>(
898 iter: impl Iterator<Item = (usize, &'a Type, Option<&'a SolIdent>)>,
899 cx: &'a ExpCtxt<'_>,
900) -> TokenStream {
901 let statements = iter.into_iter().map(|(i, ty, name)| {
902 let ty = cx.expand_type(ty);
903 let name = name.cloned().unwrap_or_else(|| generate_name(i).into());
904 quote! {
905 <#ty as alloy_sol_types::SolType>::tokenize(&self.#name)
906 }
907 });
908 quote! {
909 (#(#statements,)*)
910 }
911}
912
913#[allow(dead_code)]
914fn emit_json_error() {
915 static EMITTED: AtomicBool = AtomicBool::new(false);
916 if !EMITTED.swap(true, Ordering::Relaxed) {
917 emit_error!(
918 Span::call_site(),
919 "the `#[sol(abi)]` attribute requires the `\"json\"` feature"
920 );
921 }
922}