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    ensure_callable_invariants,
17    Callable,
18    CallableKind,
19    InputsIter,
20    Visibility,
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}
86
87impl quote::ToTokens for Constructor {
88    /// We mainly implement this trait for this ink! type to have a derived
89    /// [`Spanned`](`syn::spanned::Spanned`) implementation for it.
90    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
91        self.item.to_tokens(tokens)
92    }
93}
94
95impl Constructor {
96    /// Ensure that the constructor has a return.
97    /// Returns an error otherwise.
98    ///
99    /// # Errors
100    ///
101    /// If the ink! constructor is missing a return type.
102    fn ensure_return(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
103        if let syn::ReturnType::Default = &method_item.sig.output {
104            return Err(format_err_spanned!(
105                &method_item.sig,
106                "missing return for ink! constructor",
107            ))
108        }
109        Ok(())
110    }
111
112    /// Ensures that the ink! constructor has no `self` receiver.
113    ///
114    /// Returns an appropriate error otherwise.
115    ///
116    /// # Errors
117    ///
118    /// If the ink! constructor has a `&self`, `&mut self`, `self` or any other
119    /// kind of a `self` receiver as first argument.
120    fn ensure_no_self_receiver(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
121        match method_item.sig.inputs.iter().next() {
122            None | Some(syn::FnArg::Typed(_)) => (),
123            Some(syn::FnArg::Receiver(receiver)) => {
124                return Err(format_err_spanned!(
125                    receiver,
126                    "ink! constructors must have no `self` receiver",
127                ))
128            }
129        }
130        Ok(())
131    }
132
133    /// Sanitizes the attributes for the ink! constructor.
134    ///
135    /// Returns a tuple of ink! attributes and non-ink! attributes.
136    fn sanitize_attributes(
137        method_item: &syn::ImplItemFn,
138    ) -> Result<(ir::InkAttribute, Vec<syn::Attribute>), syn::Error> {
139        ir::sanitize_attributes(
140            method_item.span(),
141            method_item.attrs.clone(),
142            &ir::AttributeArgKind::Constructor,
143            |arg| {
144                match arg.kind() {
145                    ir::AttributeArg::Constructor
146                    | ir::AttributeArg::Payable
147                    | ir::AttributeArg::Default
148                    | ir::AttributeArg::Selector(_) => Ok(()),
149                    _ => Err(None),
150                }
151            },
152        )
153    }
154}
155
156impl TryFrom<syn::ImplItemFn> for Constructor {
157    type Error = syn::Error;
158
159    fn try_from(method_item: syn::ImplItemFn) -> Result<Self, Self::Error> {
160        ensure_callable_invariants(&method_item, CallableKind::Constructor)?;
161        Self::ensure_return(&method_item)?;
162        Self::ensure_no_self_receiver(&method_item)?;
163        let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
164        let is_payable = ink_attrs.is_payable();
165        let is_default = ink_attrs.is_default();
166        let selector = ink_attrs.selector();
167        Ok(Constructor {
168            selector,
169            is_payable,
170            is_default,
171            item: syn::ImplItemFn {
172                attrs: other_attrs,
173                ..method_item
174            },
175        })
176    }
177}
178
179impl Callable for Constructor {
180    fn kind(&self) -> CallableKind {
181        CallableKind::Constructor
182    }
183
184    fn ident(&self) -> &Ident {
185        &self.item.sig.ident
186    }
187
188    fn user_provided_selector(&self) -> Option<&ir::Selector> {
189        if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
190            return Some(selector)
191        }
192        None
193    }
194
195    fn has_wildcard_selector(&self) -> bool {
196        matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
197    }
198
199    fn has_wildcard_complement_selector(&self) -> bool {
200        self.selector == Some(SelectorOrWildcard::wildcard_complement())
201    }
202
203    fn is_payable(&self) -> bool {
204        self.is_payable
205    }
206
207    fn is_default(&self) -> bool {
208        self.is_default
209    }
210
211    fn visibility(&self) -> Visibility {
212        match &self.item.vis {
213            syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
214            syn::Visibility::Inherited => Visibility::Inherited,
215            _ => unreachable!("encountered invalid visibility for ink! constructor"),
216        }
217    }
218
219    fn inputs(&self) -> InputsIter {
220        InputsIter::from(self)
221    }
222
223    fn inputs_span(&self) -> Span {
224        self.item.sig.inputs.span()
225    }
226
227    fn statements(&self) -> &[syn::Stmt] {
228        &self.item.block.stmts
229    }
230}
231
232impl Constructor {
233    /// Returns a slice of all non-ink! attributes of the ink! constructor.
234    pub fn attrs(&self) -> &[syn::Attribute] {
235        &self.item.attrs
236    }
237
238    /// Returns a list of `cfg` attributes if any.
239    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
240        extract_cfg_attributes(self.attrs(), span)
241    }
242
243    /// Returns a list of `cfg` attributes as `syn::Attribute` if any.
244    pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
245        extract_cfg_syn_attributes(self.attrs())
246    }
247
248    /// Returns the return type of the ink! constructor if any.
249    pub fn output(&self) -> Option<&syn::Type> {
250        match &self.item.sig.output {
251            syn::ReturnType::Default => None,
252            syn::ReturnType::Type(_, return_type) => Some(return_type),
253        }
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260
261    #[test]
262    fn inputs_works() {
263        macro_rules! expected_inputs {
264            ( $( $name:ident: $ty:ty ),* ) => {{
265                vec![
266                    $(
267                        syn::parse_quote! {
268                            $name: $ty
269                        }
270                    ),*
271                ]
272            }};
273        }
274        let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
275            (
276                // No inputs:
277                expected_inputs!(),
278                syn::parse_quote! {
279                    #[ink(constructor)]
280                    fn my_constructor() -> Self {}
281                },
282            ),
283            (
284                // Single input:
285                expected_inputs!(a: i32),
286                syn::parse_quote! {
287                    #[ink(constructor)]
288                    fn my_constructor(a: i32) -> Self {}
289                },
290            ),
291            (
292                // Some inputs:
293                expected_inputs!(a: i32, b: u64, c: [u8; 32]),
294                syn::parse_quote! {
295                    #[ink(constructor)]
296                    fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
297                },
298            ),
299        ];
300        for (expected_inputs, item_method) in test_inputs {
301            let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
302                .unwrap()
303                .inputs()
304                .cloned()
305                .map(syn::FnArg::Typed)
306                .collect::<Vec<_>>();
307            assert_eq!(actual_inputs, expected_inputs);
308        }
309    }
310
311    #[test]
312    fn is_payable_works() {
313        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
314            // Not payable.
315            (
316                false,
317                syn::parse_quote! {
318                    #[ink(constructor)]
319                    fn my_constructor() -> Self {}
320                },
321            ),
322            // Normalized ink! attribute.
323            (
324                true,
325                syn::parse_quote! {
326                    #[ink(constructor, payable)]
327                    pub fn my_constructor() -> Self {}
328                },
329            ),
330            // Different ink! attributes.
331            (
332                true,
333                syn::parse_quote! {
334                    #[ink(constructor)]
335                    #[ink(payable)]
336                    pub fn my_constructor() -> Self {}
337                },
338            ),
339            // Another ink! attribute, separate and normalized attribute.
340            (
341                true,
342                syn::parse_quote! {
343                    #[ink(constructor)]
344                    #[ink(selector = 0xDEADBEEF, payable)]
345                    pub fn my_constructor() -> Self {}
346                },
347            ),
348        ];
349        for (expect_payable, item_method) in test_inputs {
350            let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
351                .unwrap()
352                .is_payable();
353            assert_eq!(is_payable, expect_payable);
354        }
355    }
356
357    #[test]
358    fn is_default_works() {
359        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
360            // Not default.
361            (
362                false,
363                syn::parse_quote! {
364                    #[ink(constructor)]
365                    fn my_constructor() -> Self {}
366                },
367            ),
368            // Default constructor.
369            (
370                true,
371                syn::parse_quote! {
372                    #[ink(constructor, default)]
373                    pub fn my_constructor() -> Self {}
374                },
375            ),
376        ];
377        for (expect_default, item_method) in test_inputs {
378            let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
379                .unwrap()
380                .is_default();
381            assert_eq!(is_default, expect_default);
382        }
383    }
384
385    #[test]
386    fn visibility_works() {
387        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
388            // inherited
389            (
390                false,
391                syn::parse_quote! {
392                    #[ink(constructor)]
393                    fn my_constructor() -> Self {}
394                },
395            ),
396            // public
397            (
398                true,
399                syn::parse_quote! {
400                    #[ink(constructor)]
401                    pub fn my_constructor() -> Self {}
402                },
403            ),
404        ];
405        for (is_pub, item_method) in test_inputs {
406            let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
407                .unwrap()
408                .visibility();
409            assert_eq!(visibility.is_pub(), is_pub);
410            assert_eq!(visibility.is_inherited(), !is_pub);
411        }
412    }
413
414    #[test]
415    fn try_from_works() {
416        let item_methods: Vec<syn::ImplItemFn> = vec![
417            // simple + inherited visibility
418            syn::parse_quote! {
419                #[ink(constructor)]
420                fn my_constructor() -> Self {}
421            },
422            // simple + public visibility
423            syn::parse_quote! {
424                #[ink(constructor)]
425                pub fn my_constructor() -> Self {}
426            },
427            // many inputs
428            syn::parse_quote! {
429                #[ink(constructor)]
430                fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
431            },
432            // Result return type
433            syn::parse_quote! {
434                #[ink(constructor)]
435                pub fn my_constructor() -> Result<Self, ()> {}
436            },
437        ];
438        for item_method in item_methods {
439            assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
440        }
441    }
442
443    fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
444        assert_eq!(
445            <ir::Constructor as TryFrom<_>>::try_from(item_method)
446                .map_err(|err| err.to_string()),
447            Err(expected_err.to_string()),
448        );
449    }
450
451    #[test]
452    fn try_from_missing_return_fails() {
453        let item_methods: Vec<syn::ImplItemFn> = vec![
454            syn::parse_quote! {
455                #[ink(constructor)]
456                fn my_constructor() {}
457            },
458            syn::parse_quote! {
459                #[ink(constructor)]
460                pub fn my_constructor() {}
461            },
462        ];
463        for item_method in item_methods {
464            assert_try_from_fails(item_method, "missing return for ink! constructor")
465        }
466    }
467
468    #[test]
469    fn try_from_invalid_self_receiver_fails() {
470        let item_methods: Vec<syn::ImplItemFn> = vec![
471            syn::parse_quote! {
472                #[ink(constructor)]
473                fn my_constructor(&self) -> Self {}
474            },
475            syn::parse_quote! {
476                #[ink(constructor)]
477                pub fn my_constructor(&mut self) -> Self {}
478            },
479            syn::parse_quote! {
480                #[ink(constructor)]
481                pub fn my_constructor(self) -> Self {}
482            },
483            syn::parse_quote! {
484                #[ink(constructor)]
485                pub fn my_constructor(mut self) -> Self {}
486            },
487        ];
488        for item_method in item_methods {
489            assert_try_from_fails(
490                item_method,
491                "ink! constructors must have no `self` receiver",
492            )
493        }
494    }
495
496    #[test]
497    fn try_from_generics_fails() {
498        let item_methods: Vec<syn::ImplItemFn> = vec![
499            syn::parse_quote! {
500                #[ink(constructor)]
501                fn my_constructor<T>() -> Self {}
502            },
503            syn::parse_quote! {
504                #[ink(constructor)]
505                pub fn my_constructor<T>() -> Self {}
506            },
507        ];
508        for item_method in item_methods {
509            assert_try_from_fails(item_method, "ink! constructors must not be generic")
510        }
511    }
512
513    #[test]
514    fn try_from_const_fails() {
515        let item_methods: Vec<syn::ImplItemFn> = vec![
516            syn::parse_quote! {
517                #[ink(constructor)]
518                const fn my_constructor() -> Self {}
519            },
520            syn::parse_quote! {
521                #[ink(constructor)]
522                pub const fn my_constructor() -> Self {}
523            },
524        ];
525        for item_method in item_methods {
526            assert_try_from_fails(item_method, "ink! constructors must not be const")
527        }
528    }
529
530    #[test]
531    fn try_from_async_fails() {
532        let item_methods: Vec<syn::ImplItemFn> = vec![
533            syn::parse_quote! {
534                #[ink(constructor)]
535                async fn my_constructor() -> Self {}
536            },
537            syn::parse_quote! {
538                #[ink(constructor)]
539                async fn my_constructor() -> Self {}
540            },
541        ];
542        for item_method in item_methods {
543            assert_try_from_fails(item_method, "ink! constructors must not be async")
544        }
545    }
546
547    #[test]
548    fn try_from_unsafe_fails() {
549        let item_methods: Vec<syn::ImplItemFn> = vec![
550            syn::parse_quote! {
551                #[ink(constructor)]
552                unsafe fn my_constructor() -> Self {}
553            },
554            syn::parse_quote! {
555                #[ink(constructor)]
556                unsafe fn my_constructor() -> Self {}
557            },
558        ];
559        for item_method in item_methods {
560            assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
561        }
562    }
563
564    #[test]
565    fn try_from_explicit_abi_fails() {
566        let item_methods: Vec<syn::ImplItemFn> = vec![
567            syn::parse_quote! {
568                #[ink(constructor)]
569                extern "C" fn my_constructor() -> Self {}
570            },
571            syn::parse_quote! {
572                #[ink(constructor)]
573                extern "C" fn my_constructor() -> Self {}
574            },
575        ];
576        for item_method in item_methods {
577            assert_try_from_fails(
578                item_method,
579                "ink! constructors must not have explicit ABI",
580            )
581        }
582    }
583
584    #[test]
585    fn try_from_variadic_fails() {
586        let item_methods: Vec<syn::ImplItemFn> = vec![
587            syn::parse_quote! {
588                #[ink(constructor)]
589                fn my_constructor(...) -> Self {}
590            },
591            syn::parse_quote! {
592                #[ink(constructor)]
593                fn my_constructor(...) -> Self {}
594            },
595        ];
596        for item_method in item_methods {
597            assert_try_from_fails(item_method, "ink! constructors must not be variadic")
598        }
599    }
600
601    #[test]
602    fn try_from_visibility_fails() {
603        let item_methods: Vec<syn::ImplItemFn> = vec![
604            syn::parse_quote! {
605                #[ink(constructor)]
606                pub(crate) fn my_constructor() -> Self {}
607            },
608            syn::parse_quote! {
609                #[ink(constructor)]
610                pub(in my::path) fn my_constructor() -> Self {}
611            },
612        ];
613        for item_method in item_methods {
614            assert_try_from_fails(
615                item_method,
616                "ink! constructors must have public or inherited visibility",
617            )
618        }
619    }
620
621    #[test]
622    fn conflicting_attributes_fails() {
623        let item_methods: Vec<syn::ImplItemFn> = vec![
624            // storage
625            syn::parse_quote! {
626                #[ink(constructor, storage)]
627                fn my_constructor() -> Self {}
628            },
629            // namespace
630            syn::parse_quote! {
631                #[ink(constructor, namespace = "my_namespace")]
632                fn my_constructor() -> Self {}
633            },
634            // event + multiple attributes
635            syn::parse_quote! {
636                #[ink(constructor)]
637                #[ink(event)]
638                fn my_constructor() -> Self {}
639            },
640        ];
641        for item_method in item_methods {
642            assert_try_from_fails(
643                item_method,
644                "encountered conflicting ink! attribute argument",
645            )
646        }
647    }
648
649    #[test]
650    fn try_from_wildcard_constructor_works() {
651        let item: syn::ImplItemFn = syn::parse_quote! {
652            #[ink(constructor, selector = _)]
653            pub fn my_constructor() -> Self {}
654        };
655        assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
656    }
657}