ink_ir/ir/item_impl/
message.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::ir::{
23    self,
24    attrs::SelectorOrWildcard,
25    utils,
26    utils::{
27        extract_cfg_attributes,
28        extract_cfg_syn_attributes,
29    },
30};
31use proc_macro2::{
32    Ident,
33    Span,
34    TokenStream,
35};
36use syn::spanned::Spanned as _;
37
38/// The receiver of an ink! message.
39#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40pub enum Receiver {
41    /// The `&self` message receiver.
42    Ref,
43    /// The `&mut self` message receiver.
44    RefMut,
45}
46
47impl quote::ToTokens for Receiver {
48    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
49        let receiver = match self {
50            Self::Ref => quote::quote! { &self },
51            Self::RefMut => quote::quote! { &mut self },
52        };
53        tokens.extend(receiver);
54    }
55}
56
57impl Receiver {
58    /// Returns `true` if the receiver is `&self`.
59    pub fn is_ref(self) -> bool {
60        matches!(self, Self::Ref)
61    }
62
63    /// Returns `true` if the receiver is `&mut self`.
64    pub fn is_ref_mut(self) -> bool {
65        matches!(self, Self::RefMut)
66    }
67}
68
69/// An ink! message definition.
70///
71/// # Example
72///
73/// ## Inherent implementation message:
74///
75/// ```
76/// # <ink_ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(syn::parse_quote! {
77/// impl MyStorage {
78///     #[ink(message)]
79///     pub fn my_message(&self, input: i32) -> bool {
80///         // message implementation goes here
81/// #       unimplemented!()
82///     }
83/// }
84/// # }).unwrap();
85/// ```
86///
87/// ## Trait implementation message:
88///
89/// ```
90/// # let event = <ink_ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(syn::parse_quote! {
91/// impl MyTrait for MyStorage {
92///     #[ink(message)]
93///     fn my_message(&mut self, input: bool) -> i32 {
94///         /* message implementation goes here */
95/// #       unimplemented!()
96///     }
97/// }
98/// # }).unwrap();
99/// ```
100#[derive(Debug, PartialEq, Eq)]
101pub struct Message {
102    /// The underlying Rust method item.
103    pub(super) item: syn::ImplItemFn,
104    /// If the ink! message can receive funds.
105    is_payable: bool,
106    /// If the ink! message is default.
107    is_default: bool,
108    /// An optional user provided selector.
109    ///
110    /// # Note
111    ///
112    /// This overrides the computed selector, even when using a manual namespace
113    /// for the parent implementation block.
114    selector: Option<SelectorOrWildcard>,
115    /// An optional function name override.
116    ///
117    /// # Note
118    ///
119    /// - Useful for defining overloaded interfaces.
120    /// - If provided, the name must be a valid "identifier-like" string.
121    name: Option<String>,
122}
123
124impl quote::ToTokens for Message {
125    /// We mainly implement this trait for this ink! type to have a derived
126    /// [`Spanned`](`syn::spanned::Spanned`) implementation for it.
127    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
128        self.item.to_tokens(tokens)
129    }
130}
131
132impl Message {
133    /// Returns the self reference receiver (if any), given method inputs.
134    ///
135    /// If not an appropriate error is returned.
136    ///
137    /// # Errors
138    ///
139    /// - If the method inputs yields no elements.
140    /// - If the first method input is not `&self` or `&mut self`.
141    fn self_ref_receiver(
142        method_item: &syn::ImplItemFn,
143    ) -> Result<&syn::Receiver, syn::Error> {
144        let mut fn_args = method_item.sig.inputs.iter();
145        fn bail(span: Span) -> syn::Error {
146            format_err!(
147                span,
148                "ink! messages must have `&self` or `&mut self` receiver",
149            )
150        }
151        match fn_args.next() {
152            None => Err(bail(method_item.span())),
153            Some(syn::FnArg::Typed(pat_typed)) => Err(bail(pat_typed.span())),
154            Some(syn::FnArg::Receiver(receiver)) => {
155                match receiver.reference {
156                    None => Err(bail(receiver.span())),
157                    Some(_) => Ok(receiver),
158                }
159            }
160        }
161    }
162
163    /// Ensures that the ink! message does not return `Self`.
164    ///
165    /// # Errors
166    ///
167    /// If the given Rust method has a `Self` return type.
168    fn ensure_not_return_self(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
169        match &method_item.sig.output {
170            syn::ReturnType::Default => (),
171            syn::ReturnType::Type(_arrow, ret_type) => {
172                if let syn::Type::Path(type_path) = &**ret_type
173                    && type_path.path.is_ident("Self")
174                {
175                    return Err(format_err!(
176                        ret_type,
177                        "ink! messages must not return `Self`"
178                    ))
179                }
180            }
181        }
182        Ok(())
183    }
184
185    /// Sanitizes the attributes for the ink! message.
186    ///
187    /// Returns a tuple of ink! attributes and non-ink! attributes.
188    fn sanitize_attributes(
189        method_item: &syn::ImplItemFn,
190    ) -> Result<(ir::InkAttribute, Vec<syn::Attribute>), syn::Error> {
191        ir::sanitize_attributes(
192            method_item.span(),
193            method_item.attrs.clone(),
194            &ir::AttributeArgKind::Message,
195            |arg| {
196                match arg.kind() {
197                    ir::AttributeArg::Message
198                    | ir::AttributeArg::Payable
199                    | ir::AttributeArg::Default
200                    | ir::AttributeArg::Selector(_)
201                    | ir::AttributeArg::Name(_) => Ok(()),
202                    _ => Err(None),
203                }
204            },
205        )
206    }
207}
208
209impl TryFrom<syn::ImplItemFn> for Message {
210    type Error = syn::Error;
211
212    fn try_from(method_item: syn::ImplItemFn) -> Result<Self, Self::Error> {
213        ensure_callable_invariants(&method_item, CallableKind::Message)?;
214        // Ensures that the given method inputs start with `&self` or `&mut self`
215        // receivers.
216        let self_ref_receiver = Self::self_ref_receiver(&method_item)?;
217        Self::ensure_not_return_self(&method_item)?;
218        let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
219        let is_payable = ink_attrs.is_payable();
220        let is_default = ink_attrs.is_default();
221        let selector = ink_attrs.selector();
222        let name = ink_attrs.name();
223        // Ensures that immutable messages are NOT payable.
224        if is_payable && self_ref_receiver.mutability.is_none() {
225            return Err(format_err!(
226                method_item.span(),
227                "ink! messages with a `payable` attribute argument must have a `&mut self` receiver",
228            ));
229        }
230        #[cfg(ink_abi = "sol")]
231        if selector.is_some() {
232            let selector_span = ink_attrs.args().find_map(|arg| {
233                matches!(arg.kind(), ir::AttributeArg::Selector(_)).then_some(arg.span())
234            });
235            return Err(format_err!(
236                selector_span.unwrap_or_else(|| method_item.span()),
237                "message `selector` attributes are not supported in Solidity ABI compatibility mode",
238            ));
239        }
240        Ok(Self {
241            is_payable,
242            is_default,
243            selector,
244            name,
245            item: syn::ImplItemFn {
246                attrs: other_attrs,
247                ..method_item
248            },
249        })
250    }
251}
252
253impl Callable for Message {
254    fn kind(&self) -> CallableKind {
255        CallableKind::Message
256    }
257
258    fn ident(&self) -> &Ident {
259        &self.item.sig.ident
260    }
261
262    fn user_provided_selector(&self) -> Option<&ir::Selector> {
263        if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
264            return Some(selector)
265        }
266        None
267    }
268
269    fn has_wildcard_selector(&self) -> bool {
270        matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
271    }
272
273    fn has_wildcard_complement_selector(&self) -> bool {
274        self.selector == Some(SelectorOrWildcard::wildcard_complement())
275    }
276
277    fn is_payable(&self) -> bool {
278        self.is_payable
279    }
280
281    fn is_default(&self) -> bool {
282        self.is_default
283    }
284
285    fn visibility(&self) -> Visibility {
286        match &self.item.vis {
287            syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
288            syn::Visibility::Inherited => Visibility::Inherited,
289            _ => unreachable!("encountered invalid visibility for ink! message"),
290        }
291    }
292
293    fn inputs(&self) -> InputsIter<'_> {
294        InputsIter::from(self)
295    }
296
297    fn inputs_span(&self) -> Span {
298        self.item.sig.inputs.span()
299    }
300
301    fn statements(&self) -> &[syn::Stmt] {
302        &self.item.block.stmts
303    }
304
305    fn name(&self) -> Option<&str> {
306        self.name.as_deref()
307    }
308
309    fn normalized_name(&self) -> String {
310        self.normalized_name()
311    }
312}
313
314impl Message {
315    /// Returns a slice of all non-ink! attributes of the ink! message.
316    pub fn attrs(&self) -> &[syn::Attribute] {
317        &self.item.attrs
318    }
319
320    /// Returns a list of `cfg` attributes if any.
321    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
322        extract_cfg_attributes(self.attrs(), span)
323    }
324
325    /// Returns a list of `cfg` attributes as `syn::Attribute` if any.
326    pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
327        extract_cfg_syn_attributes(self.attrs())
328    }
329
330    /// Returns the `self` receiver of the ink! message.
331    pub fn receiver(&self) -> Receiver {
332        match self.item.sig.inputs.iter().next() {
333            Some(syn::FnArg::Receiver(receiver)) => {
334                debug_assert!(receiver.reference.is_some());
335                if receiver.mutability.is_some() {
336                    Receiver::RefMut
337                } else {
338                    Receiver::Ref
339                }
340            }
341            _ => unreachable!("encountered invalid receiver argument for ink! message"),
342        }
343    }
344
345    /// Returns the return type of the ink! message if any.
346    pub fn output(&self) -> Option<&syn::Type> {
347        match &self.item.sig.output {
348            syn::ReturnType::Default => None,
349            syn::ReturnType::Type(_, return_type) => Some(return_type),
350        }
351    }
352
353    /// Returns the return type of the message, but wrapped within a `Result`.
354    ///
355    /// This is used to to allow callers to handle certain types of errors which are not
356    /// exposed by messages.
357    pub fn wrapped_output(&self) -> syn::Type {
358        let return_type = self
359            .output()
360            .map(quote::ToTokens::to_token_stream)
361            .unwrap_or_else(|| quote::quote! { () });
362
363        syn::parse_quote! {
364            ::ink::MessageResult<#return_type>
365        }
366    }
367
368    /// Returns a local ID unique to the ink! message with respect to its implementation
369    /// block.
370    ///
371    /// # Note
372    ///
373    /// It is a compile error if two ink! trait messages share the same local ID.
374    /// Although the above scenario is very unlikely since the local ID is computed
375    /// solely by the identifier of the ink! message.
376    pub fn local_id(&self) -> u32 {
377        utils::local_message_id(&self.normalized_name())
378    }
379
380    /// Returns the identifier of the message with an additional `try_` prefix attached.
381    pub fn try_ident(&self) -> Ident {
382        quote::format_ident!("try_{}", self.ident())
383    }
384
385    /// Returns the function name override (if any).
386    pub fn name(&self) -> Option<&str> {
387        self.name.as_deref()
388    }
389
390    /// Returns the "normalized" function name
391    ///
392    /// # Note
393    /// This returns the name override (if provided), otherwise the identifier is
394    /// returned.
395    pub fn normalized_name(&self) -> String {
396        self.name()
397            .map(ToString::to_string)
398            .unwrap_or_else(|| self.ident().to_string())
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405
406    #[test]
407    fn output_works() {
408        let test_inputs: Vec<(Option<syn::Type>, syn::ImplItemFn)> = vec![
409            (
410                // No output:
411                None,
412                syn::parse_quote! {
413                    #[ink(message)]
414                    fn my_message(&self) {}
415                },
416            ),
417            (
418                // Single output:
419                Some(syn::parse_quote! { i32 }),
420                syn::parse_quote! {
421                    #[ink(message)]
422                    fn my_message(&self) -> i32 {}
423                },
424            ),
425            (
426                // Tuple output:
427                Some(syn::parse_quote! { (i32, u64, bool) }),
428                syn::parse_quote! {
429                    #[ink(message)]
430                    fn my_message(&self) -> (i32, u64, bool) {}
431                },
432            ),
433        ];
434        for (expected_output, item_method) in test_inputs {
435            let actual_output = <ir::Message as TryFrom<_>>::try_from(item_method)
436                .unwrap()
437                .output()
438                .cloned();
439            assert_eq!(actual_output, expected_output);
440        }
441    }
442
443    #[test]
444    fn inputs_works() {
445        macro_rules! expected_input {
446            ( mut $name:ident: $ty:ty  ) => {{
447                syn::parse_quote! {
448                    mut $name: $ty
449                }
450            }};
451            ( $name:ident: $ty:ty  ) => {{
452                syn::parse_quote! {
453                    $name: $ty
454                }
455            }};
456        }
457        macro_rules! expected_inputs {
458            ( $( $($ts:ident)+: $ty:ty ),* ) => {{
459                vec![
460                    $(
461                        expected_input!($($ts)+: $ty)
462                    ),*
463                ]
464            }};
465        }
466        let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
467            (
468                // No inputs:
469                expected_inputs!(),
470                syn::parse_quote! {
471                    #[ink(message)]
472                    fn my_message(&self) {}
473                },
474            ),
475            (
476                // Single input:
477                expected_inputs!(a: i32),
478                syn::parse_quote! {
479                    #[ink(message)]
480                    fn my_message(&self, a: i32) {}
481                },
482            ),
483            (
484                // Single mutable input:
485                expected_inputs!(mut a: i32),
486                syn::parse_quote! {
487                    #[ink(message)]
488                    fn my_message(&self, mut a: i32) {}
489                },
490            ),
491            (
492                // Some inputs:
493                expected_inputs!(a: i32, b: u64, mut c: [u8; 32]),
494                syn::parse_quote! {
495                    #[ink(message)]
496                    fn my_message(&self, a: i32, b: u64, mut c: [u8; 32]) {}
497                },
498            ),
499        ];
500        for (expected_inputs, item_method) in test_inputs {
501            let actual_inputs = <ir::Message as TryFrom<_>>::try_from(item_method)
502                .unwrap()
503                .inputs()
504                .cloned()
505                .map(syn::FnArg::Typed)
506                .collect::<Vec<_>>();
507            assert_eq!(actual_inputs, expected_inputs);
508        }
509    }
510
511    #[test]
512    fn is_payable_works() {
513        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
514            // Not payable.
515            (
516                false,
517                syn::parse_quote! {
518                    #[ink(message)]
519                    fn my_message(&self) {}
520                },
521            ),
522            // Normalized ink! attribute.
523            (
524                true,
525                syn::parse_quote! {
526                    #[ink(message, payable)]
527                    pub fn my_message(&mut self) {}
528                },
529            ),
530            // Different ink! attributes.
531            (
532                true,
533                syn::parse_quote! {
534                    #[ink(message)]
535                    #[ink(payable)]
536                    pub fn my_message(&mut self) {}
537                },
538            ),
539            // Another ink! attribute, separate and normalized attribute.
540            (
541                true,
542                syn::parse_quote! {
543                    #[ink(message)]
544                    #[ink(selector = 0xDEADBEEF, payable)]
545                    pub fn my_message(&mut self) {}
546                },
547            ),
548        ];
549        for (expect_payable, item_method) in test_inputs {
550            let is_payable = <ir::Message as TryFrom<_>>::try_from(item_method)
551                .unwrap()
552                .is_payable();
553            assert_eq!(is_payable, expect_payable);
554        }
555    }
556
557    #[test]
558    fn is_default_works() {
559        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
560            // Not default.
561            (
562                false,
563                syn::parse_quote! {
564                    #[ink(message)]
565                    fn my_message(&self) {}
566                },
567            ),
568            // Default message.
569            (
570                true,
571                syn::parse_quote! {
572                    #[ink(message, payable, default)]
573                    pub fn my_message(&mut self) {}
574                },
575            ),
576        ];
577        for (expect_default, item_method) in test_inputs {
578            let is_default = <ir::Message as TryFrom<_>>::try_from(item_method)
579                .unwrap()
580                .is_default();
581            assert_eq!(is_default, expect_default);
582        }
583    }
584
585    #[test]
586    fn name_override_works() {
587        let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
588            // No name override.
589            (
590                None,
591                syn::parse_quote! {
592                    #[ink(message)]
593                    fn my_message(&self) {}
594                },
595            ),
596            // Name override.
597            (
598                Some("myMessage"),
599                syn::parse_quote! {
600                    #[ink(message, name = "myMessage")]
601                    pub fn my_message(&mut self) {}
602                },
603            ),
604        ];
605        for (expected_name, item_method) in test_inputs {
606            let message = <ir::Message as TryFrom<_>>::try_from(item_method).unwrap();
607            assert_eq!(message.name(), expected_name);
608        }
609    }
610
611    #[test]
612    fn receiver_works() {
613        let test_inputs: Vec<(Receiver, syn::ImplItemFn)> = vec![
614            (
615                Receiver::Ref,
616                syn::parse_quote! {
617                    #[ink(message)]
618                    fn my_message(&self) {}
619                },
620            ),
621            (
622                Receiver::RefMut,
623                syn::parse_quote! {
624                    #[ink(message, payable)]
625                    fn my_message(&mut self) {}
626                },
627            ),
628        ];
629        for (expected_receiver, item_method) in test_inputs {
630            let actual_receiver = <ir::Message as TryFrom<_>>::try_from(item_method)
631                .unwrap()
632                .receiver();
633            assert_eq!(actual_receiver, expected_receiver);
634        }
635    }
636
637    #[test]
638    fn visibility_works() {
639        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
640            // &self
641            (
642                false,
643                syn::parse_quote! {
644                    #[ink(message)]
645                    fn my_message(&self) {}
646                },
647            ),
648            // &self + pub
649            (
650                true,
651                syn::parse_quote! {
652                    #[ink(message)]
653                    pub fn my_message(&self) {}
654                },
655            ),
656            // &mut self
657            (
658                false,
659                syn::parse_quote! {
660                    #[ink(message)]
661                    fn my_message(&mut self) {}
662                },
663            ),
664            // &mut self + pub
665            (
666                true,
667                syn::parse_quote! {
668                    #[ink(message)]
669                    pub fn my_message(&mut self) {}
670                },
671            ),
672        ];
673        for (is_pub, item_method) in test_inputs {
674            let visibility = <ir::Message as TryFrom<_>>::try_from(item_method)
675                .unwrap()
676                .visibility();
677            assert_eq!(visibility.is_pub(), is_pub);
678            assert_eq!(visibility.is_inherited(), !is_pub);
679        }
680    }
681
682    #[test]
683    fn try_from_works() {
684        let item_methods: Vec<syn::ImplItemFn> = vec![
685            // &self
686            syn::parse_quote! {
687                #[ink(message)]
688                fn my_message(&self) {}
689            },
690            // &self + pub
691            syn::parse_quote! {
692                #[ink(message)]
693                pub fn my_message(&self) {}
694            },
695            // &mut self
696            syn::parse_quote! {
697                #[ink(message)]
698                fn my_message(&mut self) {}
699            },
700            // &mut self + pub
701            syn::parse_quote! {
702                #[ink(message)]
703                pub fn my_message(&mut self) {}
704            },
705            // &self + payable
706            syn::parse_quote! {
707                #[ink(message, payable)]
708                fn my_message(&mut self) {}
709            },
710            // &mut self + payable
711            syn::parse_quote! {
712                #[ink(message, payable)]
713                fn my_message(&mut self) {}
714            },
715            // &self + many inputs + output works
716            syn::parse_quote! {
717                #[ink(message)]
718                fn my_message(&self, input1: i32, input2: i64, input3: u32, input4: u64) -> bool {}
719            },
720            // &mut self + many inputs + output works
721            syn::parse_quote! {
722                #[ink(message)]
723                fn my_message(&mut self, input1: i32, input2: i64, input3: u32, input4: u64) -> bool {}
724            },
725        ];
726        for item_method in item_methods {
727            assert!(<ir::Message as TryFrom<_>>::try_from(item_method).is_ok());
728        }
729    }
730
731    fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
732        assert_eq!(
733            <ir::Message as TryFrom<_>>::try_from(item_method)
734                .map_err(|err| err.to_string()),
735            Err(expected_err.to_string()),
736        );
737    }
738
739    #[test]
740    fn try_from_generics_fails() {
741        let item_methods: Vec<syn::ImplItemFn> = vec![
742            syn::parse_quote! {
743                #[ink(message)]
744                fn my_message<T>(&self) {}
745            },
746            syn::parse_quote! {
747                #[ink(message)]
748                pub fn my_message<T>(&self) {}
749            },
750            syn::parse_quote! {
751                #[ink(message)]
752                fn my_message<T>(&mut self) {}
753            },
754            syn::parse_quote! {
755                #[ink(message)]
756                pub fn my_message<T>(&mut self) {}
757            },
758        ];
759        for item_method in item_methods {
760            assert_try_from_fails(item_method, "ink! messages must not be generic")
761        }
762    }
763
764    #[test]
765    fn try_from_receiver_fails() {
766        let item_methods: Vec<syn::ImplItemFn> = vec![
767            syn::parse_quote! {
768                #[ink(message)]
769                fn my_message() {}
770            },
771            syn::parse_quote! {
772                #[ink(message)]
773                fn my_message(self) {}
774            },
775            syn::parse_quote! {
776                #[ink(message)]
777                pub fn my_message(mut self) {}
778            },
779            syn::parse_quote! {
780                #[ink(message)]
781                fn my_message(this: &Self) {}
782            },
783            syn::parse_quote! {
784                #[ink(message)]
785                pub fn my_message(this: &mut Self) {}
786            },
787        ];
788        for item_method in item_methods {
789            assert_try_from_fails(
790                item_method,
791                "ink! messages must have `&self` or `&mut self` receiver",
792            )
793        }
794    }
795
796    #[test]
797    fn try_from_const_fails() {
798        let item_methods: Vec<syn::ImplItemFn> = vec![
799            // &self
800            syn::parse_quote! {
801                #[ink(message)]
802                const fn my_message(&self) {}
803            },
804            // &mut self
805            syn::parse_quote! {
806                #[ink(message)]
807                const fn my_message(&mut self) {}
808            },
809        ];
810        for item_method in item_methods {
811            assert_try_from_fails(item_method, "ink! messages must not be const")
812        }
813    }
814
815    #[test]
816    fn try_from_async_fails() {
817        let item_methods: Vec<syn::ImplItemFn> = vec![
818            // &self
819            syn::parse_quote! {
820                #[ink(message)]
821                async fn my_message(&self) {}
822            },
823            // &mut self
824            syn::parse_quote! {
825                #[ink(message)]
826                async fn my_message(&mut self) {}
827            },
828        ];
829        for item_method in item_methods {
830            assert_try_from_fails(item_method, "ink! messages must not be async")
831        }
832    }
833
834    #[test]
835    fn try_from_unsafe_fails() {
836        let item_methods: Vec<syn::ImplItemFn> = vec![
837            // &self
838            syn::parse_quote! {
839                #[ink(message)]
840                unsafe fn my_message(&self) {}
841            },
842            // &mut self
843            syn::parse_quote! {
844                #[ink(message)]
845                unsafe fn my_message(&mut self) {}
846            },
847        ];
848        for item_method in item_methods {
849            assert_try_from_fails(item_method, "ink! messages must not be unsafe")
850        }
851    }
852
853    #[test]
854    fn try_from_explicit_abi_fails() {
855        let item_methods: Vec<syn::ImplItemFn> = vec![
856            // &self
857            syn::parse_quote! {
858                #[ink(message)]
859                extern "C" fn my_message(&self) {}
860            },
861            // &mut self
862            syn::parse_quote! {
863                #[ink(message)]
864                extern "C" fn my_message(&mut self) {}
865            },
866        ];
867        for item_method in item_methods {
868            assert_try_from_fails(item_method, "ink! messages must not have explicit ABI")
869        }
870    }
871
872    #[test]
873    fn try_from_variadic_fails() {
874        let item_methods: Vec<syn::ImplItemFn> = vec![
875            // &self
876            syn::parse_quote! {
877                #[ink(message)]
878                fn my_message(&self, ...) {}
879            },
880            // &mut self
881            syn::parse_quote! {
882                #[ink(message)]
883                fn my_message(&mut self, ...) {}
884            },
885        ];
886        for item_method in item_methods {
887            assert_try_from_fails(item_method, "ink! messages must not be variadic")
888        }
889    }
890
891    #[test]
892    fn try_from_visibility_fails() {
893        let item_methods: Vec<syn::ImplItemFn> = vec![
894            // &self + crate visibility
895            syn::parse_quote! {
896                #[ink(message)]
897                pub(crate) fn my_message(&self) {}
898            },
899            // &mut self + crate visibility
900            syn::parse_quote! {
901                #[ink(message)]
902                pub(crate) fn my_message(&mut self) {}
903            },
904            // &self + pub restricted visibility
905            syn::parse_quote! {
906                #[ink(message)]
907                pub(in my::path) fn my_message(&self) {}
908            },
909            // &mut self + pub restricted visibility
910            syn::parse_quote! {
911                #[ink(message)]
912                pub(in my::path) fn my_message(&mut self) {}
913            },
914        ];
915        for item_method in item_methods {
916            assert_try_from_fails(
917                item_method,
918                "ink! messages must have public or inherited visibility",
919            )
920        }
921    }
922
923    #[test]
924    fn conflicting_attributes_fails() {
925        let item_methods: Vec<syn::ImplItemFn> = vec![
926            // storage
927            syn::parse_quote! {
928                #[ink(message, storage)]
929                fn my_message(&self) {}
930            },
931            // namespace
932            syn::parse_quote! {
933                #[ink(message, namespace = "my_namespace")]
934                fn my_message(&self) {}
935            },
936            // event + multiple attributes
937            syn::parse_quote! {
938                #[ink(message)]
939                #[ink(event)]
940                fn my_message(&self) {}
941            },
942        ];
943        for item_method in item_methods {
944            assert_try_from_fails(
945                item_method,
946                "encountered conflicting ink! attribute argument",
947            )
948        }
949    }
950}