From 76ab5a7a43fa93348193a4945f958d264594ebd8 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 15 May 2024 18:33:05 +0200 Subject: [PATCH] Add lifetime container attribute for user-provided lifetime This PR is for discussion and related to #2190. As discussed there, the benefit is not obvious (except for providing more control to the user). With this PR, a user can use `#[serde(lifetime = 'a)]` to control the lifetime of the implementation of `Deserialize`. In particular, the user is now able to control all the implementation parameters when using `#[serde(bound = "", lifetime = 'a)]`. See https://github.com/ia0/wasefire/blob/ccf7bc30ac8440b30981b2f39b734de10d1a037c/crates/protocol/src/lib.rs#L65 for an example in real code. --- serde_derive/src/de.rs | 9 +++++++-- serde_derive/src/internals/attr.rs | 11 +++++++++++ serde_derive/src/internals/symbol.rs | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index e3b737c61..7e1c6565d 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -225,13 +225,15 @@ fn requires_default(field: &attr::Field, _variant: Option<&attr::Variant>) -> bo enum BorrowedLifetimes { Borrowed(BTreeSet), Static, + User(syn::Lifetime), } impl BorrowedLifetimes { fn de_lifetime(&self) -> syn::Lifetime { - match *self { + match self { BorrowedLifetimes::Borrowed(_) => syn::Lifetime::new("'de", Span::call_site()), BorrowedLifetimes::Static => syn::Lifetime::new("'static", Span::call_site()), + BorrowedLifetimes::User(a) => a.clone(), } } @@ -244,6 +246,7 @@ impl BorrowedLifetimes { bounds: bounds.iter().cloned().collect(), }), BorrowedLifetimes::Static => None, + BorrowedLifetimes::User(_) => None, } } } @@ -264,7 +267,9 @@ fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes { lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned()); } } - if lifetimes.iter().any(|b| b.to_string() == "'static") { + if let Some(a) = cont.attrs.lifetime() { + BorrowedLifetimes::User(a.clone()) + } else if lifetimes.iter().any(|b| b.to_string() == "'static") { BorrowedLifetimes::Static } else { BorrowedLifetimes::Borrowed(lifetimes) diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 0cfb23bf1..d3b079d84 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -208,6 +208,7 @@ pub struct Container { default: Default, rename_all_rules: RenameAllRules, rename_all_fields_rules: RenameAllRules, + lifetime: Option, ser_bound: Option>, de_bound: Option>, tag: TagType, @@ -294,6 +295,7 @@ impl Container { let mut rename_all_de_rule = Attr::none(cx, RENAME_ALL); let mut rename_all_fields_ser_rule = Attr::none(cx, RENAME_ALL_FIELDS); let mut rename_all_fields_de_rule = Attr::none(cx, RENAME_ALL_FIELDS); + let mut lifetime = Attr::none(cx, LIFETIME); let mut ser_bound = Attr::none(cx, BOUND); let mut de_bound = Attr::none(cx, BOUND); let mut untagged = BoolAttr::none(cx, UNTAGGED); @@ -440,6 +442,10 @@ impl Container { } } } + } else if meta.path == LIFETIME { + // #[serde(lifetime = 'a)] + let a: syn::Lifetime = meta.value()?.parse()?; + lifetime.set(&meta.path, a); } else if meta.path == BOUND { // #[serde(bound = "T: SomeBound")] // #[serde(bound(serialize = "...", deserialize = "..."))] @@ -579,6 +585,7 @@ impl Container { serialize: rename_all_fields_ser_rule.get().unwrap_or(RenameRule::None), deserialize: rename_all_fields_de_rule.get().unwrap_or(RenameRule::None), }, + lifetime: lifetime.get(), ser_bound: ser_bound.get(), de_bound: de_bound.get(), tag: decide_tag(cx, item, untagged, internal_tag, content), @@ -619,6 +626,10 @@ impl Container { &self.default } + pub fn lifetime(&self) -> Option<&syn::Lifetime> { + self.lifetime.as_ref() + } + pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { self.ser_bound.as_ref().map(|vec| &vec[..]) } diff --git a/serde_derive/src/internals/symbol.rs b/serde_derive/src/internals/symbol.rs index 572391a80..b0d7095f0 100644 --- a/serde_derive/src/internals/symbol.rs +++ b/serde_derive/src/internals/symbol.rs @@ -19,6 +19,7 @@ pub const FLATTEN: Symbol = Symbol("flatten"); pub const FROM: Symbol = Symbol("from"); pub const GETTER: Symbol = Symbol("getter"); pub const INTO: Symbol = Symbol("into"); +pub const LIFETIME: Symbol = Symbol("lifetime"); pub const NON_EXHAUSTIVE: Symbol = Symbol("non_exhaustive"); pub const OTHER: Symbol = Symbol("other"); pub const REMOTE: Symbol = Symbol("remote");