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_json = "1.0"
serde_plain = "1.0"
syn = { version = "1.0", features = [ "full", "visit-mut" ] }
syn = { version = "2", features = [ "full", "visit-mut" ] }
tar = "0.4"
tokio = "1.6"
tokio-openssl = "0.6.1"

View File

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

View File

@ -69,7 +69,7 @@ pub fn handle_enum(
};
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 {
error!(attr => "multiple default values defined");
error!(default_value => "default previously defined here");

View File

@ -114,7 +114,7 @@ impl TryFrom<JSONObject> for Schema {
);
Ok(Self {
span: obj.brace_token.span,
span: obj.span(),
description,
item: SchemaItem::try_extract_from(&mut 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)
}
syn::Fields::Unnamed(fields) => bail!(
fields.paren_token.span,
fields.paren_token.span.open(),
"api macro does not support tuple structs"
),
syn::Fields::Named(_) => handle_regular_struct(attribs, stru),

View File

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

View File

@ -183,7 +183,7 @@ impl JSONValue {
pub fn span(&self) -> Span {
match self {
JSONValue::Object(obj) => obj.brace_token.span,
JSONValue::Object(obj) => obj.span(),
JSONValue::Expr(expr) => expr.span(),
}
}
@ -194,7 +194,7 @@ impl TryFrom<JSONValue> for syn::Expr {
type Error = syn::Error;
fn try_from(value: JSONValue) -> Result<Self, syn::Error> {
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),
}
}
@ -298,7 +298,7 @@ impl Parse for JSONValue {
/// The "core" of our schema is a json object.
pub struct JSONObject {
pub brace_token: syn::token::Brace,
pub brace_token: Option<syn::token::Brace>,
pub elements: HashMap<FieldName, JSONValue>,
}
@ -308,8 +308,7 @@ impl JSONObject {
}
fn parse_elements(input: ParseStream) -> syn::Result<HashMap<FieldName, JSONValue>> {
let map_elems: Punctuated<JSONMapEntry, Token![,]> =
input.parse_terminated(JSONMapEntry::parse)?;
let map_elems = input.parse_terminated(JSONMapEntry::parse, Token![,])?;
let mut elems = HashMap::with_capacity(map_elems.len());
for c in map_elems {
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> {
Ok(Self {
brace_token: syn::token::Brace {
span: Span::call_site(),
},
brace_token: None,
elements: Self::parse_elements(input)?,
})
}
@ -333,7 +330,7 @@ impl Parse for JSONObject {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Self {
brace_token: syn::braced!(content in input),
brace_token: Some(syn::braced!(content in input)),
elements: Self::parse_elements(&content)?,
})
}
@ -355,7 +352,10 @@ impl std::ops::DerefMut for JSONObject {
impl JSONObject {
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> {
@ -415,12 +415,20 @@ pub fn get_doc_comments(attributes: &[syn::Attribute]) -> Result<(String, Span),
continue;
}
if attr.path.is_ident("doc") {
let doc: BareAssignment<syn::LitStr> = syn::parse2(attr.tokens.clone())?;
let nv = match &attr.meta {
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() {
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 {
syn::Path {
leading_colon: if leading_colon {
Some(syn::token::Colon2 {
Some(syn::token::PathSep {
spans: [span, span],
})
} 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.
pub fn join<T>(separator: &str, iter: impl Iterator<Item = T>) -> String
where
@ -701,7 +685,7 @@ pub fn derives_trait(attributes: &[syn::Attribute], ident: &str) -> bool {
/// Iterator over the types found in `#[derive(...)]` attributes.
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>,
}
@ -713,7 +697,7 @@ impl<'a> Iterator for DerivedItems<'a> {
if let Some(current) = &mut self.current {
loop {
match current.next() {
Some(syn::NestedMeta::Meta(syn::Meta::Path(path))) => return Some(path),
Some(syn::Meta::Path(path)) => return Some(path),
Some(_) => continue,
None => {
self.current = None;
@ -728,15 +712,16 @@ impl<'a> Iterator for DerivedItems<'a> {
continue;
}
match attr.parse_meta() {
Ok(syn::Meta::List(list)) if list.path.is_ident("derive") => {
self.current = Some(list.nested.into_iter());
match &attr.meta {
syn::Meta::List(list) if list.path.is_ident("derive") => {
if let Ok(items) =
list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated)
{
self.current = Some(items.into_iter());
}
continue;
}
// ignore anything that isn't a `derive(...)` attribute
Ok(_) => continue,
// ignore parse errors
Err(_) => continue,
_ => continue,
}
}
}
@ -756,102 +741,71 @@ where
continue;
}
if !attr.path.is_ident("derive") {
attributes.push(attr);
continue;
}
let mut args: AttrArgs = match syn::parse2(attr.tokens.clone()) {
Ok(args) => args,
Err(_) => {
// if we can't parse it, we don't care
attributes.push(attr);
continue;
}
};
for arg in std::mem::take(&mut args.args).into_pairs() {
match arg {
Pair::Punctuated(item, punct) => {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = &item {
if !func(path) {
continue;
}
}
args.args.push_value(item);
args.args.push_punct(punct);
}
Pair::End(item) => {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = &item {
if !func(path) {
continue;
}
}
args.args.push_value(item);
}
}
}
if !args.args.is_empty() {
attr.tokens = args.into_token_stream();
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 {
make_attribute(
span,
make_ident_path(Ident::new("derive", span)),
quote::quote! { (#content) },
)
}
/// Extract (remove) an attribute from a list and run a callback on its parameters.
pub fn extract_attributes(
attributes: &mut Vec<syn::Attribute>,
attr_name: &str,
mut func_matching: impl FnMut(&syn::Attribute, syn::NestedMeta) -> Result<(), syn::Error>,
) {
for attr in std::mem::take(attributes) {
if attr.style != syn::AttrStyle::Outer {
attributes.push(attr);
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,
let list = match &mut attr.meta {
syn::Meta::List(list) if list.path.is_ident("derive") => list,
_ => {
attributes.push(attr);
continue;
}
};
for entry in list.nested {
match func_matching(&attr, entry) {
Ok(()) => (),
Err(err) => crate::add_error(err),
let mut args =
match list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
Ok(args) => args,
Err(_) => {
// if we can't parse it, we don't care
attributes.push(attr);
continue;
}
};
for arg in std::mem::take(&mut args).into_pairs() {
match arg {
Pair::Punctuated(item, punct) => {
if let syn::Meta::Path(path) = &item {
if !func(path) {
continue;
}
}
args.push_value(item);
args.push_punct(punct);
}
Pair::End(item) => {
if let syn::Meta::Path(path) = &item {
if !func(path) {
continue;
}
}
args.push_value(item);
}
}
}
if !args.is_empty() {
list.tokens = args.into_token_stream();
attributes.push(attr);
}
}
}
pub fn make_derive_attribute(span: Span, content: TokenStream) -> syn::Attribute {
// FIXME: syn2 wtf come on...
let bracket_span =
proc_macro2::Group::new(proc_macro2::Delimiter::Bracket, TokenStream::new()).delim_span();
let paren_span =
proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, TokenStream::new())
.delim_span();
syn::Attribute {
pound_token: syn::token::Pound { spans: [span] },
style: syn::AttrStyle::Outer,
bracket_token: syn::token::Bracket { span: bracket_span },
//meta: syn::Meta::List(syn::parse_quote_spanned!(span => derive ( #content )).unwrap()),
meta: syn::Meta::List(syn::MetaList {
path: make_ident_path(Ident::new("derive", span)),
delimiter: syn::MacroDelimiter::Paren(syn::token::Paren { span: paren_span }),
tokens: content,
}),
}
}
@ -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
/// set.
pub fn parse_str_value_to_option<T: Parse>(target: &mut Option<T>, nv: &syn::MetaNameValue) {
duplicate(&*target, &nv.path);
match &nv.lit {
syn::Lit::Str(s) => match parse_lit_str(s) {
Ok(value) => *target = Some(value),
Err(err) => crate::add_error(err),
},
other => error!(other => "bad value for '{:?}' attribute", nv.path),
pub fn parse_str_value_to_option<T: Parse>(
target: &mut Option<T>,
path: &syn::Path,
nv: syn::parse::ParseStream<'_>,
) {
duplicate(&*target, &path);
match nv.parse().and_then(|lit| parse_lit_str(&lit)) {
Ok(value) => *target = Some(value),
Err(err) => crate::add_error(err),
}
}