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    ensure_callable_invariants,
17    Callable,
18    CallableKind,
19    InputsIter,
20    Visibility,
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                    if type_path.path.is_ident("Self") {
174                        return Err(format_err!(
175                            ret_type,
176                            "ink! messages must not return `Self`"
177                        ))
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        Ok(Self {
231            is_payable,
232            is_default,
233            selector,
234            name,
235            item: syn::ImplItemFn {
236                attrs: other_attrs,
237                ..method_item
238            },
239        })
240    }
241}
242
243impl Callable for Message {
244    fn kind(&self) -> CallableKind {
245        CallableKind::Message
246    }
247
248    fn ident(&self) -> &Ident {
249        &self.item.sig.ident
250    }
251
252    fn user_provided_selector(&self) -> Option<&ir::Selector> {
253        if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
254            return Some(selector)
255        }
256        None
257    }
258
259    fn has_wildcard_selector(&self) -> bool {
260        matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
261    }
262
263    fn has_wildcard_complement_selector(&self) -> bool {
264        self.selector == Some(SelectorOrWildcard::wildcard_complement())
265    }
266
267    fn is_payable(&self) -> bool {
268        self.is_payable
269    }
270
271    fn is_default(&self) -> bool {
272        self.is_default
273    }
274
275    fn visibility(&self) -> Visibility {
276        match &self.item.vis {
277            syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
278            syn::Visibility::Inherited => Visibility::Inherited,
279            _ => unreachable!("encountered invalid visibility for ink! message"),
280        }
281    }
282
283    fn inputs(&self) -> InputsIter<'_> {
284        InputsIter::from(self)
285    }
286
287    fn inputs_span(&self) -> Span {
288        self.item.sig.inputs.span()
289    }
290
291    fn statements(&self) -> &[syn::Stmt] {
292        &self.item.block.stmts
293    }
294
295    fn name(&self) -> Option<&str> {
296        self.name.as_deref()
297    }
298}
299
300impl Message {
301    /// Returns a slice of all non-ink! attributes of the ink! message.
302    pub fn attrs(&self) -> &[syn::Attribute] {
303        &self.item.attrs
304    }
305
306    /// Returns a list of `cfg` attributes if any.
307    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
308        extract_cfg_attributes(self.attrs(), span)
309    }
310
311    /// Returns a list of `cfg` attributes as `syn::Attribute` if any.
312    pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
313        extract_cfg_syn_attributes(self.attrs())
314    }
315
316    /// Returns the `self` receiver of the ink! message.
317    pub fn receiver(&self) -> Receiver {
318        match self.item.sig.inputs.iter().next() {
319            Some(syn::FnArg::Receiver(receiver)) => {
320                debug_assert!(receiver.reference.is_some());
321                if receiver.mutability.is_some() {
322                    Receiver::RefMut
323                } else {
324                    Receiver::Ref
325                }
326            }
327            _ => unreachable!("encountered invalid receiver argument for ink! message"),
328        }
329    }
330
331    /// Returns the return type of the ink! message if any.
332    pub fn output(&self) -> Option<&syn::Type> {
333        match &self.item.sig.output {
334            syn::ReturnType::Default => None,
335            syn::ReturnType::Type(_, return_type) => Some(return_type),
336        }
337    }
338
339    /// Returns the return type of the message, but wrapped within a `Result`.
340    ///
341    /// This is used to to allow callers to handle certain types of errors which are not
342    /// exposed by messages.
343    pub fn wrapped_output(&self) -> syn::Type {
344        let return_type = self
345            .output()
346            .map(quote::ToTokens::to_token_stream)
347            .unwrap_or_else(|| quote::quote! { () });
348
349        syn::parse_quote! {
350            ::ink::MessageResult<#return_type>
351        }
352    }
353
354    /// Returns a local ID unique to the ink! message with respect to its implementation
355    /// block.
356    ///
357    /// # Note
358    ///
359    /// It is a compile error if two ink! trait messages share the same local ID.
360    /// Although the above scenario is very unlikely since the local ID is computed
361    /// solely by the identifier of the ink! message.
362    pub fn local_id(&self) -> u32 {
363        utils::local_message_id(self.ident())
364    }
365
366    /// Returns the identifier of the message with an additional `try_` prefix attached.
367    pub fn try_ident(&self) -> Ident {
368        quote::format_ident!("try_{}", self.ident())
369    }
370
371    /// Returns the function name override (if any).
372    pub fn name(&self) -> Option<&str> {
373        self.name.as_deref()
374    }
375}
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380
381    #[test]
382    fn output_works() {
383        let test_inputs: Vec<(Option<syn::Type>, syn::ImplItemFn)> = vec![
384            (
385                // No output:
386                None,
387                syn::parse_quote! {
388                    #[ink(message)]
389                    fn my_message(&self) {}
390                },
391            ),
392            (
393                // Single output:
394                Some(syn::parse_quote! { i32 }),
395                syn::parse_quote! {
396                    #[ink(message)]
397                    fn my_message(&self) -> i32 {}
398                },
399            ),
400            (
401                // Tuple output:
402                Some(syn::parse_quote! { (i32, u64, bool) }),
403                syn::parse_quote! {
404                    #[ink(message)]
405                    fn my_message(&self) -> (i32, u64, bool) {}
406                },
407            ),
408        ];
409        for (expected_output, item_method) in test_inputs {
410            let actual_output = <ir::Message as TryFrom<_>>::try_from(item_method)
411                .unwrap()
412                .output()
413                .cloned();
414            assert_eq!(actual_output, expected_output);
415        }
416    }
417
418    #[test]
419    fn inputs_works() {
420        macro_rules! expected_input {
421            ( mut $name:ident: $ty:ty  ) => {{
422                syn::parse_quote! {
423                    mut $name: $ty
424                }
425            }};
426            ( $name:ident: $ty:ty  ) => {{
427                syn::parse_quote! {
428                    $name: $ty
429                }
430            }};
431        }
432        macro_rules! expected_inputs {
433            ( $( $($ts:ident)+: $ty:ty ),* ) => {{
434                vec![
435                    $(
436                        expected_input!($($ts)+: $ty)
437                    ),*
438                ]
439            }};
440        }
441        let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
442            (
443                // No inputs:
444                expected_inputs!(),
445                syn::parse_quote! {
446                    #[ink(message)]
447                    fn my_message(&self) {}
448                },
449            ),
450            (
451                // Single input:
452                expected_inputs!(a: i32),
453                syn::parse_quote! {
454                    #[ink(message)]
455                    fn my_message(&self, a: i32) {}
456                },
457            ),
458            (
459                // Single mutable input:
460                expected_inputs!(mut a: i32),
461                syn::parse_quote! {
462                    #[ink(message)]
463                    fn my_message(&self, mut a: i32) {}
464                },
465            ),
466            (
467                // Some inputs:
468                expected_inputs!(a: i32, b: u64, mut c: [u8; 32]),
469                syn::parse_quote! {
470                    #[ink(message)]
471                    fn my_message(&self, a: i32, b: u64, mut c: [u8; 32]) {}
472                },
473            ),
474        ];
475        for (expected_inputs, item_method) in test_inputs {
476            let actual_inputs = <ir::Message as TryFrom<_>>::try_from(item_method)
477                .unwrap()
478                .inputs()
479                .cloned()
480                .map(syn::FnArg::Typed)
481                .collect::<Vec<_>>();
482            assert_eq!(actual_inputs, expected_inputs);
483        }
484    }
485
486    #[test]
487    fn is_payable_works() {
488        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
489            // Not payable.
490            (
491                false,
492                syn::parse_quote! {
493                    #[ink(message)]
494                    fn my_message(&self) {}
495                },
496            ),
497            // Normalized ink! attribute.
498            (
499                true,
500                syn::parse_quote! {
501                    #[ink(message, payable)]
502                    pub fn my_message(&mut self) {}
503                },
504            ),
505            // Different ink! attributes.
506            (
507                true,
508                syn::parse_quote! {
509                    #[ink(message)]
510                    #[ink(payable)]
511                    pub fn my_message(&mut self) {}
512                },
513            ),
514            // Another ink! attribute, separate and normalized attribute.
515            (
516                true,
517                syn::parse_quote! {
518                    #[ink(message)]
519                    #[ink(selector = 0xDEADBEEF, payable)]
520                    pub fn my_message(&mut self) {}
521                },
522            ),
523        ];
524        for (expect_payable, item_method) in test_inputs {
525            let is_payable = <ir::Message as TryFrom<_>>::try_from(item_method)
526                .unwrap()
527                .is_payable();
528            assert_eq!(is_payable, expect_payable);
529        }
530    }
531
532    #[test]
533    fn is_default_works() {
534        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
535            // Not default.
536            (
537                false,
538                syn::parse_quote! {
539                    #[ink(message)]
540                    fn my_message(&self) {}
541                },
542            ),
543            // Default message.
544            (
545                true,
546                syn::parse_quote! {
547                    #[ink(message, payable, default)]
548                    pub fn my_message(&mut self) {}
549                },
550            ),
551        ];
552        for (expect_default, item_method) in test_inputs {
553            let is_default = <ir::Message as TryFrom<_>>::try_from(item_method)
554                .unwrap()
555                .is_default();
556            assert_eq!(is_default, expect_default);
557        }
558    }
559
560    #[test]
561    fn name_override_works() {
562        let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
563            // No name override.
564            (
565                None,
566                syn::parse_quote! {
567                    #[ink(message)]
568                    fn my_message(&self) {}
569                },
570            ),
571            // Name override.
572            (
573                Some("myMessage"),
574                syn::parse_quote! {
575                    #[ink(message, name = "myMessage")]
576                    pub fn my_message(&mut self) {}
577                },
578            ),
579        ];
580        for (expected_name, item_method) in test_inputs {
581            let message = <ir::Message as TryFrom<_>>::try_from(item_method).unwrap();
582            assert_eq!(message.name(), expected_name);
583        }
584    }
585
586    #[test]
587    fn receiver_works() {
588        let test_inputs: Vec<(Receiver, syn::ImplItemFn)> = vec![
589            (
590                Receiver::Ref,
591                syn::parse_quote! {
592                    #[ink(message)]
593                    fn my_message(&self) {}
594                },
595            ),
596            (
597                Receiver::RefMut,
598                syn::parse_quote! {
599                    #[ink(message, payable)]
600                    fn my_message(&mut self) {}
601                },
602            ),
603        ];
604        for (expected_receiver, item_method) in test_inputs {
605            let actual_receiver = <ir::Message as TryFrom<_>>::try_from(item_method)
606                .unwrap()
607                .receiver();
608            assert_eq!(actual_receiver, expected_receiver);
609        }
610    }
611
612    #[test]
613    fn visibility_works() {
614        let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
615            // &self
616            (
617                false,
618                syn::parse_quote! {
619                    #[ink(message)]
620                    fn my_message(&self) {}
621                },
622            ),
623            // &self + pub
624            (
625                true,
626                syn::parse_quote! {
627                    #[ink(message)]
628                    pub fn my_message(&self) {}
629                },
630            ),
631            // &mut self
632            (
633                false,
634                syn::parse_quote! {
635                    #[ink(message)]
636                    fn my_message(&mut self) {}
637                },
638            ),
639            // &mut self + pub
640            (
641                true,
642                syn::parse_quote! {
643                    #[ink(message)]
644                    pub fn my_message(&mut self) {}
645                },
646            ),
647        ];
648        for (is_pub, item_method) in test_inputs {
649            let visibility = <ir::Message as TryFrom<_>>::try_from(item_method)
650                .unwrap()
651                .visibility();
652            assert_eq!(visibility.is_pub(), is_pub);
653            assert_eq!(visibility.is_inherited(), !is_pub);
654        }
655    }
656
657    #[test]
658    fn try_from_works() {
659        let item_methods: Vec<syn::ImplItemFn> = vec![
660            // &self
661            syn::parse_quote! {
662                #[ink(message)]
663                fn my_message(&self) {}
664            },
665            // &self + pub
666            syn::parse_quote! {
667                #[ink(message)]
668                pub fn my_message(&self) {}
669            },
670            // &mut self
671            syn::parse_quote! {
672                #[ink(message)]
673                fn my_message(&mut self) {}
674            },
675            // &mut self + pub
676            syn::parse_quote! {
677                #[ink(message)]
678                pub fn my_message(&mut self) {}
679            },
680            // &self + payable
681            syn::parse_quote! {
682                #[ink(message, payable)]
683                fn my_message(&mut self) {}
684            },
685            // &mut self + payable
686            syn::parse_quote! {
687                #[ink(message, payable)]
688                fn my_message(&mut self) {}
689            },
690            // &self + many inputs + output works
691            syn::parse_quote! {
692                #[ink(message)]
693                fn my_message(&self, input1: i32, input2: i64, input3: u32, input4: u64) -> bool {}
694            },
695            // &mut self + many inputs + output works
696            syn::parse_quote! {
697                #[ink(message)]
698                fn my_message(&mut self, input1: i32, input2: i64, input3: u32, input4: u64) -> bool {}
699            },
700        ];
701        for item_method in item_methods {
702            assert!(<ir::Message as TryFrom<_>>::try_from(item_method).is_ok());
703        }
704    }
705
706    fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
707        assert_eq!(
708            <ir::Message as TryFrom<_>>::try_from(item_method)
709                .map_err(|err| err.to_string()),
710            Err(expected_err.to_string()),
711        );
712    }
713
714    #[test]
715    fn try_from_generics_fails() {
716        let item_methods: Vec<syn::ImplItemFn> = vec![
717            syn::parse_quote! {
718                #[ink(message)]
719                fn my_message<T>(&self) {}
720            },
721            syn::parse_quote! {
722                #[ink(message)]
723                pub fn my_message<T>(&self) {}
724            },
725            syn::parse_quote! {
726                #[ink(message)]
727                fn my_message<T>(&mut self) {}
728            },
729            syn::parse_quote! {
730                #[ink(message)]
731                pub fn my_message<T>(&mut self) {}
732            },
733        ];
734        for item_method in item_methods {
735            assert_try_from_fails(item_method, "ink! messages must not be generic")
736        }
737    }
738
739    #[test]
740    fn try_from_receiver_fails() {
741        let item_methods: Vec<syn::ImplItemFn> = vec![
742            syn::parse_quote! {
743                #[ink(message)]
744                fn my_message() {}
745            },
746            syn::parse_quote! {
747                #[ink(message)]
748                fn my_message(self) {}
749            },
750            syn::parse_quote! {
751                #[ink(message)]
752                pub fn my_message(mut self) {}
753            },
754            syn::parse_quote! {
755                #[ink(message)]
756                fn my_message(this: &Self) {}
757            },
758            syn::parse_quote! {
759                #[ink(message)]
760                pub fn my_message(this: &mut Self) {}
761            },
762        ];
763        for item_method in item_methods {
764            assert_try_from_fails(
765                item_method,
766                "ink! messages must have `&self` or `&mut self` receiver",
767            )
768        }
769    }
770
771    #[test]
772    fn try_from_const_fails() {
773        let item_methods: Vec<syn::ImplItemFn> = vec![
774            // &self
775            syn::parse_quote! {
776                #[ink(message)]
777                const fn my_message(&self) {}
778            },
779            // &mut self
780            syn::parse_quote! {
781                #[ink(message)]
782                const fn my_message(&mut self) {}
783            },
784        ];
785        for item_method in item_methods {
786            assert_try_from_fails(item_method, "ink! messages must not be const")
787        }
788    }
789
790    #[test]
791    fn try_from_async_fails() {
792        let item_methods: Vec<syn::ImplItemFn> = vec![
793            // &self
794            syn::parse_quote! {
795                #[ink(message)]
796                async fn my_message(&self) {}
797            },
798            // &mut self
799            syn::parse_quote! {
800                #[ink(message)]
801                async fn my_message(&mut self) {}
802            },
803        ];
804        for item_method in item_methods {
805            assert_try_from_fails(item_method, "ink! messages must not be async")
806        }
807    }
808
809    #[test]
810    fn try_from_unsafe_fails() {
811        let item_methods: Vec<syn::ImplItemFn> = vec![
812            // &self
813            syn::parse_quote! {
814                #[ink(message)]
815                unsafe fn my_message(&self) {}
816            },
817            // &mut self
818            syn::parse_quote! {
819                #[ink(message)]
820                unsafe fn my_message(&mut self) {}
821            },
822        ];
823        for item_method in item_methods {
824            assert_try_from_fails(item_method, "ink! messages must not be unsafe")
825        }
826    }
827
828    #[test]
829    fn try_from_explicit_abi_fails() {
830        let item_methods: Vec<syn::ImplItemFn> = vec![
831            // &self
832            syn::parse_quote! {
833                #[ink(message)]
834                extern "C" fn my_message(&self) {}
835            },
836            // &mut self
837            syn::parse_quote! {
838                #[ink(message)]
839                extern "C" fn my_message(&mut self) {}
840            },
841        ];
842        for item_method in item_methods {
843            assert_try_from_fails(item_method, "ink! messages must not have explicit ABI")
844        }
845    }
846
847    #[test]
848    fn try_from_variadic_fails() {
849        let item_methods: Vec<syn::ImplItemFn> = vec![
850            // &self
851            syn::parse_quote! {
852                #[ink(message)]
853                fn my_message(&self, ...) {}
854            },
855            // &mut self
856            syn::parse_quote! {
857                #[ink(message)]
858                fn my_message(&mut self, ...) {}
859            },
860        ];
861        for item_method in item_methods {
862            assert_try_from_fails(item_method, "ink! messages must not be variadic")
863        }
864    }
865
866    #[test]
867    fn try_from_visibility_fails() {
868        let item_methods: Vec<syn::ImplItemFn> = vec![
869            // &self + crate visibility
870            syn::parse_quote! {
871                #[ink(message)]
872                pub(crate) fn my_message(&self) {}
873            },
874            // &mut self + crate visibility
875            syn::parse_quote! {
876                #[ink(message)]
877                pub(crate) fn my_message(&mut self) {}
878            },
879            // &self + pub restricted visibility
880            syn::parse_quote! {
881                #[ink(message)]
882                pub(in my::path) fn my_message(&self) {}
883            },
884            // &mut self + pub restricted visibility
885            syn::parse_quote! {
886                #[ink(message)]
887                pub(in my::path) fn my_message(&mut self) {}
888            },
889        ];
890        for item_method in item_methods {
891            assert_try_from_fails(
892                item_method,
893                "ink! messages must have public or inherited visibility",
894            )
895        }
896    }
897
898    #[test]
899    fn conflicting_attributes_fails() {
900        let item_methods: Vec<syn::ImplItemFn> = vec![
901            // storage
902            syn::parse_quote! {
903                #[ink(message, storage)]
904                fn my_message(&self) {}
905            },
906            // namespace
907            syn::parse_quote! {
908                #[ink(message, namespace = "my_namespace")]
909                fn my_message(&self) {}
910            },
911            // event + multiple attributes
912            syn::parse_quote! {
913                #[ink(message)]
914                #[ink(event)]
915                fn my_message(&self) {}
916            },
917        ];
918        for item_method in item_methods {
919            assert_try_from_fails(
920                item_method,
921                "encountered conflicting ink! attribute argument",
922            )
923        }
924    }
925}