ink_ir/ir/item_impl/
constructor.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::{
16    Callable,
17    CallableKind,
18    InputsIter,
19    Visibility,
20    ensure_callable_invariants,
21};
22use crate::{
23    ir,
24    ir::{
25        attrs::SelectorOrWildcard,
26        utils::{
27            extract_cfg_attributes,
28            extract_cfg_syn_attributes,
29        },
30    },
31};
32use proc_macro2::{
33    Ident,
34    Span,
35    TokenStream,
36};
37use syn::spanned::Spanned as _;
38
39/// An ink! constructor definition.
40///
41/// # Example
42///
43/// ## Inherent implementation constructor:
44///
45/// ```
46/// # let event = <ink_ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(syn::parse_quote! {
47/// impl MyStorage {
48///     #[ink(constructor)]
49///     pub fn new(init_value: i32) -> Self {
50///         /* contract initialization goes here */
51/// #       unimplemented!()
52///     }
53/// }
54/// # }).unwrap();
55/// ```
56///
57/// ## Trait implementation constructor:
58///
59/// ```
60/// # <ink_ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(syn::parse_quote! {
61/// impl MyTrait for MyStorage {
62///     #[ink(constructor)]
63///     fn new(init_value: i32) -> Self {
64///         // contract initialization goes here
65/// #       unimplemented!()
66///     }
67/// }
68/// # }).unwrap();
69/// ```
70#[derive(Debug, PartialEq, Eq)]
71pub struct Constructor {
72    /// The underlying Rust method item.
73    pub(super) item: syn::ImplItemFn,
74    /// If the ink! constructor can receive funds.
75    is_payable: bool,
76    /// If the ink! constructor is default.
77    is_default: bool,
78    /// An optional user provided selector.
79    ///
80    /// # Note
81    ///
82    /// This overrides the computed selector, even when using a manual namespace
83    /// for the parent implementation block.
84    selector: Option<SelectorOrWildcard>,
85    /// An optional function name override.
86    ///
87    /// # Note
88    ///
89    /// - Useful for defining overloaded interfaces.
90    /// - If provided, the name must be a valid "identifier-like" string.
91    name: Option<String>,
92}
93
94impl quote::ToTokens for Constructor {
95    /// We mainly implement this trait for this ink! type to have a derived
96    /// [`Spanned`](`syn::spanned::Spanned`) implementation for it.
97    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
98        self.item.to_tokens(tokens)
99    }
100}
101
102impl Constructor {
103    /// Ensure that the constructor has a return.
104    /// Returns an error otherwise.
105    ///
106    /// # Errors
107    ///
108    /// If the ink! constructor is missing a return type.
109    fn ensure_return(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
110        if let syn::ReturnType::Default = &method_item.sig.output {
111            return Err(format_err_spanned!(
112                &method_item.sig,
113                "missing return for ink! constructor",
114            ))
115        }
116        Ok(())
117    }
118
119    /// Ensures that the ink! constructor has no `self` receiver.
120    ///
121    /// Returns an appropriate error otherwise.
122    ///
123    /// # Errors
124    ///
125    /// If the ink! constructor has a `&self`, `&mut self`, `self` or any other
126    /// kind of a `self` receiver as first argument.
127    fn ensure_no_self_receiver(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
128        match method_item.sig.inputs.iter().next() {
129            None | Some(syn::FnArg::Typed(_)) => (),
130            Some(syn::FnArg::Receiver(receiver)) => {
131                return Err(format_err_spanned!(
132                    receiver,
133                    "ink! constructors must have no `self` receiver",
134                ))
135            }
136        }
137        Ok(())
138    }
139
140    /// Sanitizes the attributes for the ink! constructor.
141    ///
142    /// Returns a tuple of ink! attributes and non-ink! attributes.
143    fn sanitize_attributes(
144        method_item: &syn::ImplItemFn,
145    ) -> Result<(ir::InkAttribute, Vec<syn::Attribute>), syn::Error> {
146        ir::sanitize_attributes(
147            method_item.span(),
148            method_item.attrs.clone(),
149            &ir::AttributeArgKind::Constructor,
150            |arg| {
151                match arg.kind() {
152                    ir::AttributeArg::Constructor
153                    | ir::AttributeArg::Payable
154                    | ir::AttributeArg::Default
155                    | ir::AttributeArg::Selector(_)
156                    | ir::AttributeArg::Name(_) => Ok(()),
157                    _ => Err(None),
158                }
159            },
160        )
161    }
162}
163
164impl TryFrom<syn::ImplItemFn> for Constructor {
165    type Error = syn::Error;
166
167    fn try_from(method_item: syn::ImplItemFn) -> Result<Self, Self::Error> {
168        ensure_callable_invariants(&method_item, CallableKind::Constructor)?;
169        Self::ensure_return(&method_item)?;
170        Self::ensure_no_self_receiver(&method_item)?;
171        let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
172        let is_payable = ink_attrs.is_payable();
173        let is_default = ink_attrs.is_default();
174        let selector = ink_attrs.selector();
175        let name = ink_attrs.name();
176        #[cfg(ink_abi = "sol")]
177        if selector.is_some() {
178            let selector_span = ink_attrs.args().find_map(|arg| {
179                matches!(arg.kind(), ir::AttributeArg::Selector(_)).then_some(arg.span())
180            });
181            return Err(format_err!(
182                selector_span.unwrap_or_else(|| method_item.span()),
183                "constructor `selector` attributes are not supported in Solidity ABI compatibility mode",
184            ));
185        }
186        Ok(Constructor {
187            selector,
188            is_payable,
189            is_default,
190            name,
191            item: syn::ImplItemFn {
192                attrs: other_attrs,
193                ..method_item
194            },
195        })
196    }
197}
198
199impl Callable for Constructor {
200    fn kind(&self) -> CallableKind {
201        CallableKind::Constructor
202    }
203
204    fn ident(&self) -> &Ident {
205        &self.item.sig.ident
206    }
207
208    fn user_provided_selector(&self) -> Option<&ir::Selector> {
209        if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
210            return Some(selector)
211        }
212        None
213    }
214
215    fn has_wildcard_selector(&self) -> bool {
216        matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
217    }
218
219    fn has_wildcard_complement_selector(&self) -> bool {
220        self.selector == Some(SelectorOrWildcard::wildcard_complement())
221    }
222
223    fn is_payable(&self) -> bool {
224        self.is_payable
225    }
226
227    fn is_default(&self) -> bool {
228        self.is_default
229    }
230
231    fn visibility(&self) -> Visibility {
232        match &self.item.vis {
233            syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
234            syn::Visibility::Inherited => Visibility::Inherited,
235            _ => unreachable!("encountered invalid visibility for ink! constructor"),
236        }
237    }
238
239    fn inputs(&self) -> InputsIter<'_> {
240        InputsIter::from(self)
241    }
242
243    fn inputs_span(&self) -> Span {
244        self.item.sig.inputs.span()
245    }
246
247    fn statements(&self) -> &[syn::Stmt] {
248        &self.item.block.stmts
249    }
250
251    fn name(&self) -> Option<&str> {
252        self.name.as_deref()
253    }
254}
255
256impl Constructor {
257    /// Returns a slice of all non-ink! attributes of the ink! constructor.
258    pub fn attrs(&self) -> &[syn::Attribute] {
259        &self.item.attrs
260    }
261
262    /// Returns a list of `cfg` attributes if any.
263    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
264        extract_cfg_attributes(self.attrs(), span)
265    }
266
267    /// Returns a list of `cfg` attributes as `syn::Attribute` if any.
268    pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
269        extract_cfg_syn_attributes(self.attrs())
270    }
271
272    /// Returns the return type of the ink! constructor if any.
273    pub fn output(&self) -> Option<&syn::Type> {
274        match &self.item.sig.output {
275            syn::ReturnType::Default => None,
276            syn::ReturnType::Type(_, return_type) => Some(return_type),
277        }
278    }
279
280    /// Returns the function name override (if any).
281    pub fn name(&self) -> Option<&str> {
282        self.name.as_deref()
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289
290    #[test]
291    fn inputs_works() {
292        macro_rules! expected_inputs {
293            ( $( $name:ident: $ty:ty ),* ) => {{
294                vec![
295                    $(
296                        syn::parse_quote! {
297                            $name: $ty
298                        }
299                    ),*
300                ]
301            }};
302        }
303        let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
304            (
305                // No inputs:
306                expected_inputs!(),
307                syn::parse_quote! {
308                    #[ink(constructor)]
309                    fn my_constructor() -> Self {}
310                },
311            ),
312            (
313                // Single input:
314                expected_inputs!(a: i32),
315                syn::parse_quote! {
316                    #[ink(constructor)]
317                    fn my_constructor(a: i32) -> Self {}
318                },
319            ),
320            (
321                // Some inputs:
322                expected_inputs!(a: i32, b: u64, c: [u8; 32]),
323                syn::parse_quote! {
324                    #[ink(constructor)]
325                    fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
326                },
327            ),
328        ];
329        for (expected_inputs, item_method) in test_inputs {
330            let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
331                .unwrap()
332                .inputs()
333                .cloned()
334                .map(syn::FnArg::Typed)
335                .collect::<Vec<_>>();
336            assert_eq!(actual_inputs, expected_inputs);
337        }
338    }
339
340    #[test]
341    fn is_payable_works() {
342        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
343            // Not payable.
344            (
345                false,
346                syn::parse_quote! {
347                    #[ink(constructor)]
348                    fn my_constructor() -> Self {}
349                },
350            ),
351            // Normalized ink! attribute.
352            (
353                true,
354                syn::parse_quote! {
355                    #[ink(constructor, payable)]
356                    pub fn my_constructor() -> Self {}
357                },
358            ),
359            // Different ink! attributes.
360            (
361                true,
362                syn::parse_quote! {
363                    #[ink(constructor)]
364                    #[ink(payable)]
365                    pub fn my_constructor() -> Self {}
366                },
367            ),
368            // Another ink! attribute, separate and normalized attribute.
369            (
370                true,
371                syn::parse_quote! {
372                    #[ink(constructor)]
373                    #[ink(selector = 0xDEADBEEF, payable)]
374                    pub fn my_constructor() -> Self {}
375                },
376            ),
377        ];
378        for (expect_payable, item_method) in test_inputs {
379            let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
380                .unwrap()
381                .is_payable();
382            assert_eq!(is_payable, expect_payable);
383        }
384    }
385
386    #[test]
387    fn is_default_works() {
388        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
389            // Not default.
390            (
391                false,
392                syn::parse_quote! {
393                    #[ink(constructor)]
394                    fn my_constructor() -> Self {}
395                },
396            ),
397            // Default constructor.
398            (
399                true,
400                syn::parse_quote! {
401                    #[ink(constructor, default)]
402                    pub fn my_constructor() -> Self {}
403                },
404            ),
405        ];
406        for (expect_default, item_method) in test_inputs {
407            let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
408                .unwrap()
409                .is_default();
410            assert_eq!(is_default, expect_default);
411        }
412    }
413
414    #[test]
415    fn name_override_works() {
416        let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
417            // No name override.
418            (
419                None,
420                syn::parse_quote! {
421                    #[ink(constructor)]
422                    fn my_constructor() -> Self {}
423                },
424            ),
425            // Name override.
426            (
427                Some("myConstructor"),
428                syn::parse_quote! {
429                    #[ink(constructor, name = "myConstructor")]
430                    pub fn my_constructor() -> Self {}
431                },
432            ),
433        ];
434        for (expected_name, item_method) in test_inputs {
435            let ctor = <ir::Constructor as TryFrom<_>>::try_from(item_method).unwrap();
436            assert_eq!(ctor.name(), expected_name);
437        }
438    }
439
440    #[test]
441    fn visibility_works() {
442        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
443            // inherited
444            (
445                false,
446                syn::parse_quote! {
447                    #[ink(constructor)]
448                    fn my_constructor() -> Self {}
449                },
450            ),
451            // public
452            (
453                true,
454                syn::parse_quote! {
455                    #[ink(constructor)]
456                    pub fn my_constructor() -> Self {}
457                },
458            ),
459        ];
460        for (is_pub, item_method) in test_inputs {
461            let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
462                .unwrap()
463                .visibility();
464            assert_eq!(visibility.is_pub(), is_pub);
465            assert_eq!(visibility.is_inherited(), !is_pub);
466        }
467    }
468
469    #[test]
470    fn try_from_works() {
471        let item_methods: Vec<syn::ImplItemFn> = vec![
472            // simple + inherited visibility
473            syn::parse_quote! {
474                #[ink(constructor)]
475                fn my_constructor() -> Self {}
476            },
477            // simple + public visibility
478            syn::parse_quote! {
479                #[ink(constructor)]
480                pub fn my_constructor() -> Self {}
481            },
482            // many inputs
483            syn::parse_quote! {
484                #[ink(constructor)]
485                fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
486            },
487            // Result return type
488            syn::parse_quote! {
489                #[ink(constructor)]
490                pub fn my_constructor() -> Result<Self, ()> {}
491            },
492        ];
493        for item_method in item_methods {
494            assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
495        }
496    }
497
498    fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
499        assert_eq!(
500            <ir::Constructor as TryFrom<_>>::try_from(item_method)
501                .map_err(|err| err.to_string()),
502            Err(expected_err.to_string()),
503        );
504    }
505
506    #[test]
507    fn try_from_missing_return_fails() {
508        let item_methods: Vec<syn::ImplItemFn> = vec![
509            syn::parse_quote! {
510                #[ink(constructor)]
511                fn my_constructor() {}
512            },
513            syn::parse_quote! {
514                #[ink(constructor)]
515                pub fn my_constructor() {}
516            },
517        ];
518        for item_method in item_methods {
519            assert_try_from_fails(item_method, "missing return for ink! constructor")
520        }
521    }
522
523    #[test]
524    fn try_from_invalid_self_receiver_fails() {
525        let item_methods: Vec<syn::ImplItemFn> = vec![
526            syn::parse_quote! {
527                #[ink(constructor)]
528                fn my_constructor(&self) -> Self {}
529            },
530            syn::parse_quote! {
531                #[ink(constructor)]
532                pub fn my_constructor(&mut self) -> Self {}
533            },
534            syn::parse_quote! {
535                #[ink(constructor)]
536                pub fn my_constructor(self) -> Self {}
537            },
538            syn::parse_quote! {
539                #[ink(constructor)]
540                pub fn my_constructor(mut self) -> Self {}
541            },
542        ];
543        for item_method in item_methods {
544            assert_try_from_fails(
545                item_method,
546                "ink! constructors must have no `self` receiver",
547            )
548        }
549    }
550
551    #[test]
552    fn try_from_generics_fails() {
553        let item_methods: Vec<syn::ImplItemFn> = vec![
554            syn::parse_quote! {
555                #[ink(constructor)]
556                fn my_constructor<T>() -> Self {}
557            },
558            syn::parse_quote! {
559                #[ink(constructor)]
560                pub fn my_constructor<T>() -> Self {}
561            },
562        ];
563        for item_method in item_methods {
564            assert_try_from_fails(item_method, "ink! constructors must not be generic")
565        }
566    }
567
568    #[test]
569    fn try_from_const_fails() {
570        let item_methods: Vec<syn::ImplItemFn> = vec![
571            syn::parse_quote! {
572                #[ink(constructor)]
573                const fn my_constructor() -> Self {}
574            },
575            syn::parse_quote! {
576                #[ink(constructor)]
577                pub const fn my_constructor() -> Self {}
578            },
579        ];
580        for item_method in item_methods {
581            assert_try_from_fails(item_method, "ink! constructors must not be const")
582        }
583    }
584
585    #[test]
586    fn try_from_async_fails() {
587        let item_methods: Vec<syn::ImplItemFn> = vec![
588            syn::parse_quote! {
589                #[ink(constructor)]
590                async fn my_constructor() -> Self {}
591            },
592            syn::parse_quote! {
593                #[ink(constructor)]
594                async fn my_constructor() -> Self {}
595            },
596        ];
597        for item_method in item_methods {
598            assert_try_from_fails(item_method, "ink! constructors must not be async")
599        }
600    }
601
602    #[test]
603    fn try_from_unsafe_fails() {
604        let item_methods: Vec<syn::ImplItemFn> = vec![
605            syn::parse_quote! {
606                #[ink(constructor)]
607                unsafe fn my_constructor() -> Self {}
608            },
609            syn::parse_quote! {
610                #[ink(constructor)]
611                unsafe fn my_constructor() -> Self {}
612            },
613        ];
614        for item_method in item_methods {
615            assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
616        }
617    }
618
619    #[test]
620    fn try_from_explicit_abi_fails() {
621        let item_methods: Vec<syn::ImplItemFn> = vec![
622            syn::parse_quote! {
623                #[ink(constructor)]
624                extern "C" fn my_constructor() -> Self {}
625            },
626            syn::parse_quote! {
627                #[ink(constructor)]
628                extern "C" fn my_constructor() -> Self {}
629            },
630        ];
631        for item_method in item_methods {
632            assert_try_from_fails(
633                item_method,
634                "ink! constructors must not have explicit ABI",
635            )
636        }
637    }
638
639    #[test]
640    fn try_from_variadic_fails() {
641        let item_methods: Vec<syn::ImplItemFn> = vec![
642            syn::parse_quote! {
643                #[ink(constructor)]
644                fn my_constructor(...) -> Self {}
645            },
646            syn::parse_quote! {
647                #[ink(constructor)]
648                fn my_constructor(...) -> Self {}
649            },
650        ];
651        for item_method in item_methods {
652            assert_try_from_fails(item_method, "ink! constructors must not be variadic")
653        }
654    }
655
656    #[test]
657    fn try_from_visibility_fails() {
658        let item_methods: Vec<syn::ImplItemFn> = vec![
659            syn::parse_quote! {
660                #[ink(constructor)]
661                pub(crate) fn my_constructor() -> Self {}
662            },
663            syn::parse_quote! {
664                #[ink(constructor)]
665                pub(in my::path) fn my_constructor() -> Self {}
666            },
667        ];
668        for item_method in item_methods {
669            assert_try_from_fails(
670                item_method,
671                "ink! constructors must have public or inherited visibility",
672            )
673        }
674    }
675
676    #[test]
677    fn conflicting_attributes_fails() {
678        let item_methods: Vec<syn::ImplItemFn> = vec![
679            // storage
680            syn::parse_quote! {
681                #[ink(constructor, storage)]
682                fn my_constructor() -> Self {}
683            },
684            // namespace
685            syn::parse_quote! {
686                #[ink(constructor, namespace = "my_namespace")]
687                fn my_constructor() -> Self {}
688            },
689            // event + multiple attributes
690            syn::parse_quote! {
691                #[ink(constructor)]
692                #[ink(event)]
693                fn my_constructor() -> Self {}
694            },
695        ];
696        for item_method in item_methods {
697            assert_try_from_fails(
698                item_method,
699                "encountered conflicting ink! attribute argument",
700            )
701        }
702    }
703
704    #[test]
705    fn try_from_wildcard_constructor_works() {
706        let item: syn::ImplItemFn = syn::parse_quote! {
707            #[ink(constructor, selector = _)]
708            pub fn my_constructor() -> Self {}
709        };
710        assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
711    }
712}