Skip to main content

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    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
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        self.resolve_entry(path, current_namespace).map(|(_, value)| value)
80    }
81
82    /// Resolves an item and returns the namespace it was found in.
83    fn resolve_entry(
84        &self,
85        path: &SolPath,
86        current_namespace: &Option<SolIdent>,
87    ) -> Option<(&Option<SolIdent>, &T)> {
88        // If path contains two components, its `Contract.Something` where `Contract` is a namespace
89        if path.len() == 2 {
90            self.get_entry_by_name_and_namespace(&Some(path.first().clone()), path.last())
91        } else {
92            // If there's only one component, this is either global item, or item declared in the
93            // current namespace.
94            //
95            // NOTE: This does not account for inheritance
96            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    /// Inserts an item into the map if it does not exist and returns a mutable reference to it.
113    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/// The expansion context.
119#[derive(Debug)]
120pub struct ExpCtxt<'ast> {
121    /// Keeps items along with optional parent contract holding their definition.
122    all_items: NamespacedMap<&'ast Item>,
123    custom_types: NamespacedMap<Type>,
124
125    /// `name => item`
126    overloaded_items: NamespacedMap<Vec<OverloadedItem<'ast>>>,
127    /// `namespace => signature => new_name`
128    overloads: IndexMap<Option<SolIdent>, IndexMap<String, String>>,
129
130    attrs: SolAttrs,
131    crates: ExternCrates,
132    ast: &'ast File,
133
134    /// Current namespace. Switched during AST traversal and expansion of different contracts.
135    current_namespace: Option<SolIdent>,
136}
137
138// expand
139impl<'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    /// Sets the current namespace for the duration of the closure.
154    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            // Selector collisions requires resolved types.
178            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            // TODO: Dummy items
189            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
215// resolve
216impl 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        /// Helper struct, recursively resolving types and keeping track of namespace which is
246        /// updated when entering a type from external contract.
247        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                    // Update namespace if we're entering a new one
263                    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    /// Checks for function and error selector collisions in the resolved items.
296    fn check_selector_collisions(&mut self) -> std::result::Result<(), ()> {
297        #[derive(Clone, Copy)]
298        enum SelectorKind {
299            Function,
300            Error,
301            // We can ignore events since their selectors are 32 bytes which are unlikely to
302            // collide.
303            // Event,
304        }
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                    // Self::Event => "event",
312                }
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                        // Item::Event(event) => (SelectorKind::Event, this.event_selector(event)),
330                        _ => continue,
331                    };
332                    let selector: [u8; 4] = selector.array.try_into().unwrap();
333                    // 0x00000000 or 0xffffffff are reserved for custom errors.
334                    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                    // check for same parameters
382                    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
539// utils
540impl<'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    /// Expands an [`EventParameter`] to its Rust type, rewriting indexed
587    /// dynamically-sized types to `FixedBytes<32>`.
588    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(&param.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    /// Returns the name of the function, adjusted for overloads.
603    fn function_name(&self, function: &ItemFunction) -> SolIdent {
604        self.overloaded_name(function.into())
605    }
606
607    /// Returns the name of the given item, adjusted for overloads.
608    ///
609    /// Use `.into()` to convert from `&ItemFunction` or `&ItemEvent`.
610    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    /// Returns the name of the function's call Rust struct.
620    fn call_name(&self, function: &ItemFunction) -> Ident {
621        self.raw_call_name(&self.function_name(function).0)
622    }
623
624    /// Formats the given name as a function's call Rust struct name.
625    fn raw_call_name(&self, function_name: &Ident) -> Ident {
626        // Note: we want to strip the `r#` prefix when present since we are creating a new ident
627        // that will never be a keyword.
628        let new_ident = format!("{}Call", function_name.unraw());
629        Ident::new(&new_ident, function_name.span())
630    }
631
632    /// Returns the name of the function's return Rust struct.
633    fn return_name(&self, function: &ItemFunction) -> Ident {
634        self.raw_return_name(&self.function_name(function).0)
635    }
636
637    /// Formats the given name as a function's return Rust struct name.
638    fn raw_return_name(&self, function_name: &Ident) -> Ident {
639        // Note: we want to strip the `r#` prefix when present since we are creating a new ident
640        // that will never be a keyword.
641        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    /// Formats the name and parameters of the function as a Solidity signature.
670    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, &param.ty)).unwrap();
682            first = false;
683        }
684        name.push(')');
685        name
686    }
687
688    /// Extends `attrs` with:
689    /// - all derives specified by the user in `#[sol(extra_derives(...))]`,
690    /// - all possible derive attributes for the given type if `#[sol(all_derives)]` was passed.
691    ///
692    /// The following traits are only implemented on tuples of arity 12 or less:
693    /// - [PartialEq](https://doc.rust-lang.org/stable/std/cmp/trait.PartialEq.html)
694    /// - [Eq](https://doc.rust-lang.org/stable/std/cmp/trait.Eq.html)
695    /// - [PartialOrd](https://doc.rust-lang.org/stable/std/cmp/trait.PartialOrd.html)
696    /// - [Ord](https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html)
697    /// - [Debug](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html)
698    /// - [Default](https://doc.rust-lang.org/stable/std/default/trait.Default.html)
699    /// - [Hash](https://doc.rust-lang.org/stable/std/hash/trait.Hash.html)
700    ///
701    /// while the `Default` trait is only implemented on arrays of length 32 or
702    /// less.
703    ///
704    /// Tuple reference: <https://doc.rust-lang.org/stable/std/primitive.tuple.html#trait-implementations-1>
705    ///
706    /// Array reference: <https://doc.rust-lang.org/stable/std/primitive.array.html>
707    ///
708    /// `derive_default` should be set to false when calling this for enums.
709    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    /// Implementation of [`derives`](Self::derives).
717    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    /// Like [`type_derives`](Self::type_derives), but for the generated `*Calls`
750    /// / `*Errors` / `*Events` enums.
751    ///
752    /// The enum variant types may be synthetic names that don't resolve as
753    /// items (overloaded items get `_N` suffixes; call variants use `*Call`
754    /// structs), so whether the builtin traits can be derived is precomputed
755    /// from the underlying items' parameter types. Enums never derive
756    /// `Default`.
757    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    /// Returns an error if any of the types in the parameters are unresolved.
770    ///
771    /// Provides a better error message than an `unwrap` or `expect` when we
772    /// know beforehand that we will be needing types to be resolved.
773    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/// Configurable extern crate dependencies.
797///
798/// These should be added to import lists at the top of anonymous `const _: () = { ... }` blocks,
799/// and in case of top-level structs they should be inlined into all `path`s.
800#[derive(Clone, Debug)]
801pub struct ExternCrates {
802    /// The path to the `alloy_sol_types` crate.
803    pub sol_types: syn::Path,
804    /// The path to the `alloy_contract` crate.
805    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    /// Fills the extern crate dependencies with the given attributes.
819    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
829// helper functions
830
831/// Expands a list of parameters into a list of struct fields.
832fn 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/// Generates an anonymous name from an integer. Used in [`anon_name`].
849#[inline]
850pub fn generate_name(i: usize) -> Ident {
851    format_ident!("_{i}")
852}
853
854/// Returns the name of a parameter, or a generated name if it is `None`.
855pub 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
862/// Utility type to determining how a solidity type should be expanded.
863enum FieldKind {
864    /// The type should be expanded as is. i.e a struct with named/unnamed fields.
865    ///
866    /// e.g
867    /// ```ignore
868    /// sol! {
869    ///   error MyError(uint256, string);
870    /// }
871    /// ```
872    /// Expands to:
873    ///
874    /// ```ignore
875    /// struct MyError {
876    ///    pub _0: U256,
877    ///    pub _1: String,
878    /// }
879    Original,
880    /// The types should be deconstructed and expanded into a unit/tuple struct depending on the
881    /// number of params.
882    ///
883    /// e.g
884    /// ```ignore
885    /// sol! {
886    ///  error MyError(uint256 value);
887    ///  error UnitError();
888    /// }
889    /// ```
890    ///
891    /// Expands to:
892    ///
893    /// ```ignore
894    /// struct MyError(pub U256);
895    /// struct UnitError();
896    /// ```
897    Deconstruct,
898}
899
900impl FieldKind {
901    /// Returns whether the field kind is `Deconstruct`.
902    fn is_deconstruct(&self) -> bool {
903        matches!(self, Self::Deconstruct)
904    }
905}
906
907/// Expands `From` impls for a list of types and the corresponding tuple.
908fn 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
963/// Returns `(sol_tuple, rust_tuple)`
964fn 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
983/// Expand the body of a `tokenize` function.
984fn 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
997/// Expand the body of a `tokenize` function.
998fn 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}