From a80ce16dd2e2cc2660590d77fe8cbbfca03da7ce Mon Sep 17 00:00:00 2001 From: Joshua Maleszewski Date: Sat, 13 Apr 2024 16:41:00 -0400 Subject: [PATCH 1/5] added support for default values on fields in addition to a path to a fn that produces a default value. --- serde_derive/src/de.rs | 31 +++++++- serde_derive/src/internals/attr.rs | 75 +++++++++++++++---- test_suite/tests/test_annotations.rs | 11 ++- .../ui/default-attribute/enum_path.stderr | 2 +- 4 files changed, 101 insertions(+), 18 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index e3b737c61..354b2a867 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -174,7 +174,9 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi &generics, &parse_quote!(_serde::__private::Default), ), - attr::Default::None | attr::Default::Path(_) => generics, + attr::Default::None | + attr::Default::Expr(_) | + attr::Default::Path(_) => generics, }; let delife = borrowed.de_lifetime(); @@ -373,6 +375,7 @@ fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment { attr::Default::Default => quote!(_serde::__private::Default::default()), attr::Default::Path(path) => quote!(#path()), attr::Default::None => quote!(_serde::__private::PhantomData), + attr::Default::Expr(lit) => quote!(#lit), }; quote!(#member: #value) } @@ -754,6 +757,9 @@ fn deserialize_seq( attr::Default::Path(path) => Some(quote!( let __default: Self::Value = #path(); )), + attr::Default::Expr(lit) => Some(quote!( + let __default: Self::Value = #lit; + )), attr::Default::None => { // We don't need the default value, to prevent an unused variable warning // we'll leave the line empty. @@ -836,6 +842,9 @@ fn deserialize_seq_in_place( attr::Default::Path(path) => Some(quote!( let __default: #this_type #ty_generics = #path(); )), + attr::Default::Expr(lit) => Some(quote!( + let __default: #this_type #ty_generics = #lit; + )), attr::Default::None => { // We don't need the default value, to prevent an unused variable warning // we'll leave the line empty. @@ -2637,6 +2646,9 @@ fn deserialize_map( attr::Default::Path(path) => Some(quote!( let __default: Self::Value = #path(); )), + attr::Default::Expr(lit) => Some(quote!( + let __default: Self::Value = #lit + )), attr::Default::None => { // We don't need the default value, to prevent an unused variable warning // we'll leave the line empty. @@ -2804,6 +2816,9 @@ fn deserialize_map_in_place( attr::Default::Path(path) => Some(quote!( let __default: #this_type #ty_generics = #path(); )), + attr::Default::Expr(lit) => Some(quote!( + let __default: #this_type #ty_generics = #lit; + )), attr::Default::None => { // We don't need the default value, to prevent an unused variable warning // we'll leave the line empty. @@ -2947,11 +2962,16 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { attr::Default::Path(path) => { return quote_expr!(#path()); } + attr::Default::Expr(lit) => { + return quote_expr!(#lit); + } attr::Default::None => { /* below */ } } match *cattrs.default() { - attr::Default::Default | attr::Default::Path(_) => { + attr::Default::Default | + attr::Default::Path(_) | + attr::Default::Expr(_) => { let member = &field.member; return quote_expr!(__default.#member); } @@ -2990,11 +3010,16 @@ fn expr_is_missing_seq( attr::Default::Path(path) => { return quote_spanned!(path.span()=> #assign_to #path()); } + attr::Default::Expr(lit) => { + return quote_spanned!(lit.span()=> #assign_to #lit); + } attr::Default::None => { /* below */ } } match *cattrs.default() { - attr::Default::Default | attr::Default::Path(_) => { + attr::Default::Default | + attr::Default::Path(_) | + attr::Default::Expr(_) => { let member = &field.member; quote!(#assign_to __default.#member) } diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index bb9de328a..be35c6676 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -396,24 +396,21 @@ impl Container { deny_unknown_fields.set_true(meta.path); } else if meta.path == DEFAULT { if meta.input.peek(Token![=]) { - // #[serde(default = "...")] - if let Some(path) = parse_lit_into_expr_path(cx, DEFAULT, &meta)? { + // #[serde(default = ...)] + if let Some(dflt) = parse_default(cx, &meta)? { match &item.data { syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { syn::Fields::Named(_) | syn::Fields::Unnamed(_) => { - default.set(&meta.path, Default::Path(path)); + default.set(&meta.path, dflt); } syn::Fields::Unit => { - let msg = "#[serde(default = \"...\")] can only be used on structs that have fields"; + let msg = "#[serde(default = ...)] can only be used on structs that have fields"; cx.syn_error(meta.error(msg)); } }, - syn::Data::Enum(_) => { - let msg = "#[serde(default = \"...\")] can only be used on structs"; - cx.syn_error(meta.error(msg)); - } + syn::Data::Enum(_) | syn::Data::Union(_) => { - let msg = "#[serde(default = \"...\")] can only be used on structs"; + let msg = "#[serde(default = ...)] can only be used on structs"; cx.syn_error(meta.error(msg)); } } @@ -1055,13 +1052,17 @@ pub enum Default { Default, /// The default is given by this function. Path(syn::ExprPath), + /// The default is a literal value + Expr(Box), } impl Default { pub fn is_none(&self) -> bool { match self { Default::None => true, - Default::Default | Default::Path(_) => false, + Default::Default | + Default::Path(_) | + Default::Expr(_) => false, } } } @@ -1140,9 +1141,9 @@ impl Field { } } else if meta.path == DEFAULT { if meta.input.peek(Token![=]) { - // #[serde(default = "...")] - if let Some(path) = parse_lit_into_expr_path(cx, DEFAULT, &meta)? { - default.set(&meta.path, Default::Path(path)); + // #[serde(default = ...)] + if let Some(default_value) = parse_default(cx, &meta)? { + default.set(&meta.path, default_value) } } else { // #[serde(default)] @@ -1548,6 +1549,54 @@ fn parse_lit_into_expr_path( }) } +/// Parses either an [`ExprPath`](syn::ExprPath) to a `fn` which will return the +/// default value for a field, or an [`Expr`](syn::ExprParen) wrapped in parenthesies +/// which is a valid value for a field. +/// +/// ```no_run +/// use serde::{Serialize, Deserialize}; +/// +/// #[derive(Clone, Default, Serialize, Deserialize)] +/// pub struct DemoStruct { +/// #[serde(default = "my_fn")] +/// a: i32, +/// #[serde(default = (12))] +/// b: i32, +/// } +/// +/// fn my_fn() -> i32 { 16 } +/// ``` +fn parse_default( + cx: &Ctxt, + meta: &ParseNestedMeta, +) -> syn::Result> { + + let expr: syn::Expr = meta.value()?.parse()?; + match expr { + // #[serde(default = "...")] + syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) => { + match lit_str.parse::() { + Ok(path) => Ok(Some(Default::Path(path))), + _ => { + let msg = format!("failed to parse path: {:?}", lit_str.value()); + cx.error_spanned_by(&lit_str, msg); + Ok(None) + } + } + }, + + // #[serde(default = (...))] + syn::Expr::Paren(paren) => { + Ok(Some(Default::Expr(paren.expr))) + }, + _ => { + let msg = format!("failed to parse literal or fn path: {:?}", expr.to_token_stream()); + cx.error_spanned_by(&expr, msg); + Ok(None) + } + } +} + fn parse_lit_into_where( cx: &Ctxt, attr_name: Symbol, diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index 566f7d43f..ccc4cc8f4 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -95,6 +95,8 @@ where a4: D, #[serde(skip_deserializing, default = "MyDefault::my_default")] a5: E, + #[serde(skip_deserializing, default = (12.3))] + a6: f32 } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -123,6 +125,7 @@ fn test_default_struct() { a3: 3, a4: 0, a5: 123, + a6: 12.3, }, &[ Token::Struct { @@ -139,6 +142,8 @@ fn test_default_struct() { Token::I32(4), Token::Str("a5"), Token::I32(5), + Token::Str("a6"), + Token::F32(12.3), Token::StructEnd, ], ); @@ -150,14 +155,17 @@ fn test_default_struct() { a3: 123, a4: 0, a5: 123, + a6: 12.3 }, &[ Token::Struct { name: "DefaultStruct", - len: 3, + len: 6, }, Token::Str("a1"), Token::I32(1), + Token::Str("a6"), + Token::F32(12.3), Token::StructEnd, ], ); @@ -436,6 +444,7 @@ fn test_ignore_unknown() { a3: 3, a4: 0, a5: 123, + a6: 12.3 }, &[ Token::Struct { diff --git a/test_suite/tests/ui/default-attribute/enum_path.stderr b/test_suite/tests/ui/default-attribute/enum_path.stderr index 65ef3ac43..682ffdd8d 100644 --- a/test_suite/tests/ui/default-attribute/enum_path.stderr +++ b/test_suite/tests/ui/default-attribute/enum_path.stderr @@ -1,4 +1,4 @@ -error: #[serde(default = "...")] can only be used on structs +error: #[serde(default = ...)] can only be used on structs --> tests/ui/default-attribute/enum_path.rs:4:9 | 4 | #[serde(default = "default_e")] From 3028491a0eed6536e147a63c16edf06b76a4f2f6 Mon Sep 17 00:00:00 2001 From: Joshua Maleszewski Date: Sat, 13 Apr 2024 16:53:37 -0400 Subject: [PATCH 2/5] incremented version number --- serde/Cargo.toml | 2 +- serde_derive/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 73732672f..6811c46b3 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde" -version = "1.0.197" +version = "1.0.198" authors = ["Erick Tryzelaar ", "David Tolnay "] build = "build.rs" categories = ["encoding", "no-std", "no-std::no-alloc"] diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index 8d91a74d7..94b38d940 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["no-std", "no-std::no-alloc"] description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]" From e72dc96b9052876fa078086c4dd3976bdede6d79 Mon Sep 17 00:00:00 2001 From: Joshua Maleszewski Date: Sat, 13 Apr 2024 17:03:38 -0400 Subject: [PATCH 3/5] changed `serde_derive`s version requirement to match the new version number --- serde/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 6811c46b3..239d0272f 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -37,7 +37,7 @@ rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] # is compatible with exactly one serde release because the generated code # involves nonpublic APIs which are not bound by semver. [target.'cfg(any())'.dependencies] -serde_derive = { version = "=1.0.197", path = "../serde_derive" } +serde_derive = { version = "=1.0.198", path = "../serde_derive" } ### FEATURES ################################################################# From 48e573a803e0235d8da03afa6776d3126affc252 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 28 Jun 2024 10:52:14 -0400 Subject: [PATCH 4/5] added nix stuff to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 165eb22d0..13fc3a3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ target/ **/*.rs.bk *.sw[po] Cargo.lock +.vscode/ +default.nix \ No newline at end of file From 2a47c317da8c449c467f92293121d96f20584b91 Mon Sep 17 00:00:00 2001 From: Joshua Maleszewski <20520890+Threadzless@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:57:29 -0400 Subject: [PATCH 5/5] Update Cargo.toml --- serde/Cargo.toml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 28c6f17b1..3d9832268 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -1,10 +1,6 @@ [package] name = "serde" -<<<<<<< master -version = "1.0.198" -======= version = "1.0.203" ->>>>>>> master authors = ["Erick Tryzelaar ", "David Tolnay "] build = "build.rs" categories = ["encoding", "no-std", "no-std::no-alloc"] @@ -41,11 +37,7 @@ rustdoc-args = ["--generate-link-to-definition"] # is compatible with exactly one serde release because the generated code # involves nonpublic APIs which are not bound by semver. [target.'cfg(any())'.dependencies] -<<<<<<< master -serde_derive = { version = "=1.0.198", path = "../serde_derive" } -======= serde_derive = { version = "=1.0.203", path = "../serde_derive" } ->>>>>>> master ### FEATURES #################################################################