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