Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to syn v2 #1731

Merged
merged 42 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6e2f529
syn 2.0
ascjones Mar 20, 2023
a5ab273
TraitItemMethod -> TraitItemFn
ascjones Mar 20, 2023
bb7ef57
Token bang
ascjones Mar 20, 2023
4fb7de5
ImplItemMethod -> ImplItemFn
ascjones Mar 20, 2023
97732bc
Token![pub]
ascjones Mar 20, 2023
ce957fa
Remove ItemMacro2
ascjones Mar 20, 2023
f91f702
WIP fix Attribute Span
ascjones Mar 20, 2023
4126aa3
WIP updates
ascjones Mar 20, 2023
88834aa
WIP updates
ascjones Mar 20, 2023
6ab7117
Method to Fn
ascjones Mar 20, 2023
49c0049
Handle remaining attributes
ascjones Mar 20, 2023
604047e
Temp fix for extracting cfg attrs
ascjones Mar 20, 2023
d54370c
Fix some other errors
ascjones Mar 20, 2023
8b2349e
Add test for wildcard selector (doesn't work :()
ascjones Mar 21, 2023
5b15e77
Merge branch 'master' into aj/update-syn
ascjones Mar 21, 2023
b7d5d3f
WIP impl parse for AttributeFrag
ascjones Mar 22, 2023
e814c83
Merge branch 'master' into aj/update-syn
ascjones Mar 22, 2023
849064e
Fmt
ascjones Mar 22, 2023
cd51f8a
Import skeleton parse impl
ascjones Mar 22, 2023
a678649
WIP
ascjones Mar 22, 2023
95da414
Copy across parse impl
ascjones Mar 22, 2023
d8e59fa
Parse terminated
ascjones Mar 22, 2023
d702fa6
WIP fix attr parsing
ascjones Mar 23, 2023
8191cbb
WIP fix attr parsing
ascjones Mar 23, 2023
f12afa5
Remove attr property
ascjones Mar 24, 2023
a91459c
Merge branch 'master' into aj/update-syn
ascjones Mar 24, 2023
e1cadd3
Utilize attr_args meta for parsing
ascjones Mar 24, 2023
1cb115b
Format error messages
ascjones Mar 24, 2023
c57b023
Extract selector parsing to TryFrom
ascjones Mar 24, 2023
35515c0
Handle parsing wildcard selector
ascjones Mar 24, 2023
7b3b2cf
Factor out namesapce parsing
ascjones Mar 24, 2023
39b4eb0
Introduce helper methods
ascjones Mar 24, 2023
efcd942
suggestions
Mar 27, 2023
05533fa
fix parsing of cfg attrs
Mar 27, 2023
11284da
remove expand.rs
Mar 27, 2023
97e79d6
add error message
Mar 27, 2023
b9a4e0c
start working on more detailed errors
Mar 27, 2023
ecc2bc6
fix tests
Mar 27, 2023
431b5e0
rework docs parsing
Mar 27, 2023
06a3171
changelog entry
Mar 28, 2023
d82693d
some refactoring
Mar 28, 2023
e042ade
fmt
Mar 28, 2023
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- Upgraded `syn` to version `2` - [#1731](https://github.com/paritytech/ink/pull/1731)

## Version 4.1.0

### Added
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ derive_more = "0.99.17"
env_logger = "0.10.0"
log = "0.4.17"
serde_json = "1.0.89"
syn = "1"
syn = "2"
proc-macro2 = "1"
quote = "1"
which = "4.4.0"
2 changes: 1 addition & 1 deletion crates/ink/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ name = "ink_codegen"
ink_primitives = { version = "4.1.0", path = "../../primitives" }
ir = { version = "4.1.0", package = "ink_ir", path = "../ir", default-features = false }
quote = "1"
syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] }
syn = { version = "2.0", features = ["parsing", "full", "extra-traits"] }
proc-macro2 = "1.0"
derive_more = { version = "0.99", default-features = false, features = ["from"] }
itertools = "0.10"
Expand Down
2 changes: 1 addition & 1 deletion crates/ink/ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ name = "ink_ir"

[dependencies]
quote = "1"
syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"] }
syn = { version = "2.0", features = ["parsing", "full", "visit", "extra-traits"] }
proc-macro2 = "1.0"
itertools = { version = "0.10", default-features = false }
either = { version = "1.5", default-features = false }
Expand Down
112 changes: 2 additions & 110 deletions crates/ink/ir/src/ast/attr_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::{
Ident,
TokenStream as TokenStream2,
};
use quote::ToTokens;
use super::MetaNameValue;
use syn::{
ext::IdentExt as _,
parse::{
Parse,
ParseStream,
},
punctuated::Punctuated,
spanned::Spanned,
Token,
};

Expand All @@ -37,24 +31,6 @@ pub struct AttributeArgs {
args: Punctuated<MetaNameValue, Token![,]>,
}

