Skip to content

Commit

Permalink
Use name-only syntax for anonymous ink! event item configuration ar…
Browse files Browse the repository at this point in the history
…gument

Required updating `ink_ir::ast::AttributeArgs` to support name-only meta items e.g. `#[ink::event(anonymous)]`
  • Loading branch information
davidsemakula committed Mar 7, 2024
1 parent f55936e commit d3f5214
Show file tree
Hide file tree
Showing 18 changed files with 202 additions and 83 deletions.
69 changes: 41 additions & 28 deletions crates/ink/ir/src/ast/attr_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use super::MetaNameValue;
use super::Meta;
use syn::{
parse::{
Parse,
Expand All @@ -28,7 +28,7 @@ use syn::{
/// in `#[ink::contract(env = ::my::env::Environment)]`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AttributeArgs {
args: Punctuated<MetaNameValue, Token![,]>,
args: Punctuated<Meta, Token![,]>,
}

impl quote::ToTokens for AttributeArgs {
Expand All @@ -38,8 +38,8 @@ impl quote::ToTokens for AttributeArgs {
}

impl IntoIterator for AttributeArgs {
type Item = MetaNameValue;
type IntoIter = syn::punctuated::IntoIter<MetaNameValue>;
type Item = Meta;
type IntoIter = syn::punctuated::IntoIter<Meta>;

fn into_iter(self) -> Self::IntoIter {
self.args.into_iter()
Expand All @@ -57,14 +57,17 @@ impl Parse for AttributeArgs {
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::MetaValue;
use crate::ast::{
MetaNameValue,
MetaValue,
};
use quote::quote;

impl AttributeArgs {
/// Creates a new attribute argument list from the given arguments.
pub fn new<I>(args: I) -> Self
where
I: IntoIterator<Item = MetaNameValue>,
I: IntoIterator<Item = Meta>,
{
Self {
args: args.into_iter().collect(),
Expand All @@ -80,51 +83,59 @@ mod tests {
)
}

#[test]
fn flag_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { flag }).unwrap(),
AttributeArgs::new(vec![Meta::Path(syn::parse_quote! { flag })])
)
}

#[test]
fn literal_bool_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = true }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { true }),
}])
})])
)
}

#[test]
fn literal_str_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = "string literal" }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
}])
})])
)
}

#[test]
fn ident_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = MyIdentifier }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Path(syn::parse_quote! { MyIdentifier }),
}])
})])
)
}

#[test]
fn root_path_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = ::this::is::my::Path }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Path(syn::parse_quote! { ::this::is::my::Path }),
}])
})])
)
}

Expand All @@ -133,24 +144,24 @@ mod tests {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = this::is::my::relative::Path })
.unwrap(),
AttributeArgs::new(vec![MetaNameValue {
AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Path(
syn::parse_quote! { this::is::my::relative::Path }
),
}])
})])
)
}

#[test]
fn trailing_comma_works() {
let mut expected_args = Punctuated::new();
expected_args.push_value(MetaNameValue {
expected_args.push_value(Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: MetaValue::Path(syn::parse_quote! { value }),
});
}));
expected_args.push_punct(<Token![,]>::default());
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = value, }).unwrap(),
Expand All @@ -164,6 +175,7 @@ mod tests {
fn many_mixed_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! {
flag,
name1 = ::root::Path,
name2 = false,
name3 = "string literal",
Expand All @@ -172,31 +184,32 @@ mod tests {
})
.unwrap(),
AttributeArgs::new(vec![
MetaNameValue {
Meta::Path(syn::parse_quote! { flag }),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name1 },
eq_token: syn::parse_quote! { = },
value: MetaValue::Path(syn::parse_quote! { ::root::Path }),
},
MetaNameValue {
}),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name2 },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { false }),
},
MetaNameValue {
}),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name3 },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
},
MetaNameValue {
}),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name4 },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { 42 }),
},
MetaNameValue {
}),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { name5 },
eq_token: syn::parse_quote! { = },
value: MetaValue::Lit(syn::parse_quote! { 7.7 }),
},
}),
])
)
}
Expand Down
66 changes: 65 additions & 1 deletion crates/ink/ir/src/ast/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use syn::{
},
punctuated::Punctuated,
spanned::Spanned,
LitBool,
LitInt,
LitStr,
Token,
};

Expand Down Expand Up @@ -62,6 +64,32 @@ impl ToTokens for Meta {
}
}

