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
256impl Constructor {
257 pub fn attrs(&self) -> &[syn::Attribute] {
259 &self.item.attrs
260 }
261
262 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
264 extract_cfg_attributes(self.attrs(), span)
265 }
266
267 pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
269 extract_cfg_syn_attributes(self.attrs())
270 }
271
272 pub fn output(&self) -> Option<&syn::Type> {
274 match &self.item.sig.output {
275 syn::ReturnType::Default => None,
276 syn::ReturnType::Type(_, return_type) => Some(return_type),
277 }
278 }
279
280 pub fn name(&self) -> Option<&str> {
282 self.name.as_deref()
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn inputs_works() {
292 macro_rules! expected_inputs {
293 ( $( $name:ident: $ty:ty ),* ) => {{
294 vec![
295 $(
296 syn::parse_quote! {
297 $name: $ty
298 }
299 ),*
300 ]
301 }};
302 }
303 let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
304 (
305 expected_inputs!(),
307 syn::parse_quote! {
308 #[ink(constructor)]
309 fn my_constructor() -> Self {}
310 },
311 ),
312 (
313 expected_inputs!(a: i32),
315 syn::parse_quote! {
316 #[ink(constructor)]
317 fn my_constructor(a: i32) -> Self {}
318 },
319 ),
320 (
321 expected_inputs!(a: i32, b: u64, c: [u8; 32]),
323 syn::parse_quote! {
324 #[ink(constructor)]
325 fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
326 },
327 ),
328 ];
329 for (expected_inputs, item_method) in test_inputs {
330 let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
331 .unwrap()
332 .inputs()
333 .cloned()
334 .map(syn::FnArg::Typed)
335 .collect::<Vec<_>>();
336 assert_eq!(actual_inputs, expected_inputs);
337 }
338 }
339
340 #[test]
341 fn is_payable_works() {
342 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
343 (
345 false,
346 syn::parse_quote! {
347 #[ink(constructor)]
348 fn my_constructor() -> Self {}
349 },
350 ),
351 (
353 true,
354 syn::parse_quote! {
355 #[ink(constructor, payable)]
356 pub fn my_constructor() -> Self {}
357 },
358 ),
359 (
361 true,
362 syn::parse_quote! {
363 #[ink(constructor)]
364 #[ink(payable)]
365 pub fn my_constructor() -> Self {}
366 },
367 ),
368 (
370 true,
371 syn::parse_quote! {
372 #[ink(constructor)]
373 #[ink(selector = 0xDEADBEEF, payable)]
374 pub fn my_constructor() -> Self {}
375 },
376 ),
377 ];
378 for (expect_payable, item_method) in test_inputs {
379 let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
380 .unwrap()
381 .is_payable();
382 assert_eq!(is_payable, expect_payable);
383 }
384 }
385
386 #[test]
387 fn is_default_works() {
388 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
389 (
391 false,
392 syn::parse_quote! {
393 #[ink(constructor)]
394 fn my_constructor() -> Self {}
395 },
396 ),
397 (
399 true,
400 syn::parse_quote! {
401 #[ink(constructor, default)]
402 pub fn my_constructor() -> Self {}
403 },
404 ),
405 ];
406 for (expect_default, item_method) in test_inputs {
407 let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
408 .unwrap()
409 .is_default();
410 assert_eq!(is_default, expect_default);
411 }
412 }
413
414 #[test]
415 fn name_override_works() {
416 let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
417 (
419 None,
420 syn::parse_quote! {
421 #[ink(constructor)]
422 fn my_constructor() -> Self {}
423 },
424 ),
425 (
427 Some("myConstructor"),
428 syn::parse_quote! {
429 #[ink(constructor, name = "myConstructor")]
430 pub fn my_constructor() -> Self {}
431 },
432 ),
433 ];
434 for (expected_name, item_method) in test_inputs {
435 let ctor = <ir::Constructor as TryFrom<_>>::try_from(item_method).unwrap();
436 assert_eq!(ctor.name(), expected_name);
437 }
438 }
439
440 #[test]
441 fn visibility_works() {
442 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
443 (
445 false,
446 syn::parse_quote! {
447 #[ink(constructor)]
448 fn my_constructor() -> Self {}
449 },
450 ),
451 (
453 true,
454 syn::parse_quote! {
455 #[ink(constructor)]
456 pub fn my_constructor() -> Self {}
457 },
458 ),
459 ];
460 for (is_pub, item_method) in test_inputs {
461 let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
462 .unwrap()
463 .visibility();
464 assert_eq!(visibility.is_pub(), is_pub);
465 assert_eq!(visibility.is_inherited(), !is_pub);
466 }
467 }
468
469 #[test]
470 fn try_from_works() {
471 let item_methods: Vec<syn::ImplItemFn> = vec![
472 syn::parse_quote! {
474 #[ink(constructor)]
475 fn my_constructor() -> Self {}
476 },
477 syn::parse_quote! {
479 #[ink(constructor)]
480 pub fn my_constructor() -> Self {}
481 },
482 syn::parse_quote! {
484 #[ink(constructor)]
485 fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
486 },
487 syn::parse_quote! {
489 #[ink(constructor)]
490 pub fn my_constructor() -> Result<Self, ()> {}
491 },
492 ];
493 for item_method in item_methods {
494 assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
495 }
496 }
497
498 fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
499 assert_eq!(
500 <ir::Constructor as TryFrom<_>>::try_from(item_method)
501 .map_err(|err| err.to_string()),
502 Err(expected_err.to_string()),
503 );
504 }
505
506 #[test]
507 fn try_from_missing_return_fails() {
508 let item_methods: Vec<syn::ImplItemFn> = vec![
509 syn::parse_quote! {
510 #[ink(constructor)]
511 fn my_constructor() {}
512 },
513 syn::parse_quote! {
514 #[ink(constructor)]
515 pub fn my_constructor() {}
516 },
517 ];
518 for item_method in item_methods {
519 assert_try_from_fails(item_method, "missing return for ink! constructor")
520 }
521 }
522
523 #[test]
524 fn try_from_invalid_self_receiver_fails() {
525 let item_methods: Vec<syn::ImplItemFn> = vec![
526 syn::parse_quote! {
527 #[ink(constructor)]
528 fn my_constructor(&self) -> Self {}
529 },
530 syn::parse_quote! {
531 #[ink(constructor)]
532 pub fn my_constructor(&mut self) -> Self {}
533 },
534 syn::parse_quote! {
535 #[ink(constructor)]
536 pub fn my_constructor(self) -> Self {}
537 },
538 syn::parse_quote! {
539 #[ink(constructor)]
540 pub fn my_constructor(mut self) -> Self {}
541 },
542 ];
543 for item_method in item_methods {
544 assert_try_from_fails(
545 item_method,
546 "ink! constructors must have no `self` receiver",
547 )
548 }
549 }
550
551 #[test]
552 fn try_from_generics_fails() {
553 let item_methods: Vec<syn::ImplItemFn> = vec![
554 syn::parse_quote! {
555 #[ink(constructor)]
556 fn my_constructor<T>() -> Self {}
557 },
558 syn::parse_quote! {
559 #[ink(constructor)]
560 pub fn my_constructor<T>() -> Self {}
561 },
562 ];
563 for item_method in item_methods {
564 assert_try_from_fails(item_method, "ink! constructors must not be generic")
565 }
566 }
567
568 #[test]
569 fn try_from_const_fails() {
570 let item_methods: Vec<syn::ImplItemFn> = vec![
571 syn::parse_quote! {
572 #[ink(constructor)]
573 const fn my_constructor() -> Self {}
574 },
575 syn::parse_quote! {
576 #[ink(constructor)]
577 pub const fn my_constructor() -> Self {}
578 },
579 ];
580 for item_method in item_methods {
581 assert_try_from_fails(item_method, "ink! constructors must not be const")
582 }
583 }
584
585 #[test]
586 fn try_from_async_fails() {
587 let item_methods: Vec<syn::ImplItemFn> = vec![
588 syn::parse_quote! {
589 #[ink(constructor)]
590 async fn my_constructor() -> Self {}
591 },
592 syn::parse_quote! {
593 #[ink(constructor)]
594 async fn my_constructor() -> Self {}
595 },
596 ];
597 for item_method in item_methods {
598 assert_try_from_fails(item_method, "ink! constructors must not be async")
599 }
600 }
601
602 #[test]
603 fn try_from_unsafe_fails() {
604 let item_methods: Vec<syn::ImplItemFn> = vec![
605 syn::parse_quote! {
606 #[ink(constructor)]
607 unsafe fn my_constructor() -> Self {}
608 },
609 syn::parse_quote! {
610 #[ink(constructor)]
611 unsafe fn my_constructor() -> Self {}
612 },
613 ];
614 for item_method in item_methods {
615 assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
616 }
617 }
618
619 #[test]
620 fn try_from_explicit_abi_fails() {
621 let item_methods: Vec<syn::ImplItemFn> = vec![
622 syn::parse_quote! {
623 #[ink(constructor)]
624 extern "C" fn my_constructor() -> Self {}
625 },
626 syn::parse_quote! {
627 #[ink(constructor)]
628 extern "C" fn my_constructor() -> Self {}
629 },
630 ];
631 for item_method in item_methods {
632 assert_try_from_fails(
633 item_method,
634 "ink! constructors must not have explicit ABI",
635 )
636 }
637 }
638
639 #[test]
640 fn try_from_variadic_fails() {
641 let item_methods: Vec<syn::ImplItemFn> = vec![
642 syn::parse_quote! {
643 #[ink(constructor)]
644 fn my_constructor(...) -> Self {}
645 },
646 syn::parse_quote! {
647 #[ink(constructor)]
648 fn my_constructor(...) -> Self {}
649 },
650 ];
651 for item_method in item_methods {
652 assert_try_from_fails(item_method, "ink! constructors must not be variadic")
653 }
654 }
655
656 #[test]
657 fn try_from_visibility_fails() {
658 let item_methods: Vec<syn::ImplItemFn> = vec![
659 syn::parse_quote! {
660 #[ink(constructor)]
661 pub(crate) fn my_constructor() -> Self {}
662 },
663 syn::parse_quote! {
664 #[ink(constructor)]
665 pub(in my::path) fn my_constructor() -> Self {}
666 },
667 ];
668 for item_method in item_methods {
669 assert_try_from_fails(
670 item_method,
671 "ink! constructors must have public or inherited visibility",
672 )
673 }
674 }
675
676 #[test]
677 fn conflicting_attributes_fails() {
678 let item_methods: Vec<syn::ImplItemFn> = vec![
679 syn::parse_quote! {
681 #[ink(constructor, storage)]
682 fn my_constructor() -> Self {}
683 },
684 syn::parse_quote! {
686 #[ink(constructor, namespace = "my_namespace")]
687 fn my_constructor() -> Self {}
688 },
689 syn::parse_quote! {
691 #[ink(constructor)]
692 #[ink(event)]
693 fn my_constructor() -> Self {}
694 },
695 ];
696 for item_method in item_methods {
697 assert_try_from_fails(
698 item_method,
699 "encountered conflicting ink! attribute argument",
700 )
701 }
702 }
703
704 #[test]
705 fn try_from_wildcard_constructor_works() {
706 let item: syn::ImplItemFn = syn::parse_quote! {
707 #[ink(constructor, selector = _)]
708 pub fn my_constructor() -> Self {}
709 };
710 assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
711 }
712}