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