Skip to content

Commit

Permalink
Add #[ink(default)] attribute for constructors and messages (#1724)
Browse files Browse the repository at this point in the history
* default attribute

* fmt

* fix tests

* update changelog

* Update crates/metadata/src/specs.rs

* Update crates/metadata/src/specs.rs

* Apply suggestions from code review

* Add UIs to dictionary

---------

Co-authored-by: Michael Müller <[email protected]>
Co-authored-by: Hernando Castano <[email protected]>
Co-authored-by: Hernando Castano <[email protected]>
  • Loading branch information
4 people authored Apr 15, 2023
1 parent a20ffc1 commit b4fff41
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ KECCAK
Polkadot
RPC
SHA
UI
UI/S
URI
Wasm
Wasm32
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Upgraded `syn` to version `2` - [#1731](https://github.com/paritytech/ink/pull/1731)
- Added `default` attribute to constructors and messages - [#1703](https://github.com/paritytech/ink/pull/1724)

## Version 4.1.0

Expand Down
4 changes: 4 additions & 0 deletions crates/ink/codegen/src/generator/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ impl Metadata<'_> {
let selector_bytes = constructor.composed_selector().hex_lits();
let selector_id = constructor.composed_selector().into_be_u32();
let is_payable = constructor.is_payable();
let is_default = constructor.is_default();
let constructor = constructor.callable();
let ident = constructor.ident();
let args = constructor.inputs().map(Self::generate_dispatch_argument);
Expand All @@ -163,6 +164,7 @@ impl Metadata<'_> {
#( #args ),*
])
.payable(#is_payable)
.default(#is_default)
.returns(#ret_ty)
.docs([
#( #docs ),*
Expand Down Expand Up @@ -242,6 +244,7 @@ impl Metadata<'_> {
.filter_map(|attr| attr.extract_docs());
let selector_bytes = message.composed_selector().hex_lits();
let is_payable = message.is_payable();
let is_default = message.is_default();
let message = message.callable();
let mutates = message.receiver().is_ref_mut();
let ident = message.ident();
Expand All @@ -260,6 +263,7 @@ impl Metadata<'_> {
.returns(#ret_ty)
.mutates(#mutates)
.payable(#is_payable)
.default(#is_default)
.docs([
#( #docs ),*
])
Expand Down
25 changes: 25 additions & 0 deletions crates/ink/ir/src/ir/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ impl InkAttribute {
.any(|arg| matches!(arg.kind(), AttributeArg::Payable))
}

/// Returns `true` if the ink! attribute contains the `default` argument.
pub fn is_default(&self) -> bool {
self.args()
.any(|arg| matches!(arg.kind(), AttributeArg::Default))
}

/// Returns `true` if the ink! attribute contains the wildcard selector.
pub fn has_wildcard_selector(&self) -> bool {
self.args().any(|arg| {
Expand Down Expand Up @@ -351,6 +357,8 @@ pub enum AttributeArgKind {
Constructor,
/// `#[ink(payable)]`
Payable,
/// `#[ink(default)]`
Default,
/// `#[ink(selector = _)]`
/// `#[ink(selector = 0xDEADBEEF)]`
Selector,
Expand Down Expand Up @@ -403,6 +411,9 @@ pub enum AttributeArg {
/// Applied on ink! constructors or messages in order to specify that they
/// can receive funds from callers.
Payable,
/// Applied on ink! constructors or messages in order to indicate
/// they are default.
Default,
/// Can be either one of:
///
/// - `#[ink(selector = 0xDEADBEEF)]` Applied on ink! constructors or messages to
Expand Down Expand Up @@ -463,6 +474,7 @@ impl core::fmt::Display for AttributeArgKind {
}
Self::Implementation => write!(f, "impl"),
Self::HandleStatus => write!(f, "handle_status"),
Self::Default => write!(f, "default"),
}
}
}
Expand All @@ -483,6 +495,7 @@ impl AttributeArg {
Self::Namespace(_) => AttributeArgKind::Namespace,
Self::Implementation => AttributeArgKind::Implementation,
Self::HandleStatus(_) => AttributeArgKind::HandleStatus,
Self::Default => AttributeArgKind::Default,
}
}
}
Expand All @@ -506,6 +519,7 @@ impl core::fmt::Display for AttributeArg {
}
Self::Implementation => write!(f, "impl"),
Self::HandleStatus(value) => write!(f, "handle_status = {value:?}"),
Self::Default => write!(f, "default"),
}
}
}
Expand Down Expand Up @@ -959,6 +973,7 @@ impl Parse for AttributeFrag {
"anonymous" => Ok(AttributeArg::Anonymous),
"topic" => Ok(AttributeArg::Topic),
"payable" => Ok(AttributeArg::Payable),
"default" => Ok(AttributeArg::Default),
"impl" => Ok(AttributeArg::Implementation),
_ => match ident.to_string().as_str() {
"extension" => Err(format_err_spanned!(
Expand Down Expand Up @@ -1217,6 +1232,16 @@ mod tests {
);
}

#[test]
fn default_works() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(default)]
},
Ok(test::Attribute::Ink(vec![AttributeArg::Default])),
)
}

#[test]
fn namespace_works() {
assert_attribute_try_from(
Expand Down
11 changes: 11 additions & 0 deletions crates/ink/ir/src/ir/item_impl/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ where
<C as Callable>::is_payable(self.callable)
}

fn is_default(&self) -> bool {
<C as Callable>::is_default(self.callable)
}

fn has_wildcard_selector(&self) -> bool {
<C as Callable>::has_wildcard_selector(self.callable)
}
Expand Down Expand Up @@ -166,6 +170,13 @@ pub trait Callable {
/// Flagging as payable is done using the `#[ink(payable)]` attribute.
fn is_payable(&self) -> bool;

/// Returns `true` if the ink! callable is flagged as default.
///
/// # Note
///
/// Flagging as default is done using the `#[ink(default)]` attribute.
fn is_default(&self) -> bool;

/// Returns `true` if the ink! callable is flagged as a wildcard selector.
fn has_wildcard_selector(&self) -> bool;

Expand Down
37 changes: 37 additions & 0 deletions crates/ink/ir/src/ir/item_impl/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub struct Constructor {
pub(super) item: syn::ImplItemFn,
/// If the ink! constructor can receive funds.
is_payable: bool,
/// If the ink! constructor is default.
is_default: bool,
/// An optional user provided selector.
///
/// # Note
Expand Down Expand Up @@ -139,6 +141,7 @@ impl Constructor {
match arg.kind() {
ir::AttributeArg::Constructor
| ir::AttributeArg::Payable
| ir::AttributeArg::Default
| ir::AttributeArg::Selector(_) => Ok(()),
_ => Err(None),
}
Expand All @@ -156,10 +159,12 @@ impl TryFrom<syn::ImplItemFn> for Constructor {
Self::ensure_no_self_receiver(&method_item)?;
let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
let is_payable = ink_attrs.is_payable();
let is_default = ink_attrs.is_default();
let selector = ink_attrs.selector();
Ok(Constructor {
selector,
is_payable,
is_default,
item: syn::ImplItemFn {
attrs: other_attrs,
..method_item
Expand Down Expand Up @@ -195,6 +200,10 @@ impl Callable for Constructor {
self.is_payable
}

fn is_default(&self) -> bool {
self.is_default
}

fn visibility(&self) -> Visibility {
match &self.item.vis {
syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
Expand Down Expand Up @@ -336,6 +345,34 @@ mod tests {
}
}

#[test]
fn is_default_works() {
let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
// Not default.
(
false,
syn::parse_quote! {
#[ink(constructor)]
fn my_constructor() -> Self {}
},
),
// Default constructor.
(
true,
syn::parse_quote! {
#[ink(constructor, default)]
pub fn my_constructor() -> Self {}
},
),
];
for (expect_default, item_method) in test_inputs {
let is_default = <ir::Constructor as TryFrom<_>>::try_from(item_method)
.unwrap()
.is_default();
assert_eq!(is_default, expect_default);
}
}

#[test]
fn visibility_works() {
let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
Expand Down
37 changes: 37 additions & 0 deletions crates/ink/ir/src/ir/item_impl/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub struct Message {
pub(super) item: syn::ImplItemFn,
/// If the ink! message can receive funds.
is_payable: bool,
/// If the ink! message is default.
is_default: bool,
/// An optional user provided selector.
///
/// # Note
Expand Down Expand Up @@ -185,6 +187,7 @@ impl Message {
match arg.kind() {
ir::AttributeArg::Message
| ir::AttributeArg::Payable
| ir::AttributeArg::Default
| ir::AttributeArg::Selector(_) => Ok(()),
_ => Err(None),
}
Expand All @@ -202,9 +205,11 @@ impl TryFrom<syn::ImplItemFn> for Message {
Self::ensure_not_return_self(&method_item)?;
let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
let is_payable = ink_attrs.is_payable();
let is_default = ink_attrs.is_default();
let selector = ink_attrs.selector();
Ok(Self {
is_payable,
is_default,
selector,
item: syn::ImplItemFn {
attrs: other_attrs,
Expand Down Expand Up @@ -241,6 +246,10 @@ impl Callable for Message {
self.is_payable
}

fn is_default(&self) -> bool {
self.is_default
}

fn visibility(&self) -> Visibility {
match &self.item.vis {
syn::Visibility::Public(vis_public) => Visibility::Public(*vis_public),
Expand Down Expand Up @@ -466,6 +475,34 @@ mod tests {
}
}

#[test]
fn is_default_works() {
let test_inputs: Vec<(bool, syn::ImplItemFn)> = vec![
// Not default.
(
false,
syn::parse_quote! {
#[ink(message)]
fn my_message(&self) {}
},
),
// Default message.
(
true,
syn::parse_quote! {
#[ink(message, payable, default)]
pub fn my_message(&self) {}
},
),
];
for (expect_default, item_method) in test_inputs {
let is_default = <ir::Message as TryFrom<_>>::try_from(item_method)
.unwrap()
.is_default();
assert_eq!(is_default, expect_default);
}
}

#[test]
fn receiver_works() {
let test_inputs: Vec<(Receiver, syn::ImplItemFn)> = vec![
Expand Down
1 change: 1 addition & 0 deletions crates/ink/ir/src/ir/trait_def/item/trait_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ impl<'a> InkTraitMessage<'a> {
Err(Some(format_err!(arg.span(), "wildcard selectors are only supported for inherent ink! messages or constructors, not for traits."))),
ir::AttributeArg::Message
| ir::AttributeArg::Payable
| ir::AttributeArg::Default
| ir::AttributeArg::Selector(_) => Ok(()),
_ => Err(None),
}
Expand Down
Loading

0 comments on commit b4fff41

Please sign in to comment.