diff --git a/derive/Cargo.toml b/derive/Cargo.toml index f6084a36e..43f75a5eb 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -18,6 +18,6 @@ edition = "2021" proc-macro = true [dependencies] -syn = "1.0" +syn = { version = "2.0", default-features = false, features = ["printing", "parsing", "derive", "proc-macro"] } proc-macro2 = "1.0" quote = "1.0" diff --git a/derive/src/lib.rs b/derive/src/lib.rs index fbc3ef2e1..d19696aa4 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -2,8 +2,9 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::spanned::Spanned; use syn::{ - parse_macro_input, parse_quote, Attribute, Data, DeriveInput, Fields, GenericParam, Generics, - Ident, Index, Lit, Meta, MetaNameValue, NestedMeta, + parse::{Parse, ParseStream}, + parse_macro_input, parse_quote, Attribute, Data, DeriveInput, + Fields, GenericParam, Generics, Ident, Index, LitStr, Meta, Token }; @@ -78,38 +79,43 @@ struct Attributes { with: Option, } +struct WithIdent { + with: Option, +} +impl Parse for WithIdent { + fn parse(input: ParseStream) -> Result { + let mut result = WithIdent { with: None }; + let ident = input.parse::()?; + if ident != "with" { + return Err(syn::Error::new(ident.span(), "Expected identifier to be `with`")); + } + input.parse::()?; + let s = input.parse::()?; + result.with = Some(format_ident!("{}", s.value(), span = s.span())); + Ok(result) + } +} + impl Attributes { fn parse(attrs: &[Attribute]) -> Self { let mut out = Self::default(); - for attr in attrs.iter().filter(|a| a.path.is_ident("visit")) { - let meta = attr.parse_meta().expect("visit attribute"); - match meta { - Meta::List(l) => { - for nested in &l.nested { - match nested { - NestedMeta::Meta(Meta::NameValue(v)) => out.parse_name_value(v), - _ => panic!("Expected #[visit(key = \"value\")]"), + for attr in attrs { + if let Meta::List(ref metalist) = attr.meta { + if metalist.path.is_ident("visit") { + match syn::parse2::(metalist.tokens.clone()) { + Ok(with_ident) => { + out.with = with_ident.with; + } + Err(e) => { + panic!("{}", e); } } } - _ => panic!("Expected #[visit(...)]"), } } out } - /// Updates self with a name value attribute - fn parse_name_value(&mut self, v: &MetaNameValue) { - if v.path.is_ident("with") { - match &v.lit { - Lit::Str(s) => self.with = Some(format_ident!("{}", s.value(), span = s.span())), - _ => panic!("Expected a string value, got {}", v.lit.to_token_stream()), - } - return; - } - panic!("Unrecognised kv attribute {}", v.path.to_token_stream()) - } - /// Returns the pre and post visit token streams fn visit(&self, s: TokenStream) -> (Option, Option) { let pre_visit = self.with.as_ref().map(|m| {