1use 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#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40pub enum Receiver {
41 Ref,
43 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 pub fn is_ref(self) -> bool {
60 matches!(self, Self::Ref)
61 }
62
63 pub fn is_ref_mut(self) -> bool {
65 matches!(self, Self::RefMut)
66 }
67}
68
69#[derive(Debug, PartialEq, Eq)]
101pub struct Message {
102 pub(super) item: syn::ImplItemFn,
104 is_payable: bool,
106 is_default: bool,
108 selector: Option<SelectorOrWildcard>,
115 name: Option<String>,
122}
123
124impl quote::ToTokens for Message {
125 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
128 self.item.to_tokens(tokens)
129 }
130}
131
132impl Message {
133 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 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 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 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 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 pub fn attrs(&self) -> &[syn::Attribute] {
317 &self.item.attrs
318 }
319
320 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
322 extract_cfg_attributes(self.attrs(), span)
323 }
324
325 pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
327 extract_cfg_syn_attributes(self.attrs())
328 }
329
330 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 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 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 pub fn local_id(&self) -> u32 {
377 utils::local_message_id(&self.normalized_name())
378 }
379
380 pub fn try_ident(&self) -> Ident {
382 quote::format_ident!("try_{}", self.ident())
383 }
384
385 pub fn name(&self) -> Option<&str> {
387 self.name.as_deref()
388 }
389
390 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 None,
412 syn::parse_quote! {
413 #[ink(message)]
414 fn my_message(&self) {}
415 },
416 ),
417 (
418 Some(syn::parse_quote! { i32 }),
420 syn::parse_quote! {
421 #[ink(message)]
422 fn my_message(&self) -> i32 {}
423 },
424 ),
425 (
426 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 expected_inputs!(),
470 syn::parse_quote! {
471 #[ink(message)]
472 fn my_message(&self) {}
473 },
474 ),
475 (
476 expected_inputs!(a: i32),
478 syn::parse_quote! {
479 #[ink(message)]
480 fn my_message(&self, a: i32) {}
481 },
482 ),
483 (
484 expected_inputs!(mut a: i32),
486 syn::parse_quote! {
487 #[ink(message)]
488 fn my_message(&self, mut a: i32) {}
489 },
490 ),
491 (
492 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 (
516 false,
517 syn::parse_quote! {
518 #[ink(message)]
519 fn my_message(&self) {}
520 },
521 ),
522 (
524 true,
525 syn::parse_quote! {
526 #[ink(message, payable)]
527 pub fn my_message(&mut self) {}
528 },
529 ),
530 (
532 true,
533 syn::parse_quote! {
534 #[ink(message)]
535 #[ink(payable)]
536 pub fn my_message(&mut self) {}
537 },
538 ),
539 (
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 (
562 false,
563 syn::parse_quote! {
564 #[ink(message)]
565 fn my_message(&self) {}
566 },
567 ),
568 (
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 (
590 None,
591 syn::parse_quote! {
592 #[ink(message)]
593 fn my_message(&self) {}
594 },
595 ),
596 (
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 (
642 false,
643 syn::parse_quote! {
644 #[ink(message)]
645 fn my_message(&self) {}
646 },
647 ),
648 (
650 true,
651 syn::parse_quote! {
652 #[ink(message)]
653 pub fn my_message(&self) {}
654 },
655 ),
656 (
658 false,
659 syn::parse_quote! {
660 #[ink(message)]
661 fn my_message(&mut self) {}
662 },
663 ),
664 (
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 syn::parse_quote! {
687 #[ink(message)]
688 fn my_message(&self) {}
689 },
690 syn::parse_quote! {
692 #[ink(message)]
693 pub fn my_message(&self) {}
694 },
695 syn::parse_quote! {
697 #[ink(message)]
698 fn my_message(&mut self) {}
699 },
700 syn::parse_quote! {
702 #[ink(message)]
703 pub fn my_message(&mut self) {}
704 },
705 syn::parse_quote! {
707 #[ink(message, payable)]
708 fn my_message(&mut self) {}
709 },
710 syn::parse_quote! {
712 #[ink(message, payable)]
713 fn my_message(&mut self) {}
714 },
715 syn::parse_quote! {
717 #[ink(message)]
718 fn my_message(&self, input1: i32, input2: i64, input3: u32, input4: u64) -> bool {}
719 },
720 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 syn::parse_quote! {
801 #[ink(message)]
802 const fn my_message(&self) {}
803 },
804 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 syn::parse_quote! {
820 #[ink(message)]
821 async fn my_message(&self) {}
822 },
823 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 syn::parse_quote! {
839 #[ink(message)]
840 unsafe fn my_message(&self) {}
841 },
842 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 syn::parse_quote! {
858 #[ink(message)]
859 extern "C" fn my_message(&self) {}
860 },
861 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 syn::parse_quote! {
877 #[ink(message)]
878 fn my_message(&self, ...) {}
879 },
880 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 syn::parse_quote! {
896 #[ink(message)]
897 pub(crate) fn my_message(&self) {}
898 },
899 syn::parse_quote! {
901 #[ink(message)]
902 pub(crate) fn my_message(&mut self) {}
903 },
904 syn::parse_quote! {
906 #[ink(message)]
907 pub(in my::path) fn my_message(&self) {}
908 },
909 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 syn::parse_quote! {
928 #[ink(message, storage)]
929 fn my_message(&self) {}
930 },
931 syn::parse_quote! {
933 #[ink(message, namespace = "my_namespace")]
934 fn my_message(&self) {}
935 },
936 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}