impl Meta {
/// Returns the meta-item name.
pub fn name(&self) -> &syn::Path {
match self {
Meta::Path(path) => path,
Meta::NameValue(name_value) => &name_value.name,
}
}

/// Returns the meta-item value (if any).
pub fn value(&self) -> Option<&MetaValue> {
match self {
Meta::Path(_) => None,
Meta::NameValue(name_value) => Some(&name_value.value),
}
}

/// Returns the `NameValue` variant (if any).
pub fn name_value(&self) -> Option<&MetaNameValue> {
match self {
Meta::NameValue(name_value) => Some(name_value),
Meta::Path(_) => None,
}
}
}

/// A name-value pair within an attribute, like `feature = "nightly"`.
///
/// The only difference from `syn::MetaNameValue` is that this additionally
Expand Down Expand Up @@ -157,13 +185,37 @@ impl MetaValue {
}
}

/// Returns the the literal if it is an integer literal.
/// Returns the literal if it is an integer literal.
pub fn as_lit_int(&self) -> Option<&LitInt> {
match self {
Self::Lit(syn::Lit::Int(lit_int)) => Some(lit_int),
_ => None,
}
}

/// Returns the literal if it is a boolean literal.
pub fn as_lit_bool(&self) -> Option<&LitBool> {
match self {
Self::Lit(syn::Lit::Bool(lit_bool)) => Some(lit_bool),
_ => None,
}
}

/// Returns the literal if it is a string literal.
pub fn as_lit_string(&self) -> Option<&LitStr> {
match self {
Self::Lit(syn::Lit::Str(lit_str)) => Some(lit_str),
_ => None,
}
}

/// Returns the path (if the value is a path).
pub fn as_path(&self) -> Option<&syn::Path> {
match self {
Self::Path(path) => Some(path),
_ => None,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -243,4 +295,16 @@ mod tests {
})
)
}

#[test]
fn at_token_works() {
assert_eq!(
syn::parse2::<Meta>(quote! { selector = @ }).unwrap(),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { selector },
eq_token: syn::parse_quote! { = },
value: MetaValue::Symbol(Symbol::AtSign(syn::parse_quote! { @ })),
})
)
}
}
15 changes: 15 additions & 0 deletions crates/ink/ir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ macro_rules! format_err_spanned {
}
}

/// Creates a [`syn::Error`] with the format message and infers the
/// [`Span`](`proc_macro2::Span`) using the [`ToTokens`](`quote::ToTokens`) implementation
/// for the [`MetaValue`][crate::ast::MetaValue] (if possible).
///
/// See [`format_err_spanned`] for more details.
macro_rules! format_err_spanned_value {
($arg:expr, $($msg:tt)*) => {
if let Some(value) = $arg.value() {
format_err_spanned!(value, $($msg)*)
} else {
format_err_spanned!($arg, $($msg)*)
}
};
}

/// Creates a [`syn::Error`] with the format message and infers the
/// [`Span`](`proc_macro2::Span`) using [`Spanned`](`syn::spanned::Spanned`).
///
Expand Down
18 changes: 9 additions & 9 deletions crates/ink/ir/src/ir/chain_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ impl TryFrom<ast::AttributeArgs> for Config {
let mut ext_id: Option<ExtensionId> = None;

for arg in args.clone().into_iter() {
if arg.name.is_ident("extension") {
if arg.name().is_ident("extension") {
if ext_id.is_some() {
return Err(format_err_spanned!(
arg.value,
return Err(format_err_spanned_value!(
arg,
"encountered duplicate ink! contract `extension` configuration argument",
))
));
}

if let Some(lit_int) = arg.value.as_lit_int() {
if let Some(ast::MetaValue::Lit(syn::Lit::Int(lit_int))) = arg.value() {
let id = lit_int.base10_parse::<u16>()
.map_err(|error| {
format_err_spanned!(
Expand All @@ -98,16 +98,16 @@ impl TryFrom<ast::AttributeArgs> for Config {
})?;
ext_id = Some(ExtensionId::from_u16(id));
} else {
return Err(format_err_spanned!(
arg.value,
return Err(format_err_spanned_value!(
arg,
"expected `u16` integer type for `N` in `extension = N`",
))
));
}
} else {
return Err(format_err_spanned!(
arg,
"encountered unknown or unsupported chain extension configuration argument",
))
));
}
}

Expand Down
Loading

0 comments on commit d3f5214

Please sign in to comment.