/// 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.
#[derive(Debug, PartialEq, Eq)]
pub struct MetaNameValue {
pub name: syn::Path,
pub eq_token: syn::token::Eq,
pub value: PathOrLit,
}

/// Either a path or a literal.
#[derive(Debug, PartialEq, Eq)]
pub enum PathOrLit {
Path(syn::Path),
Lit(syn::Lit),
}

impl IntoIterator for AttributeArgs {
type Item = MetaNameValue;
type IntoIter = syn::punctuated::IntoIter<MetaNameValue>;
Expand All @@ -72,94 +48,10 @@ impl Parse for AttributeArgs {
}
}

impl Parse for MetaNameValue {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(Self::parse_meta_path)?;
Self::parse_meta_name_value_after_path(path, input)
}
}

impl ToTokens for PathOrLit {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Lit(lit) => lit.to_tokens(tokens),
Self::Path(path) => path.to_tokens(tokens),
}
}
}

impl ToTokens for MetaNameValue {
fn to_tokens(&self, tokens: &mut TokenStream2) {
self.name.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
self.value.to_tokens(tokens);
}
}

impl MetaNameValue {
/// 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
},
})
}

fn parse_meta_name_value_after_path(
name: syn::Path,
input: ParseStream,
) -> Result<MetaNameValue, syn::Error> {
let span = name.span();
Ok(MetaNameValue {
name,
eq_token: input.parse().map_err(|_error| {
format_err!(
span,
"ink! config options require an argument separated by '='",
)
})?,
value: input.parse()?,
})
}
}

impl Parse for PathOrLit {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
if input.fork().peek(syn::Lit) {
return input.parse::<syn::Lit>().map(PathOrLit::Lit)
}
if input.fork().peek(Ident::peek_any) || input.fork().peek(Token![::]) {
return input.parse::<syn::Path>().map(PathOrLit::Path)
}
Err(input.error("cannot parse into either literal or path"))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::ast::PathOrLit;
use quote::quote;

impl AttributeArgs {
Expand Down
211 changes: 211 additions & 0 deletions crates/ink/ir/src/ast/meta.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2018-2022 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::{
Ident,
TokenStream as TokenStream2,
};
use quote::ToTokens;
use syn::{
ext::IdentExt as _,
parse::{
Parse,
ParseStream,
},
punctuated::Punctuated,
spanned::Spanned,
LitInt,
Token,
};

/// 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.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Meta {
/// A path, like `message`.
Path(syn::Path),
/// A name-value pair, like `feature = "nightly"`.
NameValue(MetaNameValue),
}

impl Parse for Meta {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(parse_meta_path)?;
if input.peek(Token![=]) {
MetaNameValue::parse_meta_name_value_after_path(path, input)
.map(Meta::NameValue)
} else {
Ok(Meta::Path(path))
}
}
}

impl ToTokens for Meta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Path(path) => path.to_tokens(tokens),
Self::NameValue(name_value) => name_value.to_tokens(tokens),
}
}
}

/// 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.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MetaNameValue {
pub name: syn::Path,
pub eq_token: syn::token::Eq,
pub value: PathOrLit,
}

impl Parse for MetaNameValue {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(parse_meta_path)?;
Self::parse_meta_name_value_after_path(path, input)
}
}

impl ToTokens for MetaNameValue {
fn to_tokens(&self, tokens: &mut TokenStream2) {
self.name.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
self.value.to_tokens(tokens);
}
}

impl MetaNameValue {
fn parse_meta_name_value_after_path(
name: syn::Path,
input: ParseStream,
) -> Result<MetaNameValue, syn::Error> {
let span = name.span();
Ok(MetaNameValue {
name,
eq_token: input.parse().map_err(|_error| {
format_err!(
span,
"ink! config options require an argument separated by '='",
)
})?,
value: input.parse()?,
})
}
}

/// Either a path or a literal.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PathOrLit {
Path(syn::Path),
Lit(syn::Lit),
}

impl Parse for PathOrLit {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
if input.fork().peek(syn::Lit) {
return input.parse::<syn::Lit>().map(PathOrLit::Lit)
}
if input.fork().peek(Ident::peek_any) || input.fork().peek(Token![::]) {
return input.call(parse_meta_path).map(PathOrLit::Path)
}
Err(input.error("cannot parse into either literal or path"))
}
}

impl ToTokens for PathOrLit {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Lit(lit) => lit.to_tokens(tokens),
Self::Path(path) => path.to_tokens(tokens),
}
}
}

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

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

/// Returns the 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,
}
}
}

/// 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::*;
use crate::ast::PathOrLit;
use quote::quote;

#[test]
fn underscore_token_works() {
assert_eq!(
syn::parse2::<Meta>(quote! { selector = _ }).unwrap(),
Meta::NameValue(MetaNameValue {
name: syn::parse_quote! { selector },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(syn::Path::from(quote::format_ident!("_"))),
})
)
}
}
Loading