Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

## Changed
- Update ink! attribute argument syntax docs and simplify ink! attribute meta parsing implementation in ink_ir - [#1927](https://github.com/paritytech/ink/pull/1927)
- Make `set_code_hash` generic - [#1906](https://github.com/paritytech/ink/pull/1906)

## Version 5.0.0-alpha
Expand Down
78 changes: 39 additions & 39 deletions crates/ink/ir/src/ast/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use syn::{
Parse,
ParseStream,
},
punctuated::Punctuated,
spanned::Spanned,
LitInt,
Token,
Expand All @@ -32,7 +31,7 @@ use syn::{
/// Content of a compile-time structured attribute.
///
/// This is a subset of `syn::Meta` that allows the `value` of a name-value pair
/// to be a plain identifier or path.
/// to be a literal, path, underscore (`_`) or `@` symbol.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Meta {
/// A path, like `message`.
Expand All @@ -43,7 +42,15 @@ pub enum Meta {

impl Parse for Meta {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(parse_meta_path)?;
// Handles the `impl` argument.
if input.peek(Token![impl]) {
let ident = Ident::parse_any(input)?;
let path = syn::Path::from(ident);
return Ok(Self::Path(path))
}

// Handles all other arguments.
let path: syn::Path = input.parse()?;
if input.peek(Token![=]) {
MetaNameValue::parse_meta_name_value_after_path(path, input)
.map(Meta::NameValue)
Expand All @@ -65,7 +72,7 @@ impl ToTokens for Meta {
/// A name-value pair within an attribute, like `feature = "nightly"`.
///
/// The only difference from `syn::MetaNameValue` is that this additionally
/// allows the `value` to be a plain identifier or path.
/// allows the `value` to be an `@` symbol.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MetaNameValue {
pub name: syn::Path,
Expand All @@ -75,7 +82,7 @@ pub struct MetaNameValue {

impl Parse for MetaNameValue {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(parse_meta_path)?;
let path = input.parse()?;
Self::parse_meta_name_value_after_path(path, input)
}
}
Expand Down Expand Up @@ -120,13 +127,16 @@ impl Parse for MetaValue {
if input.peek(Token![_]) || input.peek(Token![@]) {
return input.parse::<Symbol>().map(MetaValue::Symbol)
}
if input.fork().peek(syn::Lit) {
if input.peek(syn::Lit) {
return input.parse::<syn::Lit>().map(MetaValue::Lit)
}
if input.fork().peek(Ident::peek_any) || input.fork().peek(Token![::]) {
return input.call(parse_meta_path).map(MetaValue::Path)
if input.peek(Ident::peek_any) || input.peek(Token![::]) {
return input.parse::<syn::Path>().map(MetaValue::Path)
}
Err(input.error("expected a literal, a path or a punct for a meta value"))
Err(input.error(
"expected a literal, a path, an underscore (`_`)\
or an `@` symbol for a meta value",
))
}
}

Expand Down Expand Up @@ -193,36 +203,6 @@ impl ToTokens for Symbol {
}
}

/// Like [`syn::Path::parse_mod_style`] but accepts keywords in the path.
///
/// # Note
///
/// This code was taken from the `syn` implementation for a very similar
/// syntactical pattern.
fn parse_meta_path(input: ParseStream) -> Result<syn::Path, syn::Error> {
Ok(syn::Path {
leading_colon: input.parse()?,
segments: {
let mut segments = Punctuated::new();
while input.peek(Ident::peek_any) {
let ident = Ident::parse_any(input)?;
segments.push_value(syn::PathSegment::from(ident));
if !input.peek(syn::Token![::]) {
break
}
let punct = input.parse()?;
segments.push_punct(punct);
}
if segments.is_empty() {
return Err(input.error("expected path"))
} else if segments.trailing_punct() {
return Err(input.error("expected path segment"))
}
segments
},
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -232,6 +212,14 @@ mod tests {
};
use quote::quote;

#[test]
fn impl_name_works() {
assert_eq!(
syn::parse2::<Meta>(quote! { impl }).unwrap(),
Meta::Path(syn::Path::from(quote::format_ident!("impl")))
)
}

#[test]
fn underscore_token_works() {
assert_eq!(
Expand All @@ -243,4 +231,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! { @ })),
})
)
}
}
39 changes: 32 additions & 7 deletions crates/ink/ir/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! Types and abstractions for ink! definitions that require custom syntax.
//! Types and abstractions for ink! definitions that require custom
//! [`syn::parse::Parse`] implementations.
//!
//! # Note
//!
//! In general we try not to require any sort of custom non-standard Rust
//! syntax.
//! In general we do not require any sort of custom non-standard Rust syntax.
//!
//! At the time of this writing we currently only use this for the argument
//! parsing of ink! configuration header `#[ink(env = my::env::Types, ...)]`
//! in order to be able to parse identifiers in `name = value` segments for
//! the `value` part.
//! However, because the Rust attribute grammar is very flexible,
//! custom [`syn::parse::Parse`] implementations are typically required
//! for parsing structured arguments from attribute syntax that doesn't
//! exactly match the
//! ["meta item" attribute syntax](https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax)
//! used by most
//! ["built-in" Rust attributes](https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index)
//! for which [`syn::Meta`] (and its related variant types) can be used directly.
//!
//! At the time of this writing, ink! attribute argument syntax deviates from
//! ["meta item" attribute syntax](https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax)
//! by:
//! - allowing the `impl` keyword in "meta item" paths
//! (i.e. `#[ink(impl)]` which is a deviation from the
//! [simple path](https://doc.rust-lang.org/reference/paths.html#simple-paths)
//! grammar).
//! - allowing the `@` symbol as a `value` in `name-value` pairs
//! (i.e. `#[ink(selector = @)]` which is a deviation from the
//! [expression](https://doc.rust-lang.org/reference/expressions.html)
//! grammar followed by the `value` part).
//!
//! NOTE: Underscore (`_`) values in `name-value` pairs
//! (e.g. `#[ink(selector = _)]`) are technically allowed by
//! the "meta item" attribute syntax as they can be interpreted as
//! [underscore expressions](https://doc.rust-lang.org/reference/expressions/underscore-expr.html)
//! (same for path values - e.g `#[ink(env = my::env::Types)]` -
//! which are valid
//! [path expressions](https://doc.rust-lang.org/reference/expressions/path-expr.html)
//! in "meta item" attribute syntax).

mod attr_args;
mod meta;
Expand Down