From e106feb5ecf14851345402ab2a8ac3e8bc7a397b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Mar 2023 11:07:18 -0800 Subject: [PATCH] Eagerly parse variant-level borrow attribute instead of deferring entire Meta --- serde_derive/src/internals/attr.rs | 72 +++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 9e820fb53..de6977f1d 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -797,7 +797,12 @@ pub struct Variant { other: bool, serialize_with: Option, deserialize_with: Option, - borrow: Option, + borrow: Option, +} + +struct BorrowAttribute { + path: syn::Path, + lifetimes: Option>, } impl Variant { @@ -950,10 +955,35 @@ impl Variant { } } - // Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]` - Meta(m) if m.path() == BORROW => match &variant.fields { + // Parse `#[serde(borrow)]` + Meta(Path(word)) if word == BORROW => match &variant.fields { + syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + 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 => { - borrow.set(m.path(), m.clone()); + 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"; @@ -1110,17 +1140,29 @@ impl Field { None => index.to_string(), }; - let variant_borrow = attrs - .and_then(|variant| variant.borrow.as_ref()) - .map(|borrow| Meta(borrow.clone())); - - for meta_item in variant_borrow.into_iter().chain( - field - .attrs - .iter() - .flat_map(|attr| get_serde_meta_items(cx, attr)) - .flatten(), - ) { + if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) { + if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) { + 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 field + .attrs + .iter() + .flat_map(|attr| get_serde_meta_items(cx, attr)) + .flatten() + { match &meta_item { // Parse `#[serde(rename = "foo")]` Meta(NameValue(m)) if m.path == RENAME => {