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 Ok(Constructor {
177 selector,
178 is_payable,
179 is_default,
180 name,
181 item: syn::ImplItemFn {
182 attrs: other_attrs,
183 ..method_item
184 },
185 })
186 }
187}
188
189impl Callable for Constructor {
190 fn kind(&self) -> CallableKind {
191 CallableKind::Constructor
192 }
193
194 fn ident(&self) -> &Ident {
195 &self.item.sig.ident
196 }
197
198 fn user_provided_selector(&self) -> Option<&ir::Selector> {
199 if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
200 return Some(selector)
201 }
202 None
203 }
204
205 fn has_wildcard_selector(&self) -> bool {
206 matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
207 }
208
209 fn has_wildcard_complement_selector(&self) -> bool {
210 self.selector == Some(SelectorOrWildcard::wildcard_complement())
211 }
212
213 fn is_payable(&self) -> bool {
214 self.is_payable
215 }
216
217 fn is_default(&self) -> bool {
218 self.is_default
219 }
220
221 fn visibility(&self) -> Visibility {
222 match &self.item.vis {
223 syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
224 syn::Visibility::Inherited => Visibility::Inherited,
225 _ => unreachable!("encountered invalid visibility for ink! constructor"),
226 }
227 }
228
229 fn inputs(&self) -> InputsIter<'_> {
230 InputsIter::from(self)
231 }
232
233 fn inputs_span(&self) -> Span {
234 self.item.sig.inputs.span()
235 }
236
237 fn statements(&self) -> &[syn::Stmt] {
238 &self.item.block.stmts
239 }
240
241 fn name(&self) -> Option<&str> {
242 self.name.as_deref()
243 }
244}
245
246impl Constructor {
247 pub fn attrs(&self) -> &[syn::Attribute] {
249 &self.item.attrs
250 }
251
252 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
254 extract_cfg_attributes(self.attrs(), span)
255 }
256
257 pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
259 extract_cfg_syn_attributes(self.attrs())
260 }
261
262 pub fn output(&self) -> Option<&syn::Type> {
264 match &self.item.sig.output {
265 syn::ReturnType::Default => None,
266 syn::ReturnType::Type(_, return_type) => Some(return_type),
267 }
268 }
269
270 pub fn name(&self) -> Option<&str> {
272 self.name.as_deref()
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn inputs_works() {
282 macro_rules! expected_inputs {
283 ( $( $name:ident: $ty:ty ),* ) => {{
284 vec![
285 $(
286 syn::parse_quote! {
287 $name: $ty
288 }
289 ),*
290 ]
291 }};
292 }
293 let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
294 (
295 expected_inputs!(),
297 syn::parse_quote! {
298 #[ink(constructor)]
299 fn my_constructor() -> Self {}
300 },
301 ),
302 (
303 expected_inputs!(a: i32),
305 syn::parse_quote! {
306 #[ink(constructor)]
307 fn my_constructor(a: i32) -> Self {}
308 },
309 ),
310 (
311 expected_inputs!(a: i32, b: u64, c: [u8; 32]),
313 syn::parse_quote! {
314 #[ink(constructor)]
315 fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
316 },
317 ),
318 ];
319 for (expected_inputs, item_method) in test_inputs {
320 let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
321 .unwrap()
322 .inputs()
323 .cloned()
324 .map(syn::FnArg::Typed)
325 .collect::<Vec<_>>();
326 assert_eq!(actual_inputs, expected_inputs);
327 }
328 }
329
330 #[test]
331 fn is_payable_works() {
332 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
333 (
335 false,
336 syn::parse_quote! {
337 #[ink(constructor)]
338 fn my_constructor() -> Self {}
339 },
340 ),
341 (
343 true,
344 syn::parse_quote! {
345 #[ink(constructor, payable)]
346 pub fn my_constructor() -> Self {}
347 },
348 ),
349 (
351 true,
352 syn::parse_quote! {
353 #[ink(constructor)]
354 #[ink(payable)]
355 pub fn my_constructor() -> Self {}
356 },
357 ),
358 (
360 true,
361 syn::parse_quote! {
362 #[ink(constructor)]
363 #[ink(selector = 0xDEADBEEF, payable)]
364 pub fn my_constructor() -> Self {}
365 },
366 ),
367 ];
368 for (expect_payable, item_method) in test_inputs {
369 let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
370 .unwrap()
371 .is_payable();
372 assert_eq!(is_payable, expect_payable);
373 }
374 }
375
376 #[test]
377 fn is_default_works() {
378 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
379 (
381 false,
382 syn::parse_quote! {
383 #[ink(constructor)]
384 fn my_constructor() -> Self {}
385 },
386 ),
387 (
389 true,
390 syn::parse_quote! {
391 #[ink(constructor, default)]
392 pub fn my_constructor() -> Self {}
393 },
394 ),
395 ];
396 for (expect_default, item_method) in test_inputs {
397 let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
398 .unwrap()
399 .is_default();
400 assert_eq!(is_default, expect_default);
401 }
402 }
403
404 #[test]
405 fn name_override_works() {
406 let test_inputs: Vec<(Option<&str>, syn::ImplItemFn)> = vec![
407 (
409 None,
410 syn::parse_quote! {
411 #[ink(constructor)]
412 fn my_constructor() -> Self {}
413 },
414 ),
415 (
417 Some("myConstructor"),
418 syn::parse_quote! {
419 #[ink(constructor, name = "myConstructor")]
420 pub fn my_constructor() -> Self {}
421 },
422 ),
423 ];
424 for (expected_name, item_method) in test_inputs {
425 let ctor = <ir::Constructor as TryFrom<_>>::try_from(item_method).unwrap();
426 assert_eq!(ctor.name(), expected_name);
427 }
428 }
429
430 #[test]
431 fn visibility_works() {
432 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
433 (
435 false,
436 syn::parse_quote! {
437 #[ink(constructor)]
438 fn my_constructor() -> Self {}
439 },
440 ),
441 (
443 true,
444 syn::parse_quote! {
445 #[ink(constructor)]
446 pub fn my_constructor() -> Self {}
447 },
448 ),
449 ];
450 for (is_pub, item_method) in test_inputs {
451 let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
452 .unwrap()
453 .visibility();
454 assert_eq!(visibility.is_pub(), is_pub);
455 assert_eq!(visibility.is_inherited(), !is_pub);
456 }
457 }
458
459 #[test]
460 fn try_from_works() {
461 let item_methods: Vec<syn::ImplItemFn> = vec![
462 syn::parse_quote! {
464 #[ink(constructor)]
465 fn my_constructor() -> Self {}
466 },
467 syn::parse_quote! {
469 #[ink(constructor)]
470 pub fn my_constructor() -> Self {}
471 },
472 syn::parse_quote! {
474 #[ink(constructor)]
475 fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
476 },
477 syn::parse_quote! {
479 #[ink(constructor)]
480 pub fn my_constructor() -> Result<Self, ()> {}
481 },
482 ];
483 for item_method in item_methods {
484 assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
485 }
486 }
487
488 fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
489 assert_eq!(
490 <ir::Constructor as TryFrom<_>>::try_from(item_method)
491 .map_err(|err| err.to_string()),
492 Err(expected_err.to_string()),
493 );
494 }
495
496 #[test]
497 fn try_from_missing_return_fails() {
498 let item_methods: Vec<syn::ImplItemFn> = vec![
499 syn::parse_quote! {
500 #[ink(constructor)]
501 fn my_constructor() {}
502 },
503 syn::parse_quote! {
504 #[ink(constructor)]
505 pub fn my_constructor() {}
506 },
507 ];
508 for item_method in item_methods {
509 assert_try_from_fails(item_method, "missing return for ink! constructor")
510 }
511 }
512
513 #[test]
514 fn try_from_invalid_self_receiver_fails() {
515 let item_methods: Vec<syn::ImplItemFn> = vec![
516 syn::parse_quote! {
517 #[ink(constructor)]
518 fn my_constructor(&self) -> Self {}
519 },
520 syn::parse_quote! {
521 #[ink(constructor)]
522 pub fn my_constructor(&mut self) -> Self {}
523 },
524 syn::parse_quote! {
525 #[ink(constructor)]
526 pub fn my_constructor(self) -> Self {}
527 },
528 syn::parse_quote! {
529 #[ink(constructor)]
530 pub fn my_constructor(mut self) -> Self {}
531 },
532 ];
533 for item_method in item_methods {
534 assert_try_from_fails(
535 item_method,
536 "ink! constructors must have no `self` receiver",
537 )
538 }
539 }
540
541 #[test]
542 fn try_from_generics_fails() {
543 let item_methods: Vec<syn::ImplItemFn> = vec![
544 syn::parse_quote! {
545 #[ink(constructor)]
546 fn my_constructor<T>() -> Self {}
547 },
548 syn::parse_quote! {
549 #[ink(constructor)]
550 pub fn my_constructor<T>() -> Self {}
551 },
552 ];
553 for item_method in item_methods {
554 assert_try_from_fails(item_method, "ink! constructors must not be generic")
555 }
556 }
557
558 #[test]
559 fn try_from_const_fails() {
560 let item_methods: Vec<syn::ImplItemFn> = vec![
561 syn::parse_quote! {
562 #[ink(constructor)]
563 const fn my_constructor() -> Self {}
564 },
565 syn::parse_quote! {
566 #[ink(constructor)]
567 pub const fn my_constructor() -> Self {}
568 },
569 ];
570 for item_method in item_methods {
571 assert_try_from_fails(item_method, "ink! constructors must not be const")
572 }
573 }
574
575 #[test]
576 fn try_from_async_fails() {
577 let item_methods: Vec<syn::ImplItemFn> = vec![
578 syn::parse_quote! {
579 #[ink(constructor)]
580 async fn my_constructor() -> Self {}
581 },
582 syn::parse_quote! {
583 #[ink(constructor)]
584 async fn my_constructor() -> Self {}
585 },
586 ];
587 for item_method in item_methods {
588 assert_try_from_fails(item_method, "ink! constructors must not be async")
589 }
590 }
591
592 #[test]
593 fn try_from_unsafe_fails() {
594 let item_methods: Vec<syn::ImplItemFn> = vec![
595 syn::parse_quote! {
596 #[ink(constructor)]
597 unsafe fn my_constructor() -> Self {}
598 },
599 syn::parse_quote! {
600 #[ink(constructor)]
601 unsafe fn my_constructor() -> Self {}
602 },
603 ];
604 for item_method in item_methods {
605 assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
606 }
607 }
608
609 #[test]
610 fn try_from_explicit_abi_fails() {
611 let item_methods: Vec<syn::ImplItemFn> = vec![
612 syn::parse_quote! {
613 #[ink(constructor)]
614 extern "C" fn my_constructor() -> Self {}
615 },
616 syn::parse_quote! {
617 #[ink(constructor)]
618 extern "C" fn my_constructor() -> Self {}
619 },
620 ];
621 for item_method in item_methods {
622 assert_try_from_fails(
623 item_method,
624 "ink! constructors must not have explicit ABI",
625 )
626 }
627 }
628
629 #[test]
630 fn try_from_variadic_fails() {
631 let item_methods: Vec<syn::ImplItemFn> = vec![
632 syn::parse_quote! {
633 #[ink(constructor)]
634 fn my_constructor(...) -> Self {}
635 },
636 syn::parse_quote! {
637 #[ink(constructor)]
638 fn my_constructor(...) -> Self {}
639 },
640 ];
641 for item_method in item_methods {
642 assert_try_from_fails(item_method, "ink! constructors must not be variadic")
643 }
644 }
645
646 #[test]
647 fn try_from_visibility_fails() {
648 let item_methods: Vec<syn::ImplItemFn> = vec![
649 syn::parse_quote! {
650 #[ink(constructor)]
651 pub(crate) fn my_constructor() -> Self {}
652 },
653 syn::parse_quote! {
654 #[ink(constructor)]
655 pub(in my::path) fn my_constructor() -> Self {}
656 },
657 ];
658 for item_method in item_methods {
659 assert_try_from_fails(
660 item_method,
661 "ink! constructors must have public or inherited visibility",
662 )
663 }
664 }
665
666 #[test]
667 fn conflicting_attributes_fails() {
668 let item_methods: Vec<syn::ImplItemFn> = vec![
669 syn::parse_quote! {
671 #[ink(constructor, storage)]
672 fn my_constructor() -> Self {}
673 },
674 syn::parse_quote! {
676 #[ink(constructor, namespace = "my_namespace")]
677 fn my_constructor() -> Self {}
678 },
679 syn::parse_quote! {
681 #[ink(constructor)]
682 #[ink(event)]
683 fn my_constructor() -> Self {}
684 },
685 ];
686 for item_method in item_methods {
687 assert_try_from_fails(
688 item_method,
689 "encountered conflicting ink! attribute argument",
690 )
691 }
692 }
693
694 #[test]
695 fn try_from_wildcard_constructor_works() {
696 let item: syn::ImplItemFn = syn::parse_quote! {
697 #[ink(constructor, selector = _)]
698 pub fn my_constructor() -> Self {}
699 };
700 assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
701 }
702}