1use super::{
16 Callable,
17 CallableKind,
18 InputsIter,
19 Visibility,
20 ensure_callable_invariants,
21};
22use crate::{
23 ir,
24 ir::{
25 attrs::SelectorOrWildcard,
26 utils::{
27 extract_cfg_attributes,
28 extract_cfg_syn_attributes,
29 },
30 },
31};
32use proc_macro2::{
33 Ident,
34 Span,
35 TokenStream,
36};
37use syn::spanned::Spanned as _;
38
39#[derive(Debug, PartialEq, Eq)]
71pub struct Constructor {
72 pub(super) item: syn::ImplItemFn,
74 is_payable: bool,
76 is_default: bool,
78 selector: Option<SelectorOrWildcard>,
85 name: Option<String>,
92}
93
94impl quote::ToTokens for Constructor {
95 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
98 self.item.to_tokens(tokens)
99 }
100}
101
102impl Constructor {
103 fn ensure_return(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
110 if let syn::ReturnType::Default = &method_item.sig.output {
111 return Err(format_err_spanned!(
112 &method_item.sig,
113 "missing return for ink! constructor",
114 ))
115 }
116 Ok(())
117 }
118
119 fn ensure_no_self_receiver(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
128 match method_item.sig.inputs.iter().next() {
129 None | Some(syn::FnArg::Typed(_)) => (),
130 Some(syn::FnArg::Receiver(receiver)) => {
131 return Err(format_err_spanned!(
132 receiver,
133 "ink! constructors must have no `self` receiver",
134 ))
135 }
136 }
137 Ok(())
138 }
139
140 fn sanitize_attributes(
144 method_item: &syn::ImplItemFn,
145 ) -> Result<(ir::InkAttribute, Vec<syn::Attribute>), syn::Error> {
146 ir::sanitize_attributes(
147 method_item.span(),
148 method_item.attrs.clone(),
149 &ir::AttributeArgKind::Constructor,
150 |arg| {
151 match arg.kind() {
152 ir::AttributeArg::Constructor
153 | ir::AttributeArg::Payable
154 | ir::AttributeArg::Default
155 | ir::AttributeArg::Selector(_)
156 | ir::AttributeArg::Name(_) => Ok(()),
157 _ => Err(None),
158 }
159 },
160 )
161 }
162}
163
164impl TryFrom<syn::ImplItemFn> for Constructor {
165 type Error = syn::Error;
166
167 fn try_from(method_item: syn::ImplItemFn) -> Result<Self, Self::Error> {
168 ensure_callable_invariants(&method_item, CallableKind::Constructor)?;
169 Self::ensure_return(&method_item)?;
170 Self::ensure_no_self_receiver(&method_item)?;
171 let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
172 let is_payable = ink_attrs.is_payable();
173 let is_default = ink_attrs.is_default();
174 let selector = ink_attrs.selector();
175 let name = ink_attrs.name();
176 #[cfg(ink_abi = "sol")]
177 if selector.is_some() {
178 let selector_span = ink_attrs.args().find_map(|arg| {
179 matches!(arg.kind(), ir::AttributeArg::Selector(_)).then_some(arg.span())
180 });
181 return Err(format_err!(
182 selector_span.unwrap_or_else(|| method_item.span()),
183 "constructor `selector` attributes are not supported in Solidity ABI compatibility mode",
184 ));
185 }
186 Ok(Constructor {
187 selector,
188 is_payable,
189 is_default,
190 name,
191 item: syn::ImplItemFn {
192 attrs: other_attrs,
193 ..method_item
194 },
195 })
196 }
197}
198
199impl Callable for Constructor {
200 fn kind(&self) -> CallableKind {
201 CallableKind::Constructor
202 }
203
204 fn ident(&self) -> &Ident {
205 &self.item.sig.ident
206 }
207
208 fn user_provided_selector(&self) -> Option<&ir::Selector> {
209 if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
210 return Some(selector)
211 }
212 None
213 }
214
215 fn has_wildcard_selector(&self) -> bool {
216 matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
217 }
218
219 fn has_wildcard_complement_selector(&self) -> bool {
220 self.selector == Some(SelectorOrWildcard::wildcard_complement())
221 }
222
223 fn is_payable(&self) -> bool {
224 self.is_payable
225 }
226
227 fn is_default(&self) -> bool {
228 self.is_default
229 }
230
231 fn visibility(&self) -> Visibility {
232 match &self.item.vis {
233 syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
234 syn::Visibility::Inherited => Visibility::Inherited,
235 _ => unreachable!("encountered invalid visibility for ink! constructor"),
236 }
237 }
238
239 fn inputs(&self) -> InputsIter<'_> {
240 InputsIter::from(self)
241 }
242
243 fn inputs_span(&self) -> Span {
244 self.item.sig.inputs.span()
245 }
246
247 fn statements(&self) -> &[syn::Stmt] {
248 &self.item.block.stmts
249 }
250
251 fn name(&self) -> Option<&str> {
252 self.name.as_deref()
253 }
254
255 fn normalized_name(&self) -> String {
256 self.normalized_name()
257 }
258}
259
260impl Constructor {
261 pub fn attrs(&self) -> &[syn::Attribute] {
263 &self.item.attrs
264 }
265
266 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
268 extract_cfg_attributes(self.attrs(), span)
269 }
270
271 pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
273 extract_cfg_syn_attributes(self.attrs())
274 }
275
276 pub fn output(&self) -> Option<&syn::Type> {
278 match &self.item.sig.output {
279 syn::ReturnType::Default => None,
280 syn::ReturnType::Type(_, return_type) => Some(return_type),
281 }
282 }
283
284 pub fn name(&self) -> Option<&str> {
286 self.name.as_deref()
287 }
288
289 pub fn normalized_name(&self) -> String {
295 self.name()
296 .map(ToString::to_string)
297 .unwrap_or_else(|| self.ident().to_string())
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn inputs_works() {
307 macro_rules! expected_inputs {
308 ( $( $name:ident: $ty:ty ),* ) => {{
309 vec![
310 $(
311 syn::parse_quote! {
312 $name: $ty
313 }
314 ),*
315 ]
316 }};
317 }
318 let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
319 (
320 expected_inputs!(),
322 syn::parse_quote! {
323 #[ink(constructor)]
324 fn my_constructor() -> Self {}
325 },
326 ),
327 (
328 expected_inputs!(a: i32),
330 syn::parse_quote! {
331 #[ink(constructor)]
332 fn my_constructor(a: i32) -> Self {}
333 },
334 ),
335 (
336 expected_inputs!(a: i32, b: u64, c: [u8; 32]),
338 syn::parse_quote! {
339 #[ink(constructor)]
340 fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
341 },
342 ),
343 ];
344 for (expected_inputs, item_method) in test_inputs {
345 let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
346 .unwrap()
347 .inputs()
348 .cloned()
349 .map(syn::FnArg::Typed)
350 .collect::<Vec<_>>();
351 assert_eq!(actual_inputs, expected_inputs);
352 }
353 }
354
355 #[test]
356 fn is_payable_works() {
357 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
358 (
360 false,
361 syn::parse_quote! {
362 #[ink(constructor)]
363 fn my_constructor() -> Self {}
364 },
365 ),
366 (
368 true,
369 syn::parse_quote! {
370 #[ink(constructor, payable)]
371 pub fn my_constructor() -> Self {}
372 },
373 ),
374 (
376 true,
377 syn::parse_quote! {
378 #[ink(constructor)]
379 #[ink(payable)]
380 pub fn my_constructor() -> Self {}
381 },
382 ),
383 (
385 true,
386 syn::parse_quote! {
387 #[ink(constructor)]
388 #[ink(selector = 0xDEADBEEF, payable)]
389 pub fn my_constructor() -> Self {}
390 },
391 ),
392 ];
393 for (expect_payable, item_method) in test_inputs {
394 let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
395 .unwrap()
396 .is_payable();
397 assert_eq!(is_payable, expect_payable);
398 }
399 }
400
401 #[test]
402 fn is_default_works() {
403 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
404 (
406 false,
407 syn::parse_quote! {
408 #[ink(constructor)]
409 fn my_constructor() -> Self {}
410 },
411 ),
412 (
414 true,
415 syn::parse_quote! {
416 #[ink(constructor, default)]
417 pub fn my_constructor() -> Self {}
418 },
419 ),
420 ];
421 for (expect_default, item_method) in test_inputs {
422 let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
423 .unwrap()
424 .is_default();
425 assert_eq!(is_default, expect_default);
426 }
427 }
428
429 #[test]
430 fn name_override_works() {
431 let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
432 (
434 None,
435 syn::parse_quote! {
436 #[ink(constructor)]
437 fn my_constructor() -> Self {}
438 },
439 ),
440 (
442 Some("myConstructor"),
443 syn::parse_quote! {
444 #[ink(constructor, name = "myConstructor")]
445 pub fn my_constructor() -> Self {}
446 },
447 ),
448 ];
449 for (expected_name, item_method) in test_inputs {
450 let ctor = <ir::Constructor as TryFrom<_>>::try_from(item_method).unwrap();
451 assert_eq!(ctor.name(), expected_name);
452 }
453 }
454
455 #[test]
456 fn visibility_works() {
457 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
458 (
460 false,
461 syn::parse_quote! {
462 #[ink(constructor)]
463 fn my_constructor() -> Self {}
464 },
465 ),
466 (
468 true,
469 syn::parse_quote! {
470 #[ink(constructor)]
471 pub fn my_constructor() -> Self {}
472 },
473 ),
474 ];
475 for (is_pub, item_method) in test_inputs {
476 let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
477 .unwrap()
478 .visibility();
479 assert_eq!(visibility.is_pub(), is_pub);
480 assert_eq!(visibility.is_inherited(), !is_pub);
481 }
482 }
483
484 #[test]
485 fn try_from_works() {
486 let item_methods: Vec<syn::ImplItemFn> = vec![
487 syn::parse_quote! {
489 #[ink(constructor)]
490 fn my_constructor() -> Self {}
491 },
492 syn::parse_quote! {
494 #[ink(constructor)]
495 pub fn my_constructor() -> Self {}
496 },
497 syn::parse_quote! {
499 #[ink(constructor)]
500 fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
501 },
502 syn::parse_quote! {
504 #[ink(constructor)]
505 pub fn my_constructor() -> Result<Self, ()> {}
506 },
507 ];
508 for item_method in item_methods {
509 assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
510 }
511 }
512
513 fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
514 assert_eq!(
515 <ir::Constructor as TryFrom<_>>::try_from(item_method)
516 .map_err(|err| err.to_string()),
517 Err(expected_err.to_string()),
518 );
519 }
520
521 #[test]
522 fn try_from_missing_return_fails() {
523 let item_methods: Vec<syn::ImplItemFn> = vec![
524 syn::parse_quote! {
525 #[ink(constructor)]
526 fn my_constructor() {}
527 },
528 syn::parse_quote! {
529 #[ink(constructor)]
530 pub fn my_constructor() {}
531 },
532 ];
533 for item_method in item_methods {
534 assert_try_from_fails(item_method, "missing return for ink! constructor")
535 }
536 }
537
538 #[test]
539 fn try_from_invalid_self_receiver_fails() {
540 let item_methods: Vec<syn::ImplItemFn> = vec![
541 syn::parse_quote! {
542 #[ink(constructor)]
543 fn my_constructor(&self) -> Self {}
544 },
545 syn::parse_quote! {
546 #[ink(constructor)]
547 pub fn my_constructor(&mut self) -> Self {}
548 },
549 syn::parse_quote! {
550 #[ink(constructor)]
551 pub fn my_constructor(self) -> Self {}
552 },
553 syn::parse_quote! {
554 #[ink(constructor)]
555 pub fn my_constructor(mut self) -> Self {}
556 },
557 ];
558 for item_method in item_methods {
559 assert_try_from_fails(
560 item_method,
561 "ink! constructors must have no `self` receiver",
562 )
563 }
564 }
565
566 #[test]
567 fn try_from_generics_fails() {
568 let item_methods: Vec<syn::ImplItemFn> = vec![
569 syn::parse_quote! {
570 #[ink(constructor)]
571 fn my_constructor<T>() -> Self {}
572 },
573 syn::parse_quote! {
574 #[ink(constructor)]
575 pub fn my_constructor<T>() -> Self {}
576 },
577 ];
578 for item_method in item_methods {
579 assert_try_from_fails(item_method, "ink! constructors must not be generic")
580 }
581 }
582
583 #[test]
584 fn try_from_const_fails() {
585 let item_methods: Vec<syn::ImplItemFn> = vec![
586 syn::parse_quote! {
587 #[ink(constructor)]
588 const fn my_constructor() -> Self {}
589 },
590 syn::parse_quote! {
591 #[ink(constructor)]
592 pub const fn my_constructor() -> Self {}
593 },
594 ];
595 for item_method in item_methods {
596 assert_try_from_fails(item_method, "ink! constructors must not be const")
597 }
598 }
599
600 #[test]
601 fn try_from_async_fails() {
602 let item_methods: Vec<syn::ImplItemFn> = vec![
603 syn::parse_quote! {
604 #[ink(constructor)]
605 async fn my_constructor() -> Self {}
606 },
607 syn::parse_quote! {
608 #[ink(constructor)]
609 async fn my_constructor() -> Self {}
610 },
611 ];
612 for item_method in item_methods {
613 assert_try_from_fails(item_method, "ink! constructors must not be async")
614 }
615 }
616
617 #[test]
618 fn try_from_unsafe_fails() {
619 let item_methods: Vec<syn::ImplItemFn> = vec![
620 syn::parse_quote! {
621 #[ink(constructor)]
622 unsafe fn my_constructor() -> Self {}
623 },
624 syn::parse_quote! {
625 #[ink(constructor)]
626 unsafe fn my_constructor() -> Self {}
627 },
628 ];
629 for item_method in item_methods {
630 assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
631 }
632 }
633
634 #[test]
635 fn try_from_explicit_abi_fails() {
636 let item_methods: Vec<syn::ImplItemFn> = vec![
637 syn::parse_quote! {
638 #[ink(constructor)]
639 extern "C" fn my_constructor() -> Self {}
640 },
641 syn::parse_quote! {
642 #[ink(constructor)]
643 extern "C" fn my_constructor() -> Self {}
644 },
645 ];
646 for item_method in item_methods {
647 assert_try_from_fails(
648 item_method,
649 "ink! constructors must not have explicit ABI",
650 )
651 }
652 }
653
654 #[test]
655 fn try_from_variadic_fails() {
656 let item_methods: Vec<syn::ImplItemFn> = vec![
657 syn::parse_quote! {
658 #[ink(constructor)]
659 fn my_constructor(...) -> Self {}
660 },
661 syn::parse_quote! {
662 #[ink(constructor)]
663 fn my_constructor(...) -> Self {}
664 },
665 ];
666 for item_method in item_methods {
667 assert_try_from_fails(item_method, "ink! constructors must not be variadic")
668 }
669 }
670
671 #[test]
672 fn try_from_visibility_fails() {
673 let item_methods: Vec<syn::ImplItemFn> = vec![
674 syn::parse_quote! {
675 #[ink(constructor)]
676 pub(crate) fn my_constructor() -> Self {}
677 },
678 syn::parse_quote! {
679 #[ink(constructor)]
680 pub(in my::path) fn my_constructor() -> Self {}
681 },
682 ];
683 for item_method in item_methods {
684 assert_try_from_fails(
685 item_method,
686 "ink! constructors must have public or inherited visibility",
687 )
688 }
689 }
690
691 #[test]
692 fn conflicting_attributes_fails() {
693 let item_methods: Vec<syn::ImplItemFn> = vec![
694 syn::parse_quote! {
696 #[ink(constructor, storage)]
697 fn my_constructor() -> Self {}
698 },
699 syn::parse_quote! {
701 #[ink(constructor, namespace = "my_namespace")]
702 fn my_constructor() -> Self {}
703 },
704 syn::parse_quote! {
706 #[ink(constructor)]
707 #[ink(event)]
708 fn my_constructor() -> Self {}
709 },
710 ];
711 for item_method in item_methods {
712 assert_try_from_fails(
713 item_method,
714 "encountered conflicting ink! attribute argument",
715 )
716 }
717 }
718
719 #[test]
720 fn try_from_wildcard_constructor_works() {
721 let item: syn::ImplItemFn = syn::parse_quote! {
722 #[ink(constructor, selector = _)]
723 pub fn my_constructor() -> Self {}
724 };
725 assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
726 }
727}