mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-07 22:50:49 +00:00
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:
parent
eb1abe45b6
commit
b232b580a0
@ -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"
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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,19 +135,28 @@ 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 {
|
||||||
continue;
|
syn::Meta::List(list) if list.path.is_ident("serde") => list,
|
||||||
}
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
let args: AttrArgs = syn::parse2(attrib.tokens.clone())?;
|
let args =
|
||||||
for arg in args.args {
|
list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated)?;
|
||||||
if let syn::NestedMeta::Meta(syn::Meta::NameValue(var)) = arg {
|
|
||||||
if var.path.is_ident("rename_all") {
|
for arg in args {
|
||||||
let rename_all = RenameAll::try_from(&var.lit)?;
|
if let syn::Meta::NameValue(var) = arg {
|
||||||
if this.rename_all.is_some() && this.rename_all != Some(rename_all) {
|
if !var.path.is_ident("rename_all") {
|
||||||
error!(var.lit => "multiple conflicting 'rename_all' attributes");
|
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 {
|
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) {
|
..
|
||||||
error!(&rename => "multiple conflicting 'rename' attributes");
|
}) => {
|
||||||
}
|
if self.rename.is_some() && self.rename.as_ref() != Some(rename) {
|
||||||
self.rename = 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") => {
|
} else if path.is_ident("flatten") {
|
||||||
self.flatten = true;
|
arg.require_path_only()?;
|
||||||
}
|
self.flatten = true;
|
||||||
_ => continue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,102 +741,71 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attr.path.is_ident("derive") {
|
let list = match &mut attr.meta {
|
||||||
attributes.push(attr);
|
syn::Meta::List(list) if list.path.is_ident("derive") => list,
|
||||||
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,
|
|
||||||
_ => {
|
_ => {
|
||||||
attributes.push(attr);
|
attributes.push(attr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for entry in list.nested {
|
let mut args =
|
||||||
match func_matching(&attr, entry) {
|
match list.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
|
||||||
Ok(()) => (),
|
Ok(args) => args,
|
||||||
Err(err) => crate::add_error(err),
|
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
|
/// 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<'_>,
|
||||||
Ok(value) => *target = Some(value),
|
) {
|
||||||
Err(err) => crate::add_error(err),
|
duplicate(&*target, &path);
|
||||||
},
|
match nv.parse().and_then(|lit| parse_lit_str(&lit)) {
|
||||||
other => error!(other => "bad value for '{:?}' attribute", nv.path),
|
Ok(value) => *target = Some(value),
|
||||||
|
Err(err) => crate::add_error(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user