alloy_sol_macro_expander/expand/
mod.rs

1//! Functions which generate Rust code from the Solidity AST.
2
3use 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
38/// The limit for the number of times to resolve a type.
39const RESOLVE_LIMIT: usize = 128;
40
41/// The [`sol!`] expansion implementation.
42///
43/// [`sol!`]: https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/index.html
44pub fn expand(ast: File) -> Result<TokenStream> {
45    utils::pme_compat_result(|| ExpCtxt::new(&ast).expand())
46}
47
48/// Expands a Rust type from a Solidity type.
49pub 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/// Mapping namespace -> ident -> T
59///
60/// Keeps namespaced items. Namespace `None` represents global namespace (top-level items).
61/// Namespace `Some(ident)` represents items declared inside of a contract.
62#[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    /// Inserts an item into the map.
73    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    /// Given [SolPath] and current namespace, resolves item
78    pub fn resolve(&self, path: &SolPath, current_namespace: &Option<SolIdent>) -> Option<&T> {
79        // If path contains two components, its `Contract.Something` where `Contract` is a namespace
80        if path.len() == 2 {
81            self.get_by_name_and_namespace(&Some(path.first().clone()), path.last())
82        } else {
83            // If there's only one component, this is either global item, or item declared in the
84            // current namespace.
85            //
86            // NOTE: This does not account for inheritance
87            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    /// Inserts an item into the map if it does not exist and returns a mutable reference to it.
103    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/// The expansion context.
109#[derive(Debug)]
110pub struct ExpCtxt<'ast> {
111    /// Keeps items along with optional parent contract holding their definition.
112    all_items: NamespacedMap<&'ast Item>,
113    custom_types: NamespacedMap<Type>,
114
115    /// `name => item`
116    overloaded_items: NamespacedMap<Vec<OverloadedItem<'ast>>>,
117    /// `namespace => signature => new_name`
118    overloads: IndexMap<Option<SolIdent>, IndexMap<String, String>>,
119
120    attrs: SolAttrs,
121    crates: ExternCrates,
122    ast: &'ast File,
123
124    /// Current namespace. Switched during AST traversal and expansion of different contracts.
125    current_namespace: Option<SolIdent>,
126}
127
128// expand
129impl<'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    /// Sets the current namespace for the duration of the closure.
144    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            // Selector collisions requires resolved types.
168            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            // TODO: Dummy items
179            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
205// resolve
206impl 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        /// Helper struct, recursively resolving types and keeping track of namespace which is
236        /// updated when entering a type from external contract.
237        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                    // Update namespace if we're entering a new one
253                    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    /// Checks for function and error selector collisions in the resolved items.
286    fn check_selector_collisions(&mut self) -> std::result::Result<(), ()> {
287        #[derive(Clone, Copy)]
288        enum SelectorKind {
289            Function,
290            Error,
291            // We can ignore events since their selectors are 32 bytes which are unlikely to
292            // collide.
293            // Event,
294        }
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                    // Self::Event => "event",
302                }
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                        // Item::Event(event) => (SelectorKind::Event, this.event_selector(event)),
320                        _ => continue,
321                    };
322                    let selector: [u8; 4] = selector.array.try_into().unwrap();
323                    // 0x00000000 or 0xffffffff are reserved for custom errors.
324                    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                    // check for same parameters
372                    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
529// utils
530impl<'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    /// Returns the name of the function, adjusted for overloads.
571    fn function_name(&self, function: &ItemFunction) -> SolIdent {
572        self.overloaded_name(function.into())
573    }
574
575    /// Returns the name of the given item, adjusted for overloads.
576    ///
577    /// Use `.into()` to convert from `&ItemFunction` or `&ItemEvent`.
578    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    /// Returns the name of the function's call Rust struct.
588    fn call_name(&self, function: &ItemFunction) -> Ident {
589        self.raw_call_name(&self.function_name(function).0)
590    }
591
592    /// Formats the given name as a function's call Rust struct name.
593    fn raw_call_name(&self, function_name: &Ident) -> Ident {
594        // Note: we want to strip the `r#` prefix when present since we are creating a new ident
595        // that will never be a keyword.
596        let new_ident = format!("{}Call", function_name.unraw());
597        Ident::new(&new_ident, function_name.span())
598    }
599
600    /// Returns the name of the function's return Rust struct.
601    fn return_name(&self, function: &ItemFunction) -> Ident {
602        self.raw_return_name(&self.function_name(function).0)
603    }
604
605    /// Formats the given name as a function's return Rust struct name.
606    fn raw_return_name(&self, function_name: &Ident) -> Ident {
607        // Note: we want to strip the `r#` prefix when present since we are creating a new ident
608        // that will never be a keyword.
609        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    /// Formats the name and parameters of the function as a Solidity signature.
638    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, &param.ty)).unwrap();
650            first = false;
651        }
652        name.push(')');
653        name
654    }
655
656    /// Extends `attrs` with:
657    /// - all derives specified by the user in `#[sol(extra_derives(...))]`,
658    /// - all possible derive attributes for the given type if `#[sol(all_derives)]` was passed.
659    ///
660    /// The following traits are only implemented on tuples of arity 12 or less:
661    /// - [PartialEq](https://doc.rust-lang.org/stable/std/cmp/trait.PartialEq.html)
662    /// - [Eq](https://doc.rust-lang.org/stable/std/cmp/trait.Eq.html)
663    /// - [PartialOrd](https://doc.rust-lang.org/stable/std/cmp/trait.PartialOrd.html)
664    /// - [Ord](https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html)
665    /// - [Debug](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html)
666    /// - [Default](https://doc.rust-lang.org/stable/std/default/trait.Default.html)
667    /// - [Hash](https://doc.rust-lang.org/stable/std/hash/trait.Hash.html)
668    ///
669    /// while the `Default` trait is only implemented on arrays of length 32 or
670    /// less.
671    ///
672    /// Tuple reference: <https://doc.rust-lang.org/stable/std/primitive.tuple.html#trait-implementations-1>
673    ///
674    /// Array reference: <https://doc.rust-lang.org/stable/std/primitive.array.html>
675    ///
676    /// `derive_default` should be set to false when calling this for enums.
677    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    /// Implementation of [`derives`](Self::derives).
685    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    /// Returns an error if any of the types in the parameters are unresolved.
718    ///
719    /// Provides a better error message than an `unwrap` or `expect` when we
720    /// know beforehand that we will be needing types to be resolved.
721    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/// Configurable extern crate dependencies.
745///
746/// These should be added to import lists at the top of anonymous `const _: () = { ... }` blocks,
747/// and in case of top-level structs they should be inlined into all `path`s.
748#[derive(Clone, Debug)]
749pub struct ExternCrates {
750    /// The path to the `alloy_sol_types` crate.
751    pub sol_types: syn::Path,
752    /// The path to the `alloy_contract` crate.
753    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    /// Fills the extern crate dependencies with the given attributes.
767    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
777// helper functions
778
779/// Expands a list of parameters into a list of struct fields.
780fn 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/// Generates an anonymous name from an integer. Used in [`anon_name`].
797#[inline]
798pub fn generate_name(i: usize) -> Ident {
799    format_ident!("_{i}")
800}
801
802/// Returns the name of a parameter, or a generated name if it is `None`.
803pub 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
810/// Expands `From` impls for a list of types and the corresponding tuple.
811fn 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
857/// Returns `(sol_tuple, rust_tuple)`
858fn 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
877/// Expand the body of a `tokenize` function.
878fn 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
882/// Expand the body of a `tokenize` function.
883fn 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}