update to syn 2

This mostly affected attribute parsing (due to the syn::Meta changes).
Also creating `DelimSpan`s for custom-built `syn::Attribute`s is a
bit... ugly.
Upshot: turns out we can drop some helpers in util.rs with the new
`syn::Meta` changes.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2023-09-28 13:05:29 +02:00
parent eb1abe45b6
commit b232b580a0
7 changed files with 171 additions and 210 deletions

View File

@ -75,7 +75,7 @@ regex = "1.5"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_plain = "1.0" serde_plain = "1.0"
syn = { version = "1.0", features = [ "full", "visit-mut" ] } syn = { version = "2", features = [ "full", "visit-mut" ] }
tar = "0.4" tar = "0.4"
tokio = "1.6" tokio = "1.6"
tokio-openssl = "0.6.1" tokio-openssl = "0.6.1"

View File

@ -1,8 +1,7 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::ToTokens; use syn::meta::ParseNestedMeta;
use syn::{Meta, NestedMeta};
use crate::util::{self, default_false, parse_str_value_to_option, set_bool}; use crate::util;
#[derive(Default)] #[derive(Default)]
pub struct UpdaterFieldAttributes { pub struct UpdaterFieldAttributes {
@ -20,46 +19,42 @@ impl UpdaterFieldAttributes {
pub fn from_attributes(input: &mut Vec<syn::Attribute>) -> Self { pub fn from_attributes(input: &mut Vec<syn::Attribute>) -> Self {
let mut this = Self::default(); let mut this = Self::default();
util::extract_attributes(input, "updater", |attr, meta| this.parse(attr, meta)); for attr in std::mem::take(input) {
if attr.style != syn::AttrStyle::Outer || !attr.path().is_ident("updater") {
input.push(attr);
continue;
}
match attr.parse_nested_meta(|meta| this.parse(meta)) {
Ok(()) => (),
Err(err) => crate::add_error(err),
}
}
this this
} }
fn parse(&mut self, attr: &syn::Attribute, input: NestedMeta) -> Result<(), syn::Error> { fn parse(&mut self, meta: ParseNestedMeta<'_>) -> Result<(), syn::Error> {
match input { let path = &meta.path;
NestedMeta::Lit(lit) => bail!(lit => "unexpected literal"),
NestedMeta::Meta(meta) => self.parse_meta(attr, meta),
}
}
fn parse_meta(&mut self, attr: &syn::Attribute, meta: Meta) -> Result<(), syn::Error> { if path.is_ident("skip") {
match meta { if !meta.input.is_empty() {
Meta::Path(ref path) if path.is_ident("skip") => { return Err(meta.error("'skip' attribute does not take any data"));
set_bool(&mut self.skip, path, true);
} }
Meta::NameValue(ref nv) if nv.path.is_ident("type") => { util::set_bool(&mut self.skip, path, true);
parse_str_value_to_option(&mut self.ty, nv) } else if path.is_ident("type") {
} util::parse_str_value_to_option(&mut self.ty, path, meta.value()?);
Meta::NameValue(m) => bail!(&m => "invalid updater attribute: {:?}", m.path), } else if path.is_ident("serde") {
Meta::List(m) if m.path.is_ident("serde") => { let content: TokenStream = meta.input.parse()?;
let mut tokens = TokenStream::new(); self.serde.push(syn::parse_quote! { # [ #path #content ] });
m.paren_token } else {
.surround(&mut tokens, |tokens| m.nested.to_tokens(tokens)); return Err(meta.error(format!("invalid updater attribute: {path:?}")));
self.serde.push(syn::Attribute {
path: m.path,
tokens,
..attr.clone()
});
}
Meta::List(m) => bail!(&m => "invalid updater attribute: {:?}", m.path),
Meta::Path(m) => bail!(&m => "invalid updater attribute: {:?}", m),
} }
Ok(()) Ok(())
} }
pub fn skip(&self) -> bool { pub fn skip(&self) -> bool {
default_false(self.skip.as_ref()) util::default_false(self.skip.as_ref())
} }
pub fn ty(&self) -> Option<&syn::TypePath> { pub fn ty(&self) -> Option<&syn::TypePath> {
@ -68,7 +63,7 @@ impl UpdaterFieldAttributes {
pub fn replace_serde_attributes(&self, attrs: &mut Vec<syn::Attribute>) { pub fn replace_serde_attributes(&self, attrs: &mut Vec<syn::Attribute>) {
if !self.serde.is_empty() { if !self.serde.is_empty() {
attrs.retain(|attr| !attr.path.is_ident("serde")); attrs.retain(|attr| !attr.path().is_ident("serde"));
attrs.extend(self.serde.iter().cloned()) attrs.extend(self.serde.iter().cloned())
} }
} }

View File

@ -69,7 +69,7 @@ pub fn handle_enum(
}; };
if derives_default { if derives_default {
if let Some(attr) = variant.attrs.iter().find(|a| a.path.is_ident("default")) { if let Some(attr) = variant.attrs.iter().find(|a| a.path().is_ident("default")) {
if let Some(default_value) = &default_value { if let Some(default_value) = &default_value {
error!(attr => "multiple default values defined"); error!(attr => "multiple default values defined");
error!(default_value => "default previously defined here"); error!(default_value => "default previously defined here");

View File

@ -114,7 +114,7 @@ impl TryFrom<JSONObject> for Schema {
); );
Ok(Self { Ok(Self {
span: obj.brace_token.span, span: obj.span(),
description, description,
item: SchemaItem::try_extract_from(&mut obj)?, item: SchemaItem::try_extract_from(&mut obj)?,
properties: obj properties: obj

View File

@ -32,7 +32,7 @@ pub fn handle_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<Token
handle_newtype_struct(attribs, stru) handle_newtype_struct(attribs, stru)
} }
syn::Fields::Unnamed(fields) => bail!( syn::Fields::Unnamed(fields) => bail!(
fields.paren_token.span, fields.paren_token.span.open(),
"api macro does not support tuple structs" "api macro does not support tuple structs"
), ),
syn::Fields::Named(_) => handle_regular_struct(attribs, stru), syn::Fields::Named(_) => handle_regular_struct(attribs, stru),

View File

@ -5,7 +5,8 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use crate::util::AttrArgs; use syn::punctuated::Punctuated;
use syn::Token;
/// Serde name types. /// Serde name types.
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
@ -134,20 +135,29 @@ impl TryFrom<&[syn::Attribute]> for ContainerAttrib {
let mut this: Self = Default::default(); let mut this: Self = Default::default();
for attrib in attributes { for attrib in attributes {
if !attrib.path.is_ident("serde") { let list = match &attrib.meta {
syn::Meta::List(list) if list.path.is_ident("serde") => list,
_ => continue,
};
let args =
list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated)?;
for arg in args {
if let syn::Meta::NameValue(var) = arg {
if !var.path.is_ident("rename_all") {
continue; continue;
} }
match &var.value {
let args: AttrArgs = syn::parse2(attrib.tokens.clone())?; syn::Expr::Lit(lit) => {
for arg in args.args { let rename_all = RenameAll::try_from(&lit.lit)?;
if let syn::NestedMeta::Meta(syn::Meta::NameValue(var)) = arg {
if var.path.is_ident("rename_all") {
let rename_all = RenameAll::try_from(&var.lit)?;
if this.rename_all.is_some() && this.rename_all != Some(rename_all) { if this.rename_all.is_some() && this.rename_all != Some(rename_all) {
error!(var.lit => "multiple conflicting 'rename_all' attributes"); error!(var.value => "multiple conflicting 'rename_all' attributes");
} }
this.rename_all = Some(rename_all); this.rename_all = Some(rename_all);
} }
_ => error!(var.value => "invalid 'rename_all' value type"),
}
} }
} }
} }
@ -165,31 +175,32 @@ pub struct SerdeAttrib {
impl SerdeAttrib { impl SerdeAttrib {
pub fn parse_attribute(&mut self, attrib: &syn::Attribute) -> Result<(), syn::Error> { pub fn parse_attribute(&mut self, attrib: &syn::Attribute) -> Result<(), syn::Error> {
use syn::{Meta, NestedMeta}; let list = match &attrib.meta {
syn::Meta::List(list) if list.path.is_ident("serde") => list,
_ => return Ok(()),
};
if !attrib.path.is_ident("serde") { let args = list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated)?;
return Ok(());
}
let args: AttrArgs = syn::parse2(attrib.tokens.clone())?; for arg in args {
for arg in args.args { let path = arg.path();
match arg { if path.is_ident("rename") {
NestedMeta::Meta(Meta::NameValue(var)) if var.path.is_ident("rename") => { match &arg.require_name_value()?.value {
match var.lit { syn::Expr::Lit(syn::ExprLit {
syn::Lit::Str(rename) => { lit: syn::Lit::Str(rename),
if self.rename.is_some() && self.rename.as_ref() != Some(&rename) { ..
}) => {
if self.rename.is_some() && self.rename.as_ref() != Some(rename) {
error!(&rename => "multiple conflicting 'rename' attributes"); error!(&rename => "multiple conflicting 'rename' attributes");
} }
self.rename = Some(rename); self.rename = Some(rename.clone());
} }
_ => error!(var.lit => "'rename' value must be a string literal"), value => error!(value => "'rename' value must be a string literal"),
} }
} } else if path.is_ident("flatten") {
NestedMeta::Meta(Meta::Path(path)) if path.is_ident("flatten") => { arg.require_path_only()?;
self.flatten = true; self.flatten = true;
} }
_ => continue,
}
} }
Ok(()) Ok(())

View File

@ -183,7 +183,7 @@ impl JSONValue {
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match self { match self {
JSONValue::Object(obj) => obj.brace_token.span, JSONValue::Object(obj) => obj.span(),
JSONValue::Expr(expr) => expr.span(), JSONValue::Expr(expr) => expr.span(),
} }
} }
@ -194,7 +194,7 @@ impl TryFrom<JSONValue> for syn::Expr {
type Error = syn::Error; type Error = syn::Error;
fn try_from(value: JSONValue) -> Result<Self, syn::Error> { fn try_from(value: JSONValue) -> Result<Self, syn::Error> {
match value { match value {
JSONValue::Object(s) => bail!(s.brace_token.span, "unexpected object"), JSONValue::Object(s) => bail!(s.span(), "unexpected object"),
JSONValue::Expr(e) => Ok(e), JSONValue::Expr(e) => Ok(e),
} }
} }
@ -298,7 +298,7 @@ impl Parse for JSONValue {
/// The "core" of our schema is a json object. /// The "core" of our schema is a json object.
pub struct JSONObject { pub struct JSONObject {
pub brace_token: syn::token::Brace, pub brace_token: Option<syn::token::Brace>,
pub elements: HashMap<FieldName, JSONValue>, pub elements: HashMap<FieldName, JSONValue>,
} }
@ -308,8 +308,7 @@ impl JSONObject {
} }
fn parse_elements(input: ParseStream) -> syn::Result<HashMap<FieldName, JSONValue>> { fn parse_elements(input: ParseStream) -> syn::Result<HashMap<FieldName, JSONValue>> {
let map_elems: Punctuated<JSONMapEntry, Token![,]> = let map_elems = input.parse_terminated(JSONMapEntry::parse, Token![,])?;
input.parse_terminated(JSONMapEntry::parse)?;
let mut elems = HashMap::with_capacity(map_elems.len()); let mut elems = HashMap::with_capacity(map_elems.len());
for c in map_elems { for c in map_elems {
if elems.insert(c.key.clone(), c.value).is_some() { if elems.insert(c.key.clone(), c.value).is_some() {
@ -321,9 +320,7 @@ impl JSONObject {
pub fn parse_inner(input: ParseStream) -> syn::Result<Self> { pub fn parse_inner(input: ParseStream) -> syn::Result<Self> {
Ok(Self { Ok(Self {
brace_token: syn::token::Brace { brace_token: None,
span: Span::call_site(),
},
elements: Self::parse_elements(input)?, elements: Self::parse_elements(input)?,
}) })
} }
@ -333,7 +330,7 @@ impl Parse for JSONObject {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let content; let content;
Ok(Self { Ok(Self {
brace_token: syn::braced!(content in input), brace_token: Some(syn::braced!(content in input)),
elements: Self::parse_elements(&content)?, elements: Self::parse_elements(&content)?,
}) })
} }
@ -355,7 +352,10 @@ impl std::ops::DerefMut for JSONObject {
impl JSONObject { impl JSONObject {
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
self.brace_token.span match &self.brace_token {
Some(brace) => brace.span.join(),
None => Span::call_site(),
}
} }
pub fn remove_required_element(&mut self, name: &str) -> Result<JSONValue, syn::Error> { pub fn remove_required_element(&mut self, name: &str) -> Result<JSONValue, syn::Error> {
@ -415,12 +415,20 @@ pub fn get_doc_comments(attributes: &[syn::Attribute]) -> Result<(String, Span),
continue; continue;
} }
if attr.path.is_ident("doc") { let nv = match &attr.meta {
let doc: BareAssignment<syn::LitStr> = syn::parse2(attr.tokens.clone())?; syn::Meta::NameValue(nv) if nv.path.is_ident("doc") => &nv.value,
_ => continue,
};
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(doc),
..
}) = nv
{
if !doc_comment.is_empty() { if !doc_comment.is_empty() {
doc_comment.push('\n'); doc_comment.push('\n');
} }
doc_comment.push_str(doc.content.value().trim()); doc_comment.push_str(doc.value().trim());
} }
} }
@ -554,7 +562,7 @@ pub fn make_ident_path(ident: Ident) -> syn::Path {
pub fn make_path(span: Span, leading_colon: bool, path: &[&str]) -> syn::Path { pub fn make_path(span: Span, leading_colon: bool, path: &[&str]) -> syn::Path {
syn::Path { syn::Path {
leading_colon: if leading_colon { leading_colon: if leading_colon {
Some(syn::token::Colon2 { Some(syn::token::PathSep {
spans: [span, span], spans: [span, span],
}) })
} else { } else {
@ -570,30 +578,6 @@ pub fn make_path(span: Span, leading_colon: bool, path: &[&str]) -> syn::Path {
} }
} }
/// Helper to do basically what `parse_macro_input!` does, except the macro expects a
/// `TokenStream_1`, and we always have a `TokenStream` from `proc_macro2`.
pub struct AttrArgs {
pub paren_token: syn::token::Paren,
pub args: Punctuated<syn::NestedMeta, Token![,]>,
}
impl Parse for AttrArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Self {
paren_token: syn::parenthesized!(content in input),
args: Punctuated::parse_terminated(&content)?,
})
}
}
impl ToTokens for AttrArgs {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.paren_token
.surround(tokens, |inner| self.args.to_tokens(inner));
}
}
/// Join an iterator over `Display` values. /// Join an iterator over `Display` values.
pub fn join<T>(separator: &str, iter: impl Iterator<Item = T>) -> String pub fn join<T>(separator: &str, iter: impl Iterator<Item = T>) -> String
where where
@ -701,7 +685,7 @@ pub fn derives_trait(attributes: &[syn::Attribute], ident: &str) -> bool {
/// Iterator over the types found in `#[derive(...)]` attributes. /// Iterator over the types found in `#[derive(...)]` attributes.
pub struct DerivedItems<'a> { pub struct DerivedItems<'a> {
current: Option<<Punctuated<syn::NestedMeta, Token![,]> as IntoIterator>::IntoIter>, current: Option<<Punctuated<syn::Meta, Token![,]> as IntoIterator>::IntoIter>,
attributes: std::slice::Iter<'a, syn::Attribute>, attributes: std::slice::Iter<'a, syn::Attribute>,
} }
@ -713,7 +697,7 @@ impl<'a> Iterator for DerivedItems<'a> {
if let Some(current) = &mut self.current { if let Some(current) = &mut self.current {
loop { loop {
match current.next() { match current.next() {
Some(syn::NestedMeta::Meta(syn::Meta::Path(path))) => return Some(path), Some(syn::Meta::Path(path)) => return Some(path),
Some(_) => continue, Some(_) => continue,
None => { None => {
self.current = None; self.current = None;
@ -728,15 +712,16 @@ impl<'a> Iterator for DerivedItems<'a> {
continue; continue;
} }
match attr.parse_meta() { match &attr.meta {
Ok(syn::Meta::List(list)) if list.path.is_ident("derive") => { syn::Meta::List(list) if list.path.is_ident("derive") => {
self.current = Some(list.nested.into_iter()); if let Ok(items) =
list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated)
{
self.current = Some(items.into_iter());
}
continue; continue;
} }
// ignore anything that isn't a `derive(...)` attribute _ => continue,
Ok(_) => continue,
// ignore parse errors
Err(_) => continue,
} }
} }
} }
@ -756,12 +741,16 @@ where
continue; continue;
} }
if !attr.path.is_ident("derive") { let list = match &mut attr.meta {
syn::Meta::List(list) if list.path.is_ident("derive") => list,
_ => {
attributes.push(attr); attributes.push(attr);
continue; continue;
} }
};
let mut args: AttrArgs = match syn::parse2(attr.tokens.clone()) { let mut args =
match list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
Ok(args) => args, Ok(args) => args,
Err(_) => { Err(_) => {
// if we can't parse it, we don't care // if we can't parse it, we don't care
@ -770,88 +759,53 @@ where
} }
}; };
for arg in std::mem::take(&mut args.args).into_pairs() { for arg in std::mem::take(&mut args).into_pairs() {
match arg { match arg {
Pair::Punctuated(item, punct) => { Pair::Punctuated(item, punct) => {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = &item { if let syn::Meta::Path(path) = &item {
if !func(path) { if !func(path) {
continue; continue;
} }
} }
args.args.push_value(item); args.push_value(item);
args.args.push_punct(punct); args.push_punct(punct);
} }
Pair::End(item) => { Pair::End(item) => {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = &item { if let syn::Meta::Path(path) = &item {
if !func(path) { if !func(path) {
continue; continue;
} }
} }
args.args.push_value(item); args.push_value(item);
} }
} }
} }
if !args.args.is_empty() { if !args.is_empty() {
attr.tokens = args.into_token_stream(); list.tokens = args.into_token_stream();
attributes.push(attr); attributes.push(attr);
} }
} }
} }
pub fn make_attribute(span: Span, path: syn::Path, tokens: TokenStream) -> syn::Attribute {
syn::Attribute {
pound_token: syn::token::Pound { spans: [span] },
style: syn::AttrStyle::Outer,
bracket_token: syn::token::Bracket { span },
path,
tokens,
}
}
pub fn make_derive_attribute(span: Span, content: TokenStream) -> syn::Attribute { pub fn make_derive_attribute(span: Span, content: TokenStream) -> syn::Attribute {
make_attribute( // FIXME: syn2 wtf come on...
span, let bracket_span =
make_ident_path(Ident::new("derive", span)), proc_macro2::Group::new(proc_macro2::Delimiter::Bracket, TokenStream::new()).delim_span();
quote::quote! { (#content) }, let paren_span =
) proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, TokenStream::new())
} .delim_span();
/// Extract (remove) an attribute from a list and run a callback on its parameters. syn::Attribute {
pub fn extract_attributes( pound_token: syn::token::Pound { spans: [span] },
attributes: &mut Vec<syn::Attribute>, style: syn::AttrStyle::Outer,
attr_name: &str, bracket_token: syn::token::Bracket { span: bracket_span },
mut func_matching: impl FnMut(&syn::Attribute, syn::NestedMeta) -> Result<(), syn::Error>, //meta: syn::Meta::List(syn::parse_quote_spanned!(span => derive ( #content )).unwrap()),
) { meta: syn::Meta::List(syn::MetaList {
for attr in std::mem::take(attributes) { path: make_ident_path(Ident::new("derive", span)),
if attr.style != syn::AttrStyle::Outer { delimiter: syn::MacroDelimiter::Paren(syn::token::Paren { span: paren_span }),
attributes.push(attr); tokens: content,
continue; }),
}
let meta = match attr.parse_meta() {
Ok(meta) => meta,
Err(err) => {
crate::add_error(err);
attributes.push(attr);
continue;
}
};
let list = match meta {
syn::Meta::List(list) if list.path.is_ident(attr_name) => list,
_ => {
attributes.push(attr);
continue;
}
};
for entry in list.nested {
match func_matching(&attr, entry) {
Ok(()) => (),
Err(err) => crate::add_error(err),
}
}
} }
} }
@ -906,14 +860,15 @@ pub fn respan(mut token: TokenTree, span: Span) -> TokenTree {
/// Parse a string attribute into a value, producing a duplication error if it has already been /// Parse a string attribute into a value, producing a duplication error if it has already been
/// set. /// set.
pub fn parse_str_value_to_option<T: Parse>(target: &mut Option<T>, nv: &syn::MetaNameValue) { pub fn parse_str_value_to_option<T: Parse>(
duplicate(&*target, &nv.path); target: &mut Option<T>,
match &nv.lit { path: &syn::Path,
syn::Lit::Str(s) => match parse_lit_str(s) { nv: syn::parse::ParseStream<'_>,
) {
duplicate(&*target, &path);
match nv.parse().and_then(|lit| parse_lit_str(&lit)) {
Ok(value) => *target = Some(value), Ok(value) => *target = Some(value),
Err(err) => crate::add_error(err), Err(err) => crate::add_error(err),
},
other => error!(other => "bad value for '{:?}' attribute", nv.path),
} }
} }