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        Ok(Constructor {
177            selector,
178            is_payable,
179            is_default,
180            name,
181            item: syn::ImplItemFn {
182                attrs: other_attrs,
183                ..method_item
184            },
185        })
186    }
187}
188
189impl Callable for Constructor {
190    fn kind(&self) -> CallableKind {
191        CallableKind::Constructor
192    }
193
194    fn ident(&self) -> &Ident {
195        &self.item.sig.ident
196    }
197
198    fn user_provided_selector(&self) -> Option<&ir::Selector> {
199        if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
200            return Some(selector)
201        }
202        None
203    }
204
205    fn has_wildcard_selector(&self) -> bool {
206        matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
207    }
208
209    fn has_wildcard_complement_selector(&self) -> bool {
210        self.selector == Some(SelectorOrWildcard::wildcard_complement())
211    }
212
213    fn is_payable(&self) -> bool {
214        self.is_payable
215    }
216
217    fn is_default(&self) -> bool {
218        self.is_default
219    }
220
221    fn visibility(&self) -> Visibility {
222        match &self.item.vis {
223            syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
224            syn::Visibility::Inherited => Visibility::Inherited,
225            _ => unreachable!("encountered invalid visibility for ink! constructor"),
226        }
227    }
228
229    fn inputs(&self) -> InputsIter<'_> {
230        InputsIter::from(self)
231    }
232
233    fn inputs_span(&self) -> Span {
234        self.item.sig.inputs.span()
235    }
236
237    fn statements(&self) -> &[syn::Stmt] {
238        &self.item.block.stmts
239    }
240
241    fn name(&self) -> Option<&str> {
242        self.name.as_deref()
243    }
244}
245
246impl Constructor {
247    /// Returns a slice of all non-ink! attributes of the ink! constructor.
248    pub fn attrs(&self) -> &[syn::Attribute] {
249        &self.item.attrs
250    }
251
252    /// Returns a list of `cfg` attributes if any.
253    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
254        extract_cfg_attributes(self.attrs(), span)
255    }
256
257    /// Returns a list of `cfg` attributes as `syn::Attribute` if any.
258    pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
259        extract_cfg_syn_attributes(self.attrs())
260    }
261
262    /// Returns the return type of the ink! constructor if any.
263    pub fn output(&self) -> Option<&syn::Type> {
264        match &self.item.sig.output {
265            syn::ReturnType::Default => None,
266            syn::ReturnType::Type(_, return_type) => Some(return_type),
267        }
268    }
269
270    /// Returns the function name override (if any).
271    pub fn name(&self) -> Option<&str> {
272        self.name.as_deref()
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279
280    #[test]
281    fn inputs_works() {
282        macro_rules! expected_inputs {
283            ( $( $name:ident: $ty:ty ),* ) => {{
284                vec![
285                    $(
286                        syn::parse_quote! {
287                            $name: $ty
288                        }
289                    ),*
290                ]
291            }};
292        }
293        let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
294            (
295                // No inputs:
296                expected_inputs!(),
297                syn::parse_quote! {
298                    #[ink(constructor)]
299                    fn my_constructor() -> Self {}
300                },
301            ),
302            (
303                // Single input:
304                expected_inputs!(a: i32),
305                syn::parse_quote! {
306                    #[ink(constructor)]
307                    fn my_constructor(a: i32) -> Self {}
308                },
309            ),
310            (
311                // Some inputs:
312                expected_inputs!(a: i32, b: u64, c: [u8; 32]),
313                syn::parse_quote! {
314                    #[ink(constructor)]
315                    fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
316                },
317            ),
318        ];
319        for (expected_inputs, item_method) in test_inputs {
320            let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
321                .unwrap()
322                .inputs()
323                .cloned()
324                .map(syn::FnArg::Typed)
325                .collect::<Vec<_>>();
326            assert_eq!(actual_inputs, expected_inputs);
327        }
328    }
329
330    #[test]
331    fn is_payable_works() {
332        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
333            // Not payable.
334            (
335                false,
336                syn::parse_quote! {
337                    #[ink(constructor)]
338                    fn my_constructor() -> Self {}
339                },
340            ),
341            // Normalized ink! attribute.
342            (
343                true,
344                syn::parse_quote! {
345                    #[ink(constructor, payable)]
346                    pub fn my_constructor() -> Self {}
347                },
348            ),
349            // Different ink! attributes.
350            (
351                true,
352                syn::parse_quote! {
353                    #[ink(constructor)]
354                    #[ink(payable)]
355                    pub fn my_constructor() -> Self {}
356                },
357            ),
358            // Another ink! attribute, separate and normalized attribute.
359            (
360                true,
361                syn::parse_quote! {
362                    #[ink(constructor)]
363                    #[ink(selector = 0xDEADBEEF, payable)]
364                    pub fn my_constructor() -> Self {}
365                },
366            ),
367        ];
368        for (expect_payable, item_method) in test_inputs {
369            let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
370                .unwrap()
371                .is_payable();
372            assert_eq!(is_payable, expect_payable);
373        }
374    }
375
376    #[test]
377    fn is_default_works() {
378        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
379            // Not default.
380            (
381                false,
382                syn::parse_quote! {
383                    #[ink(constructor)]
384                    fn my_constructor() -> Self {}
385                },
386            ),
387            // Default constructor.
388            (
389                true,
390                syn::parse_quote! {
391                    #[ink(constructor, default)]
392                    pub fn my_constructor() -> Self {}
393                },
394            ),
395        ];
396        for (expect_default, item_method) in test_inputs {
397            let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
398                .unwrap()
399                .is_default();
400            assert_eq!(is_default, expect_default);
401        }
402    }
403
404    #[test]
405    fn name_override_works() {
406        let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
407            // No name override.
408            (
409                None,
410                syn::parse_quote! {
411                    #[ink(constructor)]
412                    fn my_constructor() -> Self {}
413                },
414            ),
415            // Name override.
416            (
417                Some("myConstructor"),
418                syn::parse_quote! {
419                    #[ink(constructor, name = "myConstructor")]
420                    pub fn my_constructor() -> Self {}
421                },
422            ),
423        ];
424        for (expected_name, item_method) in test_inputs {
425            let ctor = <ir::Constructor as TryFrom<_>>::try_from(item_method).unwrap();
426            assert_eq!(ctor.name(), expected_name);
427        }
428    }
429
430    #[test]
431    fn visibility_works() {
432        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
433            // inherited
434            (
435                false,
436                syn::parse_quote! {
437                    #[ink(constructor)]
438                    fn my_constructor() -> Self {}
439                },
440            ),
441            // public
442            (
443                true,
444                syn::parse_quote! {
445                    #[ink(constructor)]
446                    pub fn my_constructor() -> Self {}
447                },
448            ),
449        ];
450        for (is_pub, item_method) in test_inputs {
451            let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
452                .unwrap()
453                .visibility();
454            assert_eq!(visibility.is_pub(), is_pub);
455            assert_eq!(visibility.is_inherited(), !is_pub);
456        }
457    }
458
459    #[test]
460    fn try_from_works() {
461        let item_methods: Vec<syn::ImplItemFn> = vec![
462            // simple + inherited visibility
463            syn::parse_quote! {
464                #[ink(constructor)]
465                fn my_constructor() -> Self {}
466            },
467            // simple + public visibility
468            syn::parse_quote! {
469                #[ink(constructor)]
470                pub fn my_constructor() -> Self {}
471            },
472            // many inputs
473            syn::parse_quote! {
474                #[ink(constructor)]
475                fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
476            },
477            // Result return type
478            syn::parse_quote! {
479                #[ink(constructor)]
480                pub fn my_constructor() -> Result<Self, ()> {}
481            },
482        ];
483        for item_method in item_methods {
484            assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
485        }
486    }
487
488    fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
489        assert_eq!(
490            <ir::Constructor as TryFrom<_>>::try_from(item_method)
491                .map_err(|err| err.to_string()),
492            Err(expected_err.to_string()),
493        );
494    }
495
496    #[test]
497    fn try_from_missing_return_fails() {
498        let item_methods: Vec<syn::ImplItemFn> = vec![
499            syn::parse_quote! {
500                #[ink(constructor)]
501                fn my_constructor() {}
502            },
503            syn::parse_quote! {
504                #[ink(constructor)]
505                pub fn my_constructor() {}
506            },
507        ];
508        for item_method in item_methods {
509            assert_try_from_fails(item_method, "missing return for ink! constructor")
510        }
511    }
512
513    #[test]
514    fn try_from_invalid_self_receiver_fails() {
515        let item_methods: Vec<syn::ImplItemFn> = vec![
516            syn::parse_quote! {
517                #[ink(constructor)]
518                fn my_constructor(&self) -> Self {}
519            },
520            syn::parse_quote! {
521                #[ink(constructor)]
522                pub fn my_constructor(&mut self) -> Self {}
523            },
524            syn::parse_quote! {
525                #[ink(constructor)]
526                pub fn my_constructor(self) -> Self {}
527            },
528            syn::parse_quote! {
529                #[ink(constructor)]
530                pub fn my_constructor(mut self) -> Self {}
531            },
532        ];
533        for item_method in item_methods {
534            assert_try_from_fails(
535                item_method,
536                "ink! constructors must have no `self` receiver",
537            )
538        }
539    }
540
541    #[test]
542    fn try_from_generics_fails() {
543        let item_methods: Vec<syn::ImplItemFn> = vec![
544            syn::parse_quote! {
545                #[ink(constructor)]
546                fn my_constructor<T>() -> Self {}
547            },
548            syn::parse_quote! {
549                #[ink(constructor)]
550                pub fn my_constructor<T>() -> Self {}
551            },
552        ];
553        for item_method in item_methods {
554            assert_try_from_fails(item_method, "ink! constructors must not be generic")
555        }
556    }
557
558    #[test]
559    fn try_from_const_fails() {
560        let item_methods: Vec<syn::ImplItemFn> = vec![
561            syn::parse_quote! {
562                #[ink(constructor)]
563                const fn my_constructor() -> Self {}
564            },
565            syn::parse_quote! {
566                #[ink(constructor)]
567                pub const fn my_constructor() -> Self {}
568            },
569        ];
570        for item_method in item_methods {
571            assert_try_from_fails(item_method, "ink! constructors must not be const")
572        }
573    }
574
575    #[test]
576    fn try_from_async_fails() {
577        let item_methods: Vec<syn::ImplItemFn> = vec![
578            syn::parse_quote! {
579                #[ink(constructor)]
580                async fn my_constructor() -> Self {}
581            },
582            syn::parse_quote! {
583                #[ink(constructor)]
584                async fn my_constructor() -> Self {}
585            },
586        ];
587        for item_method in item_methods {
588            assert_try_from_fails(item_method, "ink! constructors must not be async")
589        }
590    }
591
592    #[test]
593    fn try_from_unsafe_fails() {
594        let item_methods: Vec<syn::ImplItemFn> = vec![
595            syn::parse_quote! {
596                #[ink(constructor)]
597                unsafe fn my_constructor() -> Self {}
598            },
599            syn::parse_quote! {
600                #[ink(constructor)]
601                unsafe fn my_constructor() -> Self {}
602            },
603        ];
604        for item_method in item_methods {
605            assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
606        }
607    }
608
609    #[test]
610    fn try_from_explicit_abi_fails() {
611        let item_methods: Vec<syn::ImplItemFn> = vec![
612            syn::parse_quote! {
613                #[ink(constructor)]
614                extern "C" fn my_constructor() -> Self {}
615            },
616            syn::parse_quote! {
617                #[ink(constructor)]
618                extern "C" fn my_constructor() -> Self {}
619            },
620        ];
621        for item_method in item_methods {
622            assert_try_from_fails(
623                item_method,
624                "ink! constructors must not have explicit ABI",
625            )
626        }
627    }
628
629    #[test]
630    fn try_from_variadic_fails() {
631        let item_methods: Vec<syn::ImplItemFn> = vec![
632            syn::parse_quote! {
633                #[ink(constructor)]
634                fn my_constructor(...) -> Self {}
635            },
636            syn::parse_quote! {
637                #[ink(constructor)]
638                fn my_constructor(...) -> Self {}
639            },
640        ];
641        for item_method in item_methods {
642            assert_try_from_fails(item_method, "ink! constructors must not be variadic")
643        }
644    }
645
646    #[test]
647    fn try_from_visibility_fails() {
648        let item_methods: Vec<syn::ImplItemFn> = vec![
649            syn::parse_quote! {
650                #[ink(constructor)]
651                pub(crate) fn my_constructor() -> Self {}
652            },
653            syn::parse_quote! {
654                #[ink(constructor)]
655                pub(in my::path) fn my_constructor() -> Self {}
656            },
657        ];
658        for item_method in item_methods {
659            assert_try_from_fails(
660                item_method,
661                "ink! constructors must have public or inherited visibility",
662            )
663        }
664    }
665
666    #[test]
667    fn conflicting_attributes_fails() {
668        let item_methods: Vec<syn::ImplItemFn> = vec![
669            // storage
670            syn::parse_quote! {
671                #[ink(constructor, storage)]
672                fn my_constructor() -> Self {}
673            },
674            // namespace
675            syn::parse_quote! {
676                #[ink(constructor, namespace = "my_namespace")]
677                fn my_constructor() -> Self {}
678            },
679            // event + multiple attributes
680            syn::parse_quote! {
681                #[ink(constructor)]
682                #[ink(event)]
683                fn my_constructor() -> Self {}
684            },
685        ];
686        for item_method in item_methods {
687            assert_try_from_fails(
688                item_method,
689                "encountered conflicting ink! attribute argument",
690            )
691        }
692    }
693
694    #[test]
695    fn try_from_wildcard_constructor_works() {
696        let item: syn::ImplItemFn = syn::parse_quote! {
697            #[ink(constructor, selector = _)]
698            pub fn my_constructor() -> Self {}
699        };
700        assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
701    }
702}