diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 2814738a7..0ed9610f4 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -18,6 +18,6 @@ edition = "2021" proc-macro = true [dependencies] -syn = { version = "2.0", features = ["full", "visit", "visit-mut"] } +syn = { version = "2.0", features = ["parsing"] } proc-macro2 = "1.0" quote = "1.0" diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 339f5505d..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, Expr, ExprLit, ExprPath, - Fields, GenericParam, Generics, Ident, Index, Lit, Meta, MetaNameValue, + parse::{Parse, ParseStream}, + parse_macro_input, parse_quote, Attribute, Data, DeriveInput, + Fields, GenericParam, Generics, Ident, Index, LitStr, Meta, Token }; @@ -78,36 +79,41 @@ 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 { - if let Meta::NameValue(ref namevalue) = attr.meta { - if namevalue.path.is_ident("visit") { - out.parse_name_value(namevalue); - } - } - } - if out.with.is_none() { - panic!("Expected #[visit(...)]"); - } - out - } - - /// Updates self with a name value attribute - fn parse_name_value(&mut self, v: &MetaNameValue) { - if let Expr::Assign(ref assign) = v.value { - if let Expr::Path(ExprPath { ref path, .. }) = *assign.left { - if path.is_ident("with") { - match *assign.right { - Expr::Lit(ExprLit { lit: Lit::Str(ref s), .. }) => self.with = Some(format_ident!("{}", s.value(), span = s.span())), - _ => panic!("Expected a string value, got {}", v.value.to_token_stream()), + 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); + } } - return; } } } - panic!("Unrecognised kv attribute {}", v.to_token_stream()) + out } /// Returns the pre and post visit token streams