1use crate::{
16 error::ExtError,
17 ir,
18 ir::idents_lint,
19};
20use core::slice::Iter as SliceIter;
21use proc_macro2::TokenStream as TokenStream2;
22use std::collections::HashMap;
23use syn::{
24 spanned::Spanned as _,
25 Result,
26};
27
28#[derive(Debug, PartialEq, Eq)]
30pub struct ChainExtension {
31 item: syn::ItemTrait,
32 error_code: syn::TraitItemType,
33 methods: Vec<ChainExtensionMethod>,
34}
35
36impl ChainExtension {
37 pub fn attrs(&self) -> Vec<syn::Attribute> {
39 let (_, attrs) = ir::partition_attributes(self.item.attrs.iter().cloned())
40 .expect("encountered unexpected invalid attributes for ink! chain extension");
41 attrs
42 }
43
44 pub fn span(&self) -> proc_macro2::Span {
46 self.item.span()
47 }
48
49 pub fn ident(&self) -> &proc_macro2::Ident {
51 &self.item.ident
52 }
53
54 pub fn iter_methods(&self) -> SliceIter<ChainExtensionMethod> {
56 self.methods.iter()
57 }
58
59 pub fn error_code(&self) -> &syn::Type {
61 self.error_code
62 .default
63 .as_ref()
64 .map(|(_token, ty)| ty)
65 .expect("unexpected missing default type for error code")
66 }
67}
68
69#[derive(Debug, PartialEq, Eq)]
71pub struct ChainExtensionMethod {
72 item: syn::TraitItemMethod,
74 id: ExtensionId,
76 handle_status: bool,
91 returns_result: bool,
98}
99
100impl ChainExtensionMethod {
101 pub fn attrs(&self) -> Vec<syn::Attribute> {
103 let (_, attrs) = ir::partition_attributes(self.item.attrs.iter().cloned())
104 .expect(
105 "encountered unexpected invalid attributes for ink! chain extension method",
106 );
107 attrs
108 }
109
110 pub fn span(&self) -> proc_macro2::Span {
112 self.item.span()
113 }
114
115 pub fn ident(&self) -> &proc_macro2::Ident {
117 &self.item.sig.ident
118 }
119
120 pub fn sig(&self) -> &syn::Signature {
122 &self.item.sig
123 }
124
125 pub fn id(&self) -> ExtensionId {
127 self.id
128 }
129
130 pub fn inputs(&self) -> ChainExtensionMethodInputs {
132 ChainExtensionMethodInputs {
133 iter: self.item.sig.inputs.iter(),
134 }
135 }
136
137 pub fn handle_status(&self) -> bool {
139 self.handle_status
140 }
141
142 pub fn returns_result(&self) -> bool {
144 self.returns_result
145 }
146}
147
148pub struct ChainExtensionMethodInputs<'a> {
149 iter: syn::punctuated::Iter<'a, syn::FnArg>,
150}
151
152impl<'a> Iterator for ChainExtensionMethodInputs<'a> {
153 type Item = &'a syn::PatType;
154
155 fn size_hint(&self) -> (usize, Option<usize>) {
156 self.iter.size_hint()
157 }
158
159 fn next(&mut self) -> Option<Self::Item> {
160 let item = self.iter.next()?;
161 match item {
162 syn::FnArg::Receiver(receiver) => {
163 panic!("encountered unexpected receiver in chain extension method input: {:?}", receiver)
164 }
165 syn::FnArg::Typed(pat_type) => Some(pat_type),
166 }
167 }
168}
169
170#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
178pub struct ExtensionId {
179 index: u32,
180}
181
182impl ExtensionId {
183 pub fn from_u32(index: u32) -> Self {
185 Self { index }
186 }
187
188 pub fn into_u32(self) -> u32 {
190 self.index
191 }
192}
193
194impl TryFrom<syn::ItemTrait> for ChainExtension {
195 type Error = syn::Error;
196
197 fn try_from(item_trait: syn::ItemTrait) -> core::result::Result<Self, Self::Error> {
198 idents_lint::ensure_no_ink_identifiers(&item_trait)?;
199 Self::analyse_properties(&item_trait)?;
200 let (error_code, methods) = Self::analyse_items(&item_trait)?;
201 Ok(Self {
202 item: item_trait,
203 error_code,
204 methods,
205 })
206 }
207}
208
209impl ChainExtension {
210 pub fn new(attr: TokenStream2, input: TokenStream2) -> Result<Self> {
212 if !attr.is_empty() {
213 return Err(format_err_spanned!(
214 attr,
215 "unexpected attribute input for ink! chain extension"
216 ))
217 }
218 let item_trait = syn::parse2::<syn::ItemTrait>(input)?;
219 ChainExtension::try_from(item_trait)
220 }
221
222 fn analyse_properties(item_trait: &syn::ItemTrait) -> Result<()> {
232 if let Some(unsafety) = &item_trait.unsafety {
233 return Err(format_err_spanned!(
234 unsafety,
235 "ink! chain extensions cannot be unsafe"
236 ))
237 }
238 if let Some(auto) = &item_trait.auto_token {
239 return Err(format_err_spanned!(
240 auto,
241 "ink! chain extensions cannot be automatically implemented traits"
242 ))
243 }
244 if !item_trait.generics.params.is_empty() {
245 return Err(format_err_spanned!(
246 item_trait.generics.params,
247 "ink! chain extensions must not be generic"
248 ))
249 }
250 if !matches!(item_trait.vis, syn::Visibility::Public(_)) {
251 return Err(format_err_spanned!(
252 item_trait.vis,
253 "ink! chain extensions must have public visibility"
254 ))
255 }
256 if !item_trait.supertraits.is_empty() {
257 return Err(format_err_spanned!(
258 item_trait.supertraits,
259 "ink! chain extensions with super-traits are not supported, yet"
260 ))
261 }
262 Ok(())
263 }
264
265 fn analyse_error_code(
273 item_type: &syn::TraitItemType,
274 previous: &mut Option<syn::TraitItemType>,
275 ) -> Result<()> {
276 if item_type.ident != "ErrorCode" {
277 return Err(format_err_spanned!(
278 item_type.ident,
279 "chain extensions expect an associated type with name `ErrorCode` but found {}",
280 item_type.ident,
281 ))
282 }
283 if !item_type.generics.params.is_empty() {
284 return Err(format_err_spanned!(
285 item_type.generics,
286 "generic chain extension `ErrorCode` types are not supported",
287 ))
288 }
289 if !item_type.bounds.is_empty() {
290 return Err(format_err_spanned!(
291 item_type.bounds,
292 "bounded chain extension `ErrorCode` types are not supported",
293 ))
294 }
295 if item_type.default.is_none() {
296 return Err(format_err_spanned!(
297 item_type,
298 "expected a default type for the ink! chain extension ErrorCode",
299 ))
300 }
301 match previous {
302 Some(previous_error_code) => {
303 return Err(format_err_spanned!(
304 item_type,
305 "encountered duplicate `ErrorCode` associated types for the chain extension",
306 )).map_err(|err| err.into_combine(format_err_spanned!(
307 previous_error_code,
308 "first `ErrorCode` associated type here",
309 )))
310 }
311 None => {
312 *previous = Some(item_type.clone());
313 }
314 }
315 Ok(())
316 }
317
318 fn analyse_items(
338 item_trait: &syn::ItemTrait,
339 ) -> Result<(syn::TraitItemType, Vec<ChainExtensionMethod>)> {
340 let mut methods = Vec::new();
341 let mut seen_ids = HashMap::new();
342 let mut error_code = None;
343 for trait_item in &item_trait.items {
344 match trait_item {
345 syn::TraitItem::Const(const_trait_item) => {
346 return Err(format_err_spanned!(
347 const_trait_item,
348 "associated constants in ink! chain extensions are not supported, yet"
349 ))
350 }
351 syn::TraitItem::Macro(macro_trait_item) => {
352 return Err(format_err_spanned!(
353 macro_trait_item,
354 "macros in ink! chain extensions are not supported"
355 ))
356 }
357 syn::TraitItem::Type(type_trait_item) => {
358 Self::analyse_error_code(type_trait_item, &mut error_code)?;
359 }
360 syn::TraitItem::Verbatim(verbatim) => {
361 return Err(format_err_spanned!(
362 verbatim,
363 "encountered unsupported item in ink! chain extensions"
364 ))
365 }
366 syn::TraitItem::Method(method_trait_item) => {
367 let method = Self::analyse_methods(method_trait_item)?;
368 let method_id = method.id();
369 if let Some(previous) = seen_ids.get(&method_id) {
370 return Err(format_err!(
371 method.span(),
372 "encountered duplicate extension identifiers for the same chain extension",
373 ).into_combine(format_err!(
374 *previous,
375 "previous duplicate extension identifier here",
376 )))
377 }
378 seen_ids.insert(method_id, method.span());
379 methods.push(method);
380 }
381 unknown => {
382 return Err(format_err_spanned!(
383 unknown,
384 "encountered unknown or unsupported item in ink! chain extensions"
385 ))
386 }
387 }
388 }
389 let error_code = match error_code {
390 Some(error_code) => error_code,
391 None => {
392 return Err(format_err_spanned!(
393 item_trait,
394 "missing ErrorCode associated type from ink! chain extension",
395 ))
396 }
397 };
398 Ok((error_code, methods))
399 }
400
401 fn analyse_methods(method: &syn::TraitItemMethod) -> Result<ChainExtensionMethod> {
411 if let Some(default_impl) = &method.default {
412 return Err(format_err_spanned!(
413 default_impl,
414 "ink! chain extension methods with default implementations are not supported"
415 ))
416 }
417 if let Some(constness) = &method.sig.constness {
418 return Err(format_err_spanned!(
419 constness,
420 "const ink! chain extension methods are not supported"
421 ))
422 }
423 if let Some(asyncness) = &method.sig.asyncness {
424 return Err(format_err_spanned!(
425 asyncness,
426 "async ink! chain extension methods are not supported"
427 ))
428 }
429 if let Some(unsafety) = &method.sig.unsafety {
430 return Err(format_err_spanned!(
431 unsafety,
432 "unsafe ink! chain extension methods are not supported"
433 ))
434 }
435 if let Some(abi) = &method.sig.abi {
436 return Err(format_err_spanned!(
437 abi,
438 "ink! chain extension methods with non default ABI are not supported"
439 ))
440 }
441 if let Some(variadic) = &method.sig.variadic {
442 return Err(format_err_spanned!(
443 variadic,
444 "variadic ink! chain extension methods are not supported"
445 ))
446 }
447 if !method.sig.generics.params.is_empty() {
448 return Err(format_err_spanned!(
449 method.sig.generics.params,
450 "generic ink! chain extension methods are not supported"
451 ))
452 }
453 match ir::first_ink_attribute(&method.attrs)?
454 .map(|attr| attr.first().kind().clone()) {
455 Some(ir::AttributeArg::Extension(extension)) => {
456 Self::analyse_chain_extension_method(method, extension)
457 }
458 Some(_unsupported) => {
459 Err(format_err_spanned!(
460 method,
461 "encountered unsupported ink! attribute for ink! chain extension method. expected #[ink(extension = N: u32)] attribute"
462 ))
463 }
464 None => {
465 Err(format_err_spanned!(
466 method,
467 "missing #[ink(extension = N: u32)] flag on ink! chain extension method"
468 ))
469 }
470 }
471 }
472
473 fn analyse_chain_extension_method(
479 item_method: &syn::TraitItemMethod,
480 extension: ExtensionId,
481 ) -> Result<ChainExtensionMethod> {
482 let (ink_attrs, _) = ir::sanitize_attributes(
483 item_method.span(),
484 item_method.attrs.clone(),
485 &ir::AttributeArgKind::Extension,
486 |arg| {
487 match arg.kind() {
488 ir::AttributeArg::Extension(_)
489 | ir::AttributeArg::HandleStatus(_)
490 | ir::AttributeArg::ReturnsResult(_) => Ok(()),
491 _ => Err(None),
492 }
493 },
494 )?;
495 if let Some(receiver) = item_method.sig.receiver() {
496 return Err(format_err_spanned!(
497 receiver,
498 "ink! chain extension method must not have a `self` receiver",
499 ))
500 }
501 let result = ChainExtensionMethod {
502 id: extension,
503 item: item_method.clone(),
504 handle_status: ink_attrs.is_handle_status(),
505 returns_result: ink_attrs.is_returns_result(),
506 };
507 Ok(result)
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 macro_rules! assert_ink_chain_extension_eq_err {
517 ( error: $err_str:literal, $($chain_extension:tt)* ) => {
518 assert_eq!(
519 <ChainExtension as TryFrom<syn::ItemTrait>>::try_from(syn::parse_quote! {
520 $( $chain_extension )*
521 })
522 .map_err(|err| err.to_string()),
523 Err(
524 $err_str.to_string()
525 )
526 )
527 };
528 }
529
530 #[test]
531 fn unsafe_chain_extension_is_denied() {
532 assert_ink_chain_extension_eq_err!(
533 error: "ink! chain extensions cannot be unsafe",
534 pub unsafe trait MyChainExtension {}
535 );
536 }
537
538 #[test]
539 fn auto_chain_extension_is_denied() {
540 assert_ink_chain_extension_eq_err!(
541 error: "ink! chain extensions cannot be automatically implemented traits",
542 pub auto trait MyChainExtension {}
543 );
544 }
545
546 #[test]
547 fn non_pub_chain_extension_is_denied() {
548 assert_ink_chain_extension_eq_err!(
549 error: "ink! chain extensions must have public visibility",
550 trait MyChainExtension {}
551 );
552 assert_ink_chain_extension_eq_err!(
553 error: "ink! chain extensions must have public visibility",
554 pub(crate) trait MyChainExtension {}
555 );
556 }
557
558 #[test]
559 fn generic_chain_extension_is_denied() {
560 assert_ink_chain_extension_eq_err!(
561 error: "ink! chain extensions must not be generic",
562 pub trait MyChainExtension<T> {}
563 );
564 }
565
566 #[test]
567 fn chain_extension_with_supertraits_is_denied() {
568 assert_ink_chain_extension_eq_err!(
569 error: "ink! chain extensions with super-traits are not supported, yet",
570 pub trait MyChainExtension: SuperChainExtension {}
571 );
572 }
573
574 #[test]
575 fn chain_extension_containing_const_item_is_denied() {
576 assert_ink_chain_extension_eq_err!(
577 error: "associated constants in ink! chain extensions are not supported, yet",
578 pub trait MyChainExtension {
579 const T: i32;
580 }
581 );
582 }
583
584 #[test]
585 fn chain_extension_containing_invalid_associated_type_is_denied() {
586 assert_ink_chain_extension_eq_err!(
587 error: "chain extensions expect an associated type with name `ErrorCode` but found Type",
588 pub trait MyChainExtension {
589 type Type;
590 }
591 );
592 }
593
594 #[test]
595 fn chain_extension_with_invalid_error_code() {
596 assert_ink_chain_extension_eq_err!(
597 error: "chain extensions expect an associated type with name `ErrorCode` but found IncorrectName",
598 pub trait MyChainExtension {
599 type IncorrectName = ();
600 }
601 );
602 assert_ink_chain_extension_eq_err!(
603 error: "generic chain extension `ErrorCode` types are not supported",
604 pub trait MyChainExtension {
605 type ErrorCode<T> = ();
606 }
607 );
608 assert_ink_chain_extension_eq_err!(
609 error: "bounded chain extension `ErrorCode` types are not supported",
610 pub trait MyChainExtension {
611 type ErrorCode: Copy = ();
612 }
613 );
614 assert_ink_chain_extension_eq_err!(
615 error: "expected a default type for the ink! chain extension ErrorCode",
616 pub trait MyChainExtension {
617 type ErrorCode;
618 }
619 );
620 assert_ink_chain_extension_eq_err!(
621 error: "encountered duplicate `ErrorCode` associated types for the chain extension",
622 pub trait MyChainExtension {
623 type ErrorCode = ();
624 type ErrorCode = ();
625 }
626 );
627 }
628
629 #[test]
630 fn chain_extension_containing_macro_is_denied() {
631 assert_ink_chain_extension_eq_err!(
632 error: "macros in ink! chain extensions are not supported",
633 pub trait MyChainExtension {
634 my_macro_call!();
635 }
636 );
637 }
638
639 #[test]
640 fn chain_extension_containing_non_flagged_method_is_denied() {
641 assert_ink_chain_extension_eq_err!(
642 error: "missing #[ink(extension = N: u32)] flag on ink! chain extension method",
643 pub trait MyChainExtension {
644 fn non_flagged_1(&self);
645 }
646 );
647 assert_ink_chain_extension_eq_err!(
648 error: "missing #[ink(extension = N: u32)] flag on ink! chain extension method",
649 pub trait MyChainExtension {
650 fn non_flagged_2(&mut self);
651 }
652 );
653 assert_ink_chain_extension_eq_err!(
654 error: "missing #[ink(extension = N: u32)] flag on ink! chain extension method",
655 pub trait MyChainExtension {
656 fn non_flagged_3() -> Self;
657 }
658 );
659 }
660
661 #[test]
662 fn chain_extension_containing_default_implemented_methods_is_denied() {
663 assert_ink_chain_extension_eq_err!(
664 error: "ink! chain extension methods with default implementations are not supported",
665 pub trait MyChainExtension {
666 #[ink(constructor)]
667 fn default_implemented() -> Self {}
668 }
669 );
670 }
671
672 #[test]
673 fn chain_extension_containing_const_methods_is_denied() {
674 assert_ink_chain_extension_eq_err!(
675 error: "const ink! chain extension methods are not supported",
676 pub trait MyChainExtension {
677 #[ink(extension = 1)]
678 const fn const_constructor() -> Self;
679 }
680 );
681 }
682
683 #[test]
684 fn chain_extension_containing_async_methods_is_denied() {
685 assert_ink_chain_extension_eq_err!(
686 error: "async ink! chain extension methods are not supported",
687 pub trait MyChainExtension {
688 #[ink(extension = 1)]
689 async fn const_constructor() -> Self;
690 }
691 );
692 }
693
694 #[test]
695 fn chain_extension_containing_unsafe_methods_is_denied() {
696 assert_ink_chain_extension_eq_err!(
697 error: "unsafe ink! chain extension methods are not supported",
698 pub trait MyChainExtension {
699 #[ink(extension = 1)]
700 unsafe fn const_constructor() -> Self;
701 }
702 );
703 }
704
705 #[test]
706 fn chain_extension_containing_methods_using_explicit_abi_is_denied() {
707 assert_ink_chain_extension_eq_err!(
708 error: "ink! chain extension methods with non default ABI are not supported",
709 pub trait MyChainExtension {
710 #[ink(extension = 1)]
711 extern fn const_constructor() -> Self;
712 }
713 );
714 }
715
716 #[test]
717 fn chain_extension_containing_variadic_methods_is_denied() {
718 assert_ink_chain_extension_eq_err!(
719 error: "variadic ink! chain extension methods are not supported",
720 pub trait MyChainExtension {
721 #[ink(extension = 1)]
722 fn const_constructor(...) -> Self;
723 }
724 );
725 }
726
727 #[test]
728 fn chain_extension_containing_generic_methods_is_denied() {
729 assert_ink_chain_extension_eq_err!(
730 error: "generic ink! chain extension methods are not supported",
731 pub trait MyChainExtension {
732 #[ink(extension = 1)]
733 fn const_constructor<T>() -> Self;
734 }
735 );
736 }
737
738 #[test]
739 fn chain_extension_containing_method_with_unsupported_ink_attribute_is_denied() {
740 assert_ink_chain_extension_eq_err!(
741 error: "\
742 encountered unsupported ink! attribute for ink! chain extension method. \
743 expected #[ink(extension = N: u32)] attribute",
744 pub trait MyChainExtension {
745 #[ink(message)]
746 fn unsupported_ink_attribute(&self);
747 }
748 );
749 assert_ink_chain_extension_eq_err!(
750 error: "unknown ink! attribute (path)",
751 pub trait MyChainExtension {
752 #[ink(unknown)]
753 fn unknown_ink_attribute(&self);
754 }
755 );
756 }
757
758 #[test]
759 fn chain_extension_containing_method_with_invalid_marker() {
760 assert_ink_chain_extension_eq_err!(
761 error: "could not parse `N` in `#[ink(extension = N)]` into a `u32` integer",
762 pub trait MyChainExtension {
763 #[ink(extension = -1)]
764 fn has_self_receiver();
765 }
766 );
767 let too_large = (u32::MAX as u64) + 1;
768 assert_ink_chain_extension_eq_err!(
769 error: "could not parse `N` in `#[ink(extension = N)]` into a `u32` integer",
770 pub trait MyChainExtension {
771 #[ink(extension = #too_large)]
772 fn has_self_receiver();
773 }
774 );
775 assert_ink_chain_extension_eq_err!(
776 error: "expected `u32` integer type for `N` in #[ink(extension = N)]",
777 pub trait MyChainExtension {
778 #[ink(extension = "Hello, World!")]
779 fn has_self_receiver();
780 }
781 );
782 assert_ink_chain_extension_eq_err!(
783 error: "encountered #[ink(extension)] that is missing its `id` parameter. \
784 Did you mean #[ink(extension = id: u32)] ?",
785 pub trait MyChainExtension {
786 #[ink(extension)]
787 fn has_self_receiver();
788 }
789 );
790
791 assert_ink_chain_extension_eq_err!(
792 error: "encountered duplicate ink! attribute",
793 pub trait MyChainExtension {
794 #[ink(extension = 42)]
795 #[ink(extension = 42)]
796 fn duplicate_attributes() -> Self;
797 }
798 );
799 assert_ink_chain_extension_eq_err!(
800 error: "encountered ink! attribute arguments with equal kinds",
801 pub trait MyChainExtension {
802 #[ink(extension = 1)]
803 #[ink(extension = 2)]
804 fn duplicate_attributes() -> Self;
805 }
806 );
807 assert_ink_chain_extension_eq_err!(
808 error: "encountered conflicting ink! attribute argument",
809 pub trait MyChainExtension {
810 #[ink(extension = 1)]
811 #[ink(message)]
812 fn conflicting_attributes() -> Self;
813 }
814 );
815 }
816
817 #[test]
818 fn chain_extension_containing_method_with_self_receiver_is_denied() {
819 assert_ink_chain_extension_eq_err!(
820 error: "ink! chain extension method must not have a `self` receiver",
821 pub trait MyChainExtension {
822 type ErrorCode = ();
823
824 #[ink(extension = 1)]
825 fn has_self_receiver(&self) -> Self;
826 }
827 );
828 assert_ink_chain_extension_eq_err!(
829 error: "ink! chain extension method must not have a `self` receiver",
830 pub trait MyChainExtension {
831 type ErrorCode = ();
832
833 #[ink(extension = 1)]
834 fn has_self_receiver(&mut self) -> Self;
835 }
836 );
837 assert_ink_chain_extension_eq_err!(
838 error: "ink! chain extension method must not have a `self` receiver",
839 pub trait MyChainExtension {
840 type ErrorCode = ();
841
842 #[ink(extension = 1)]
843 fn has_self_receiver(self) -> Self;
844 }
845 );
846 assert_ink_chain_extension_eq_err!(
847 error: "ink! chain extension method must not have a `self` receiver",
848 pub trait MyChainExtension {
849 type ErrorCode = ();
850
851 #[ink(extension = 1)]
852 fn has_self_receiver(self: &Self) -> Self;
853 }
854 );
855 assert_ink_chain_extension_eq_err!(
856 error: "ink! chain extension method must not have a `self` receiver",
857 pub trait MyChainExtension {
858 type ErrorCode = ();
859
860 #[ink(extension = 1)]
861 fn has_self_receiver(self: Self) -> Self;
862 }
863 );
864 }
865
866 #[test]
867 fn chain_extension_with_overlapping_extension_ids() {
868 assert_ink_chain_extension_eq_err!(
869 error: "encountered duplicate extension identifiers for the same chain extension",
870 pub trait MyChainExtension {
871 #[ink(extension = 1)]
872 fn same_id_1();
873 #[ink(extension = 1)]
874 fn same_id_2();
875 }
876 );
877 }
878
879 #[test]
880 fn chain_extension_is_ok() {
881 let chain_extension = <ChainExtension as TryFrom<syn::ItemTrait>>::try_from(syn::parse_quote! {
882 pub trait MyChainExtension {
883 type ErrorCode = ();
884
885 #[ink(extension = 1)]
886 fn extension_1();
887 #[ink(extension = 2)]
888 fn extension_2(input: i32);
889 #[ink(extension = 3)]
890 fn extension_3() -> i32;
891 #[ink(extension = 4)]
892 fn extension_4(input: i32) -> i32;
893 #[ink(extension = 5)]
894 fn extension_5(in1: i8, in2: i16, in3: i32, in4: i64) -> (u8, u16, u32, u64);
895 }
896 }).unwrap();
897 assert_eq!(chain_extension.methods.len(), 5);
898 for (actual, expected) in chain_extension
899 .methods
900 .iter()
901 .map(|method| method.id())
902 .zip(1..=5u32)
903 {
904 assert_eq!(actual.index, expected);
905 }
906 for (actual, expected) in chain_extension
907 .methods
908 .iter()
909 .map(|method| method.ident().to_string())
910 .zip(
911 [
912 "extension_1",
913 "extension_2",
914 "extension_3",
915 "extension_4",
916 "extension_5",
917 ]
918 .iter()
919 .map(ToString::to_string),
920 )
921 {
922 assert_eq!(actual, expected);
923 }
924 }
925
926 #[test]
927 fn chain_extension_with_params_is_ok() {
928 let chain_extension =
929 <ChainExtension as TryFrom<syn::ItemTrait>>::try_from(syn::parse_quote! {
930 pub trait MyChainExtension {
931 type ErrorCode = ();
932
933 #[ink(extension = 1, handle_status = false)]
934 fn extension_a();
935 #[ink(extension = 2, returns_result = false)]
936 fn extension_b();
937 #[ink(extension = 3, handle_status = false, returns_result = false)]
938 fn extension_c();
939
940 #[ink(extension = 4)]
941 #[ink(handle_status = false)]
942 fn extension_d();
943 #[ink(extension = 5)]
944 #[ink(returns_result = false)]
945 fn extension_e();
946 #[ink(extension = 6)]
947 #[ink(handle_status = false)]
948 #[ink(returns_result = false)]
949 fn extension_f();
950 }
951 })
952 .unwrap();
953 let expected_methods = 6;
954 assert_eq!(chain_extension.methods.len(), expected_methods);
955 for (actual, expected) in chain_extension
956 .methods
957 .iter()
958 .map(|method| method.id())
959 .zip(1..=expected_methods as u32)
960 {
961 assert_eq!(actual.index, expected);
962 }
963 for (actual, expected) in chain_extension
964 .methods
965 .iter()
966 .map(|method| method.ident().to_string())
967 .zip(
968 [
969 "extension_a",
970 "extension_b",
971 "extension_c",
972 "extension_d",
973 "extension_e",
974 "extension_f",
975 ]
976 .iter()
977 .map(ToString::to_string),
978 )
979 {
980 assert_eq!(actual, expected);
981 }
982 }
983}