Eagerly parse variant-level borrow attribute instead of deferring entire Meta

This commit is contained in:
David Tolnay 2023-03-11 11:07:18 -08:00
parent 696f6f56db
commit e106feb5ec
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82

View File

@ -797,7 +797,12 @@ pub struct Variant {
other: bool, other: bool,
serialize_with: Option<syn::ExprPath>, serialize_with: Option<syn::ExprPath>,
deserialize_with: Option<syn::ExprPath>, deserialize_with: Option<syn::ExprPath>,
borrow: Option<syn::Meta>, borrow: Option<BorrowAttribute>,
}
struct BorrowAttribute {
path: syn::Path,
lifetimes: Option<BTreeSet<syn::Lifetime>>,
} }
impl Variant { impl Variant {
@ -950,10 +955,35 @@ impl Variant {
} }
} }
// Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]` // Parse `#[serde(borrow)]`
Meta(m) if m.path() == BORROW => match &variant.fields { Meta(Path(word)) if word == BORROW => match &variant.fields {
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
borrow.set(m.path(), m.clone()); borrow.set(
word,
BorrowAttribute {
path: word.clone(),
lifetimes: None,
},
);
}
_ => {
let msg = "#[serde(borrow)] may only be used on newtype variants";
cx.error_spanned_by(variant, msg);
}
},
// Parse `#[serde(borrow = "'a + 'b")]`
Meta(NameValue(m)) if m.path == BORROW => match &variant.fields {
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, BORROW, &m.lit) {
borrow.set(
&m.path,
BorrowAttribute {
path: m.path.clone(),
lifetimes: Some(lifetimes),
},
);
}
} }
_ => { _ => {
let msg = "#[serde(borrow)] may only be used on newtype variants"; let msg = "#[serde(borrow)] may only be used on newtype variants";
@ -1110,17 +1140,29 @@ impl Field {
None => index.to_string(), None => index.to_string(),
}; };
let variant_borrow = attrs if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) {
.and_then(|variant| variant.borrow.as_ref()) if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
.map(|borrow| Meta(borrow.clone())); if let Some(lifetimes) = &borrow_attribute.lifetimes {
for lifetime in lifetimes {
if !borrowable.contains(lifetime) {
let msg =
format!("field `{}` does not have lifetime {}", ident, lifetime);
cx.error_spanned_by(field, msg);
}
}
borrowed_lifetimes.set(&borrow_attribute.path, lifetimes.clone());
} else {
borrowed_lifetimes.set(&borrow_attribute.path, borrowable);
}
}
}
for meta_item in variant_borrow.into_iter().chain( for meta_item in field
field .attrs
.attrs .iter()
.iter() .flat_map(|attr| get_serde_meta_items(cx, attr))
.flat_map(|attr| get_serde_meta_items(cx, attr)) .flatten()
.flatten(), {
) {
match &meta_item { match &meta_item {
// Parse `#[serde(rename = "foo")]` // Parse `#[serde(rename = "foo")]`
Meta(NameValue(m)) if m.path == RENAME => { Meta(NameValue(m)) if m.path == RENAME => {