1use super::{
16 ensure_callable_invariants,
17 Callable,
18 CallableKind,
19 InputsIter,
20 Visibility,
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}
86
87impl quote::ToTokens for Constructor {
88 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
91 self.item.to_tokens(tokens)
92 }
93}
94
95impl Constructor {
96 fn ensure_return(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
103 if let syn::ReturnType::Default = &method_item.sig.output {
104 return Err(format_err_spanned!(
105 &method_item.sig,
106 "missing return for ink! constructor",
107 ))
108 }
109 Ok(())
110 }
111
112 fn ensure_no_self_receiver(method_item: &syn::ImplItemFn) -> Result<(), syn::Error> {
121 match method_item.sig.inputs.iter().next() {
122 None | Some(syn::FnArg::Typed(_)) => (),
123 Some(syn::FnArg::Receiver(receiver)) => {
124 return Err(format_err_spanned!(
125 receiver,
126 "ink! constructors must have no `self` receiver",
127 ))
128 }
129 }
130 Ok(())
131 }
132
133 fn sanitize_attributes(
137 method_item: &syn::ImplItemFn,
138 ) -> Result<(ir::InkAttribute, Vec<syn::Attribute>), syn::Error> {
139 ir::sanitize_attributes(
140 method_item.span(),
141 method_item.attrs.clone(),
142 &ir::AttributeArgKind::Constructor,
143 |arg| {
144 match arg.kind() {
145 ir::AttributeArg::Constructor
146 | ir::AttributeArg::Payable
147 | ir::AttributeArg::Default
148 | ir::AttributeArg::Selector(_) => Ok(()),
149 _ => Err(None),
150 }
151 },
152 )
153 }
154}
155
156impl TryFrom<syn::ImplItemFn> for Constructor {
157 type Error = syn::Error;
158
159 fn try_from(method_item: syn::ImplItemFn) -> Result<Self, Self::Error> {
160 ensure_callable_invariants(&method_item, CallableKind::Constructor)?;
161 Self::ensure_return(&method_item)?;
162 Self::ensure_no_self_receiver(&method_item)?;
163 let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
164 let is_payable = ink_attrs.is_payable();
165 let is_default = ink_attrs.is_default();
166 let selector = ink_attrs.selector();
167 Ok(Constructor {
168 selector,
169 is_payable,
170 is_default,
171 item: syn::ImplItemFn {
172 attrs: other_attrs,
173 ..method_item
174 },
175 })
176 }
177}
178
179impl Callable for Constructor {
180 fn kind(&self) -> CallableKind {
181 CallableKind::Constructor
182 }
183
184 fn ident(&self) -> &Ident {
185 &self.item.sig.ident
186 }
187
188 fn user_provided_selector(&self) -> Option<&ir::Selector> {
189 if let Some(SelectorOrWildcard::UserProvided(selector)) = self.selector.as_ref() {
190 return Some(selector)
191 }
192 None
193 }
194
195 fn has_wildcard_selector(&self) -> bool {
196 matches!(self.selector, Some(SelectorOrWildcard::Wildcard))
197 }
198
199 fn has_wildcard_complement_selector(&self) -> bool {
200 self.selector == Some(SelectorOrWildcard::wildcard_complement())
201 }
202
203 fn is_payable(&self) -> bool {
204 self.is_payable
205 }
206
207 fn is_default(&self) -> bool {
208 self.is_default
209 }
210
211 fn visibility(&self) -> Visibility {
212 match &self.item.vis {
213 syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
214 syn::Visibility::Inherited => Visibility::Inherited,
215 _ => unreachable!("encountered invalid visibility for ink! constructor"),
216 }
217 }
218
219 fn inputs(&self) -> InputsIter {
220 InputsIter::from(self)
221 }
222
223 fn inputs_span(&self) -> Span {
224 self.item.sig.inputs.span()
225 }
226
227 fn statements(&self) -> &[syn::Stmt] {
228 &self.item.block.stmts
229 }
230}
231
232impl Constructor {
233 pub fn attrs(&self) -> &[syn::Attribute] {
235 &self.item.attrs
236 }
237
238 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
240 extract_cfg_attributes(self.attrs(), span)
241 }
242
243 pub fn get_cfg_syn_attrs(&self) -> Vec<syn::Attribute> {
245 extract_cfg_syn_attributes(self.attrs())
246 }
247
248 pub fn output(&self) -> Option<&syn::Type> {
250 match &self.item.sig.output {
251 syn::ReturnType::Default => None,
252 syn::ReturnType::Type(_, return_type) => Some(return_type),
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn inputs_works() {
263 macro_rules! expected_inputs {
264 ( $( $name:ident: $ty:ty ),* ) => {{
265 vec![
266 $(
267 syn::parse_quote! {
268 $name: $ty
269 }
270 ),*
271 ]
272 }};
273 }
274 let test_inputs: Vec<(Vec<syn::FnArg>, syn::ImplItemFn)> = vec![
275 (
276 expected_inputs!(),
278 syn::parse_quote! {
279 #[ink(constructor)]
280 fn my_constructor() -> Self {}
281 },
282 ),
283 (
284 expected_inputs!(a: i32),
286 syn::parse_quote! {
287 #[ink(constructor)]
288 fn my_constructor(a: i32) -> Self {}
289 },
290 ),
291 (
292 expected_inputs!(a: i32, b: u64, c: [u8; 32]),
294 syn::parse_quote! {
295 #[ink(constructor)]
296 fn my_constructor(a: i32, b: u64, c: [u8; 32]) -> Self {}
297 },
298 ),
299 ];
300 for (expected_inputs, item_method) in test_inputs {
301 let actual_inputs = <ir::Constructor as TryFrom<_>>::try_from(item_method)
302 .unwrap()
303 .inputs()
304 .cloned()
305 .map(syn::FnArg::Typed)
306 .collect::<Vec<_>>();
307 assert_eq!(actual_inputs, expected_inputs);
308 }
309 }
310
311 #[test]
312 fn is_payable_works() {
313 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
314 (
316 false,
317 syn::parse_quote! {
318 #[ink(constructor)]
319 fn my_constructor() -> Self {}
320 },
321 ),
322 (
324 true,
325 syn::parse_quote! {
326 #[ink(constructor, payable)]
327 pub fn my_constructor() -> Self {}
328 },
329 ),
330 (
332 true,
333 syn::parse_quote! {
334 #[ink(constructor)]
335 #[ink(payable)]
336 pub fn my_constructor() -> Self {}
337 },
338 ),
339 (
341 true,
342 syn::parse_quote! {
343 #[ink(constructor)]
344 #[ink(selector = 0xDEADBEEF, payable)]
345 pub fn my_constructor() -> Self {}
346 },
347 ),
348 ];
349 for (expect_payable, item_method) in test_inputs {
350 let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
351 .unwrap()
352 .is_payable();
353 assert_eq!(is_payable, expect_payable);
354 }
355 }
356
357 #[test]
358 fn is_default_works() {
359 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
360 (
362 false,
363 syn::parse_quote! {
364 #[ink(constructor)]
365 fn my_constructor() -> Self {}
366 },
367 ),
368 (
370 true,
371 syn::parse_quote! {
372 #[ink(constructor, default)]
373 pub fn my_constructor() -> Self {}
374 },
375 ),
376 ];
377 for (expect_default, item_method) in test_inputs {
378 let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
379 .unwrap()
380 .is_default();
381 assert_eq!(is_default, expect_default);
382 }
383 }
384
385 #[test]
386 fn visibility_works() {
387 let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
388 (
390 false,
391 syn::parse_quote! {
392 #[ink(constructor)]
393 fn my_constructor() -> Self {}
394 },
395 ),
396 (
398 true,
399 syn::parse_quote! {
400 #[ink(constructor)]
401 pub fn my_constructor() -> Self {}
402 },
403 ),
404 ];
405 for (is_pub, item_method) in test_inputs {
406 let visibility = <ir::Constructor as TryFrom<_>>::try_from(item_method)
407 .unwrap()
408 .visibility();
409 assert_eq!(visibility.is_pub(), is_pub);
410 assert_eq!(visibility.is_inherited(), !is_pub);
411 }
412 }
413
414 #[test]
415 fn try_from_works() {
416 let item_methods: Vec<syn::ImplItemFn> = vec![
417 syn::parse_quote! {
419 #[ink(constructor)]
420 fn my_constructor() -> Self {}
421 },
422 syn::parse_quote! {
424 #[ink(constructor)]
425 pub fn my_constructor() -> Self {}
426 },
427 syn::parse_quote! {
429 #[ink(constructor)]
430 fn my_constructor(input1: i32, input2: i64, input3: u32, input4: u64) -> Self {}
431 },
432 syn::parse_quote! {
434 #[ink(constructor)]
435 pub fn my_constructor() -> Result<Self, ()> {}
436 },
437 ];
438 for item_method in item_methods {
439 assert!(<ir::Constructor as TryFrom<_>>::try_from(item_method).is_ok());
440 }
441 }
442
443 fn assert_try_from_fails(item_method: syn::ImplItemFn, expected_err: &str) {
444 assert_eq!(
445 <ir::Constructor as TryFrom<_>>::try_from(item_method)
446 .map_err(|err| err.to_string()),
447 Err(expected_err.to_string()),
448 );
449 }
450
451 #[test]
452 fn try_from_missing_return_fails() {
453 let item_methods: Vec<syn::ImplItemFn> = vec![
454 syn::parse_quote! {
455 #[ink(constructor)]
456 fn my_constructor() {}
457 },
458 syn::parse_quote! {
459 #[ink(constructor)]
460 pub fn my_constructor() {}
461 },
462 ];
463 for item_method in item_methods {
464 assert_try_from_fails(item_method, "missing return for ink! constructor")
465 }
466 }
467
468 #[test]
469 fn try_from_invalid_self_receiver_fails() {
470 let item_methods: Vec<syn::ImplItemFn> = vec![
471 syn::parse_quote! {
472 #[ink(constructor)]
473 fn my_constructor(&self) -> Self {}
474 },
475 syn::parse_quote! {
476 #[ink(constructor)]
477 pub fn my_constructor(&mut self) -> Self {}
478 },
479 syn::parse_quote! {
480 #[ink(constructor)]
481 pub fn my_constructor(self) -> Self {}
482 },
483 syn::parse_quote! {
484 #[ink(constructor)]
485 pub fn my_constructor(mut self) -> Self {}
486 },
487 ];
488 for item_method in item_methods {
489 assert_try_from_fails(
490 item_method,
491 "ink! constructors must have no `self` receiver",
492 )
493 }
494 }
495
496 #[test]
497 fn try_from_generics_fails() {
498 let item_methods: Vec<syn::ImplItemFn> = vec![
499 syn::parse_quote! {
500 #[ink(constructor)]
501 fn my_constructor<T>() -> Self {}
502 },
503 syn::parse_quote! {
504 #[ink(constructor)]
505 pub fn my_constructor<T>() -> Self {}
506 },
507 ];
508 for item_method in item_methods {
509 assert_try_from_fails(item_method, "ink! constructors must not be generic")
510 }
511 }
512
513 #[test]
514 fn try_from_const_fails() {
515 let item_methods: Vec<syn::ImplItemFn> = vec![
516 syn::parse_quote! {
517 #[ink(constructor)]
518 const fn my_constructor() -> Self {}
519 },
520 syn::parse_quote! {
521 #[ink(constructor)]
522 pub const fn my_constructor() -> Self {}
523 },
524 ];
525 for item_method in item_methods {
526 assert_try_from_fails(item_method, "ink! constructors must not be const")
527 }
528 }
529
530 #[test]
531 fn try_from_async_fails() {
532 let item_methods: Vec<syn::ImplItemFn> = vec![
533 syn::parse_quote! {
534 #[ink(constructor)]
535 async fn my_constructor() -> Self {}
536 },
537 syn::parse_quote! {
538 #[ink(constructor)]
539 async fn my_constructor() -> Self {}
540 },
541 ];
542 for item_method in item_methods {
543 assert_try_from_fails(item_method, "ink! constructors must not be async")
544 }
545 }
546
547 #[test]
548 fn try_from_unsafe_fails() {
549 let item_methods: Vec<syn::ImplItemFn> = vec![
550 syn::parse_quote! {
551 #[ink(constructor)]
552 unsafe fn my_constructor() -> Self {}
553 },
554 syn::parse_quote! {
555 #[ink(constructor)]
556 unsafe fn my_constructor() -> Self {}
557 },
558 ];
559 for item_method in item_methods {
560 assert_try_from_fails(item_method, "ink! constructors must not be unsafe")
561 }
562 }
563
564 #[test]
565 fn try_from_explicit_abi_fails() {
566 let item_methods: Vec<syn::ImplItemFn> = vec![
567 syn::parse_quote! {
568 #[ink(constructor)]
569 extern "C" fn my_constructor() -> Self {}
570 },
571 syn::parse_quote! {
572 #[ink(constructor)]
573 extern "C" fn my_constructor() -> Self {}
574 },
575 ];
576 for item_method in item_methods {
577 assert_try_from_fails(
578 item_method,
579 "ink! constructors must not have explicit ABI",
580 )
581 }
582 }
583
584 #[test]
585 fn try_from_variadic_fails() {
586 let item_methods: Vec<syn::ImplItemFn> = vec![
587 syn::parse_quote! {
588 #[ink(constructor)]
589 fn my_constructor(...) -> Self {}
590 },
591 syn::parse_quote! {
592 #[ink(constructor)]
593 fn my_constructor(...) -> Self {}
594 },
595 ];
596 for item_method in item_methods {
597 assert_try_from_fails(item_method, "ink! constructors must not be variadic")
598 }
599 }
600
601 #[test]
602 fn try_from_visibility_fails() {
603 let item_methods: Vec<syn::ImplItemFn> = vec![
604 syn::parse_quote! {
605 #[ink(constructor)]
606 pub(crate) fn my_constructor() -> Self {}
607 },
608 syn::parse_quote! {
609 #[ink(constructor)]
610 pub(in my::path) fn my_constructor() -> Self {}
611 },
612 ];
613 for item_method in item_methods {
614 assert_try_from_fails(
615 item_method,
616 "ink! constructors must have public or inherited visibility",
617 )
618 }
619 }
620
621 #[test]
622 fn conflicting_attributes_fails() {
623 let item_methods: Vec<syn::ImplItemFn> = vec![
624 syn::parse_quote! {
626 #[ink(constructor, storage)]
627 fn my_constructor() -> Self {}
628 },
629 syn::parse_quote! {
631 #[ink(constructor, namespace = "my_namespace")]
632 fn my_constructor() -> Self {}
633 },
634 syn::parse_quote! {
636 #[ink(constructor)]
637 #[ink(event)]
638 fn my_constructor() -> Self {}
639 },
640 ];
641 for item_method in item_methods {
642 assert_try_from_fails(
643 item_method,
644 "encountered conflicting ink! attribute argument",
645 )
646 }
647 }
648
649 #[test]
650 fn try_from_wildcard_constructor_works() {
651 let item: syn::ImplItemFn = syn::parse_quote! {
652 #[ink(constructor, selector = _)]
653 pub fn my_constructor() -> Self {}
654 };
655 assert!(<ir::Constructor as TryFrom<_>>::try_from(item).is_ok());
656 }
657}