sylvia_derive/parser/
mod.rs

1//! Module defining parsing utilities for Sylvia attributes.
2//! All parsing done for the [crate::types] should be done here.
3
4pub mod attributes;
5pub mod check_generics;
6pub mod entry_point;
7pub mod variant_descs;
8
9pub use attributes::{
10    ContractErrorAttr, ContractMessageAttr, Custom, Customs, FilteredOverrideEntryPoints, MsgAttr,
11    MsgType, OverrideEntryPoint, ParsedSylviaAttributes, SylviaAttribute,
12};
13use check_generics::{CheckGenerics, GetPath};
14pub use entry_point::EntryPointArgs;
15
16use proc_macro_error::emit_error;
17use syn::punctuated::Punctuated;
18use syn::spanned::Spanned;
19use syn::{FnArg, GenericArgument, ImplItem, ItemImpl, Path, PathArguments, Signature, Token};
20
21use crate::types::msg_field::MsgField;
22
23fn extract_generics_from_path(module: &Path) -> Punctuated<GenericArgument, Token![,]> {
24    let generics = module
25        .segments
26        .last()
27        .map(|segment| match segment.arguments.clone() {
28            PathArguments::AngleBracketed(generics) => generics.args,
29            PathArguments::None => Default::default(),
30            PathArguments::Parenthesized(_) => Default::default(),
31        })
32        .unwrap_or_default();
33
34    generics
35}
36
37/// Use to make sure that [contract](crate::contract) is used over implementation containing
38/// mandatory `new` method.
39pub fn assert_new_method_defined(item: &ItemImpl) {
40    const ERROR_NOTE: &str = "`sylvia::contract` requires parameterless `new` method to be defined for dispatch to work correctly.";
41
42    let new = item.items.iter().find_map(|item| match item {
43        ImplItem::Fn(method) if method.sig.ident == "new" => Some(method),
44        _ => None,
45    });
46
47    match new {
48        Some(new) if !new.sig.inputs.is_empty() => emit_error!(
49            new.sig.inputs, "Parameters not allowed in `new` method.";
50            note = ERROR_NOTE;
51        ),
52        None => {
53            emit_error!(
54                item, "Missing `new` method in `impl` block.";
55                note = ERROR_NOTE;
56            )
57        }
58        _ => (),
59    }
60}
61
62/// Parses method signature and returns a vector of [`MsgField`].
63pub fn process_fields<'s, Generic>(
64    sig: &'s Signature,
65    generics_checker: &mut CheckGenerics<Generic>,
66) -> Vec<MsgField<'s>>
67where
68    Generic: GetPath + PartialEq,
69{
70    assert_no_self_ctx_attributes(sig);
71
72    sig.inputs
73        .iter()
74        .skip(2)
75        .filter_map(|arg| match arg {
76            FnArg::Receiver(item) => {
77                emit_error!(item.span(), "Unexpected `self` argument");
78                None
79            }
80
81            FnArg::Typed(item) => MsgField::new(item, generics_checker),
82        })
83        .collect()
84}
85
86/// Assert Sylvia attributes were not used on `self` and `ctx` parameters.
87fn assert_no_self_ctx_attributes(sig: &Signature) {
88    sig.inputs.iter().take(2).for_each(|arg| match arg {
89        FnArg::Receiver(item) => {
90            item.attrs.iter().for_each(|attr| {
91                if SylviaAttribute::new(attr).is_none() {
92                    return;
93                }
94                emit_error!(attr.span(),
95                    "Invalid usage of Sylvia attribute.";
96                    note = "First and second arguments of the method should be `self` and `ctx` respectively.";
97                    note = "Unexpected attribute on `self` parameter."
98                );
99            });
100        }
101        FnArg::Typed(item) => {
102            item.attrs.iter().for_each(|attr| {
103                if SylviaAttribute::new(attr).is_none() {
104                    return;
105                }
106                emit_error!(attr.span(),
107                    "Invalid usage of Sylvia attribute.";
108                    note = "First and second arguments of the method should be `self` and `ctx` respectively.";
109                    note = "Unexpected attribute on `ctx` parameter."
110                );
111            });
112        }
113    });
114}