Compare commits

...

50 Commits

Author SHA1 Message Date
David Tolnay 54671259aa Release 1.0.156 2023-03-14 01:02:18 -07:00
David Tolnay 994f7c7924 Format with rustfmt 1.5.2-nightly 2023-03-14 00:50:38 -07:00
David Tolnay 7a8e4977e2 Merge pull request #2401 from dtolnay/docderive
Show derive macros in serde's rustdoc
2023-03-14 00:37:06 -07:00
David Tolnay fb7b6ea7ea Enable serde derive feature when built by docs.rs 2023-03-14 00:33:41 -07:00
David Tolnay 063dd5b93f Show derive macros in serde's rustdoc 2023-03-14 00:28:20 -07:00
David Tolnay a38aa31ade Merge pull request #2400 from Nilstrieb/explicit-reexport
Use explicit re-export of `serde_derive` to give rustc more info
2023-03-14 00:26:01 -07:00
nils f42b2581da Use explicit re-export of serde_derive to give rustc more info
rustc will start looking behind `#[cfg(FALSE)]` items to start giving
better diagnostics. By using an explicit re-export instead of a glob
export, we tell rustc that `Deserialize` and `Serialize` exist here.
2023-03-14 08:11:38 +01:00
David Tolnay 2ba406726f Release 1.0.155 2023-03-11 12:57:53 -08:00
David Tolnay 7e9826e17b Add link to core CStr stabilization announcement 2023-03-11 12:57:16 -08:00
David Tolnay f4dcc5c918 Merge pull request #2374 from safarir/master
Enable CStr and CString in no-std enviroment
2023-03-11 12:55:49 -08:00
David Tolnay 8b1887c440 Remove unneeded attr_name argument when parsing borrow attr 2023-03-11 11:35:03 -08:00
David Tolnay bbfb1d3504 Merge pull request #2399 from dtolnay/borrow
Eagerly parse variant-level borrow attribute instead of deferring entire Meta
2023-03-11 11:29:55 -08:00
David Tolnay e106feb5ec Eagerly parse variant-level borrow attribute instead of deferring entire Meta 2023-03-11 11:25:00 -08:00
David Tolnay 696f6f56db Merge pull request #2398 from dtolnay/borrow
Treat field-level borrow attr as duplicate of variant-level borrow attr
2023-03-11 11:24:50 -08:00
David Tolnay b7b636a23f Treat field-level borrow attr as duplicate of variant-level borrow attr 2023-03-11 11:17:40 -08:00
David Tolnay 183b91775e Fix some comments in parsing of from/try_from/into attributes 2023-03-10 14:16:11 -08:00
David Tolnay 0e70f59021 Merge pull request #2396 from dtolnay/msg
Rearrange parts of attr.rs that rustfmt has been refusing to format
2023-03-09 20:29:50 -08:00
David Tolnay 4d9b76db73 Rearrange parts of attr.rs that rustfmt has been refusing to format 2023-03-09 20:17:43 -08:00
David Tolnay 9af132f594 Factor out duplicated error messages into reused variable 2023-03-09 20:17:20 -08:00
David Tolnay 6c063569c0 Eliminate closure from Punctuated to Vec conversion 2023-03-09 00:43:33 -08:00
David Tolnay 7e9b98401d Merge pull request #2395 from dtolnay/parsewhere
Simplify parsing of where-predicates in bound attribute
2023-03-09 00:38:57 -08:00
David Tolnay f301e09e02 Simplify parsing of where-predicates in bound attribute 2023-03-09 00:20:51 -08:00
David Tolnay b80e722f81 Merge pull request #2394 from dtolnay/emptybound
Eliminate special case on empty string passed to bound=""
2023-03-08 19:53:07 -08:00
David Tolnay 1714c262c4 Eliminate special case on empty string passed to bound=""
This works just fine if we make syn parse "where " as a syn::WhereClause.

The serde(bound = "") attribute is definitely not common enough that it
would warrant a micro-optimization.
2023-03-08 19:20:06 -08:00
David Tolnay a42cdafdcd Merge pull request #2393 from dtolnay/testbound
Add a test of serde(bound = "") attribute
2023-03-08 19:19:59 -08:00
David Tolnay eb4c3f16f7 Add a test of serde(bound = "") attribute
Without serde(bound = ""), serde_derive infers a bound of `T: Serialize`
for the generated Serialize impl and `T: Deserialize<'de> + Default` for
the Deserialize impl. `X` implements none of these so the generated code
would fail to compile.

    error[E0277]: the trait bound `X: Serialize` is not satisfied
       --> test_suite/tests/test_gen.rs:268:14
        |
    268 |     assert::<PhantomDataWrapper<X>>();
        |              ^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `X`
        |
        = help: the following other types implement trait `Serialize`:
                  &'a T
                  &'a mut T
                  ()
                  (T0, T1)
                  (T0, T1, T2)
                  (T0, T1, T2, T3)
                  (T0, T1, T2, T3, T4)
                  (T0, T1, T2, T3, T4, T5)
                and 248 others
    note: required for `PhantomDataWrapper<X>` to implement `Serialize`
       --> test_suite/tests/test_gen.rs:262:14
        |
    262 |     #[derive(Serialize, Deserialize)]
        |              ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    263 |     //#[serde(bound = "")]
    264 |     struct PhantomDataWrapper<T> {
        |            ^^^^^^^^^^^^^^^^^^^^^
    note: required by a bound in `assert`
       --> test_suite/tests/test_gen.rs:767:14
        |
    767 | fn assert<T: Serialize + DeserializeOwned>() {}
        |              ^^^^^^^^^ required by this bound in `assert`
        = note: this error originates in the derive macro `Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)

    error[E0277]: the trait bound `X: Deserialize<'_>` is not satisfied
       --> test_suite/tests/test_gen.rs:268:14
        |
    268 |     assert::<PhantomDataWrapper<X>>();
        |              ^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `X`
        |
        = help: the following other types implement trait `Deserialize<'de>`:
                  <&'a Path as Deserialize<'de>>
                  <&'a [u8] as Deserialize<'de>>
                  <&'a str as Deserialize<'de>>
                  <() as Deserialize<'de>>
                  <(T0, T1) as Deserialize<'de>>
                  <(T0, T1, T2) as Deserialize<'de>>
                  <(T0, T1, T2, T3) as Deserialize<'de>>
                  <(T0, T1, T2, T3, T4) as Deserialize<'de>>
                and 331 others
    note: required for `PhantomDataWrapper<X>` to implement `for<'de> Deserialize<'de>`
       --> test_suite/tests/test_gen.rs:262:25
        |
    262 |     #[derive(Serialize, Deserialize)]
        |                         ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    263 |     //#[serde(bound = "")]
    264 |     struct PhantomDataWrapper<T> {
        |            ^^^^^^^^^^^^^^^^^^^^^
        = note: required for `PhantomDataWrapper<X>` to implement `DeserializeOwned`
    note: required by a bound in `assert`
       --> test_suite/tests/test_gen.rs:767:26
        |
    767 | fn assert<T: Serialize + DeserializeOwned>() {}
        |                          ^^^^^^^^^^^^^^^^ required by this bound in `assert`
        = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

    error[E0277]: the trait bound `X: Default` is not satisfied
       --> test_suite/tests/test_gen.rs:268:14
        |
    268 |     assert::<PhantomDataWrapper<X>>();
        |              ^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `X`
        |
    note: required for `PhantomDataWrapper<X>` to implement `for<'de> Deserialize<'de>`
       --> test_suite/tests/test_gen.rs:262:25
        |
    262 |     #[derive(Serialize, Deserialize)]
        |                         ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    263 |     //#[serde(bound = "")]
    264 |     struct PhantomDataWrapper<T> {
        |            ^^^^^^^^^^^^^^^^^^^^^
        = note: required for `PhantomDataWrapper<X>` to implement `DeserializeOwned`
    note: required by a bound in `assert`
       --> test_suite/tests/test_gen.rs:767:26
        |
    767 | fn assert<T: Serialize + DeserializeOwned>() {}
        |                          ^^^^^^^^^^^^^^^^ required by this bound in `assert`
        = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
    help: consider annotating `X` with `#[derive(Default)]`
        |
    779 | #[derive(Default)]
        |
2023-03-08 19:11:25 -08:00
David Tolnay ce86f351d6 Make a directory dedicated to regression tests
I have had a good experience with this pattern in many of my other
libraries.
2023-03-08 19:02:42 -08:00
David Tolnay 0b90f6c96a Remove the need for allow(dead_code) added by PR 2383 2023-03-08 18:59:56 -08:00
David Tolnay 2198463218 Merge pull request #2392 from dtolnay/emptylifetimes
Eliminate special case on empty string passed to borrow=""
2023-03-08 18:55:51 -08:00
David Tolnay be57a5e00a Eliminate special case on empty string passed to borrow="" 2023-03-08 18:50:19 -08:00
David Tolnay b1b09eba60 Add ui test with nonempty string containing no lifetimes 2023-03-08 18:49:59 -08:00
David Tolnay eb1e8c140d Merge pull request #2391 from dtolnay/parselifetimes
Simplify parsing of borrow="..." attributes
2023-03-08 18:44:57 -08:00
David Tolnay 43da87939d Simplify parsing of borrow="..." attributes 2023-03-08 18:38:12 -08:00
David Tolnay 06d99a13a6 Merge pull request #2390 from dtolnay/litstrparse
Replace serde_derive_internal's parse_lit_str with syn::LitStr::parse
2023-03-08 18:30:18 -08:00
David Tolnay 49a911d7de Replace serde_derive_internal's parse_lit_str with syn::LitStr::parse 2023-03-08 18:23:35 -08:00
David Tolnay 27d6628785 Refer to syn's parse result via syn instead of parse module 2023-03-08 18:20:30 -08:00
David Tolnay e4e2956e79 Handle repr attribute consistently with every other serde attribute 2023-03-08 13:57:43 -08:00
David Tolnay ea2f7b81d9 Sort symbol list in alphabetical order
PR 1916 put EXPECTING at the end instead of in order.
2023-03-08 13:14:45 -08:00
David Tolnay f0dfdb5247 Resolve wildcard_imports pedantic clippy lint in test suite
error: usage of wildcard import
       --> test_suite/tests/test_gen.rs:901:9
        |
    901 |     use super::*;
        |         ^^^^^^^^ help: try: `super::Deserialize`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
        = note: `-D clippy::wildcard-imports` implied by `-D clippy::pedantic`
2023-03-08 12:14:04 -08:00
David Tolnay 6a5da85fcd Release 1.0.154 2023-03-08 12:09:05 -08:00
David Tolnay 0750eee4ff Merge pull request #2383 from Mingun/fix-flatten+static
Fix generation of non-existent lifetime `'de` when enum contains a #[serde(flatten)] field and a `'static` reference
2023-03-08 12:05:41 -08:00
David Tolnay ef551a517c Merge pull request #2389 from dtolnay/trimstart
Replace use of deprecated trim_left_matches with trim_start_matches
2023-03-08 12:00:34 -08:00
David Tolnay 1c5ea24f76 Replace use of deprecated trim_left_matches with trim_start_matches 2023-03-08 11:54:44 -08:00
David Tolnay 88d73e5250 Format PR 2387 with rustfmt 2023-03-08 11:54:28 -08:00
David Tolnay 1ff2a972c6 Merge pull request #2388 from serde-rs/exhaustivepatterns
Update the comment on simpler exhaustive matching in derive
2023-03-08 11:53:45 -08:00
David Tolnay bb72fe2726 Update the comment on simpler exhaustive matching in derive 2023-03-08 11:46:26 -08:00
Mingun 38c130a303 Do not generate DeserializeSeed impl when not needed
This function is called for untagged, internally and externally tagged enums,
but `deserializer` parameter is `None` only for the latest. Only when it's `None`
`DeserializeSeed` impl is used
2023-02-27 21:08:30 +05:00
Mingun c7393614ff Fix generation of non-existent lifetime 'de when enum contains a #[serde(flatten)] field and a 'static reference 2023-02-27 21:03:34 +05:00
Charles-Xavier Roy f7636428ed Add check for rust version 2023-02-17 16:25:16 -05:00
Charles-Xavier Roy bd4a0981ba Enable CStr and CString in no-std enviroment 2023-02-10 09:05:24 -05:00
20 changed files with 305 additions and 265 deletions
+3 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "1.0.153" # remember to update html_root_url and serde_derive dependency
version = "1.0.156" # remember to update html_root_url and serde_derive dependency
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
build = "build.rs"
categories = ["encoding", "no-std"]
@@ -15,7 +15,7 @@ repository = "https://github.com/serde-rs/serde"
rust-version = "1.19"
[dependencies]
serde_derive = { version = "=1.0.153", optional = true, path = "../serde_derive" }
serde_derive = { version = "=1.0.156", optional = true, path = "../serde_derive" }
[dev-dependencies]
serde_derive = { version = "1.0", path = "../serde_derive" }
@@ -27,6 +27,7 @@ doc-scrape-examples = false
features = ["derive", "rc"]
[package.metadata.docs.rs]
features = ["derive"]
targets = ["x86_64-unknown-linux-gnu"]
+6
View File
@@ -114,6 +114,12 @@ fn main() {
println!("cargo:rustc-cfg=no_std_atomic");
}
}
// Support for core::ffi::CStr and alloc::ffi::CString stabilized in Rust 1.64.
// https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#c-compatible-ffi-types-in-core-and-alloc
if minor < 64 {
println!("cargo:rustc-cfg=no_core_cstr");
}
}
fn rustc_minor_version() -> Option<u32> {
+7 -4
View File
@@ -666,10 +666,10 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a [u8] {
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
struct CStringVisitor;
#[cfg(feature = "std")]
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
impl<'de> Visitor<'de> for CStringVisitor {
type Value = CString;
@@ -720,7 +720,7 @@ impl<'de> Visitor<'de> for CStringVisitor {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
impl<'de> Deserialize<'de> for CString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -747,7 +747,10 @@ macro_rules! forwarded_impl {
}
}
#[cfg(all(feature = "std", not(no_de_boxed_c_str)))]
#[cfg(all(
any(feature = "std", all(not(no_core_cstr), feature = "alloc")),
not(no_de_boxed_c_str)
))]
forwarded_impl!((), Box<CStr>, CString::into_boxed_c_str);
#[cfg(not(no_core_reverse))]
+15 -4
View File
@@ -93,7 +93,7 @@
////////////////////////////////////////////////////////////////////////////////
// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.153")]
#![doc(html_root_url = "https://docs.rs/serde/1.0.156")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
@@ -219,13 +219,23 @@ mod lib {
#[cfg(feature = "std")]
pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
#[cfg(all(not(no_core_cstr), not(feature = "std")))]
pub use core::ffi::CStr;
#[cfg(feature = "std")]
pub use std::ffi::CStr;
#[cfg(all(not(no_core_cstr), feature = "alloc", not(feature = "std")))]
pub use alloc::ffi::CString;
#[cfg(feature = "std")]
pub use std::ffi::CString;
#[cfg(feature = "std")]
pub use std::{error, net};
#[cfg(feature = "std")]
pub use std::collections::{HashMap, HashSet};
#[cfg(feature = "std")]
pub use std::ffi::{CStr, CString, OsStr, OsString};
pub use std::ffi::{OsStr, OsString};
#[cfg(feature = "std")]
pub use std::hash::{BuildHasher, Hash};
#[cfg(feature = "std")]
@@ -328,9 +338,10 @@ mod std_error;
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
/// Derive macro available if serde is built with `features = ["derive"]`.
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;
pub use serde_derive::{Deserialize, Serialize};
#[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))]
mod actually_private {
+2 -2
View File
@@ -72,7 +72,7 @@ impl<'a> Serialize for fmt::Arguments<'a> {
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_cstr)))]
impl Serialize for CStr {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -83,7 +83,7 @@ impl Serialize for CStr {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
impl Serialize for CString {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "1.0.153" # remember to update html_root_url
version = "1.0.156" # remember to update html_root_url
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
categories = ["no-std"]
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+9 -6
View File
@@ -954,6 +954,7 @@ fn deserialize_struct(
lifetime: _serde::__private::PhantomData,
}
};
let need_seed = deserializer.is_none();
let dispatch = if let Some(deserializer) = deserializer {
quote! {
_serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
@@ -999,14 +1000,14 @@ fn deserialize_struct(
_ => None,
};
let visitor_seed = if is_enum && cattrs.has_flatten() {
let visitor_seed = if need_seed && is_enum && cattrs.has_flatten() {
Some(quote! {
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this_type #ty_generics;
fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result<Self::Value, __D::Error>
where
__D: _serde::Deserializer<'de>,
__D: _serde::Deserializer<#delife>,
{
_serde::Deserializer::deserialize_map(__deserializer, self)
}
@@ -1256,7 +1257,7 @@ fn deserialize_externally_tagged_enum(
// This is an empty enum like `enum Impossible {}` or an enum in which
// all variants have `#[serde(skip_deserializing)]`.
quote! {
// FIXME: Once we drop support for Rust 1.15:
// FIXME: Once feature(exhaustive_patterns) is stable:
// let _serde::__private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data);
// _serde::__private::Err(__err)
_serde::__private::Result::map(
@@ -2400,7 +2401,9 @@ fn deserialize_struct_as_struct_visitor(
.collect();
let fields_stmt = {
let field_names = field_names_idents.iter().flat_map(|(_, _, aliases)| aliases);
let field_names = field_names_idents
.iter()
.flat_map(|(_, _, aliases)| aliases);
quote_block! {
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
@@ -2536,7 +2539,7 @@ fn deserialize_map(
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
quote! {
// FIXME: Once we drop support for Rust 1.15:
// FIXME: Once feature(exhaustive_patterns) is stable:
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
_serde::__private::Option::map(
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
@@ -2769,7 +2772,7 @@ fn deserialize_map_in_place(
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
quote! {
// FIXME: Once we drop support for Rust 1.15:
// FIXME: Once feature(exhaustive_patterns) is stable:
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
_serde::__private::Option::map(
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
+1 -5
View File
@@ -39,10 +39,6 @@ pub fn wrap_in_const(
}
}
#[allow(deprecated)]
fn unraw(ident: &Ident) -> String {
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
// in 1.33. We currently support rustc back to 1.15 so we need to continue
// to use the deprecated one.
ident.to_string().trim_left_matches("r#").to_owned()
ident.to_string().trim_start_matches("r#").to_owned()
}
+188 -234
View File
@@ -1,16 +1,16 @@
use internals::respan::respan;
use internals::symbol::*;
use internals::{ungroup, Ctxt};
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::iter::FromIterator;
use syn;
use syn::parse::{self, Parse, ParseStream};
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::Ident;
use syn::Meta::{List, NameValue, Path};
use syn::NestedMeta::{Lit, Meta};
use syn::{Ident, Lifetime};
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
@@ -141,12 +141,8 @@ pub struct Name {
deserialize_aliases: Vec<String>,
}
#[allow(deprecated)]
fn unraw(ident: &Ident) -> String {
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
// in 1.33. We currently support rustc back to 1.15 so we need to continue
// to use the deprecated one.
ident.to_string().trim_left_matches("r#").to_owned()
ident.to_string().trim_start_matches("r#").to_owned()
}
impl Name {
@@ -379,48 +375,43 @@ impl Container {
syn::Fields::Named(_) => {
default.set(word, Default::Default);
}
syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
fields,
"#[serde(default)] can only be used on structs with named fields",
),
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
let msg =
"#[serde(default)] can only be used on structs with named fields";
cx.error_spanned_by(fields, msg);
}
},
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx.error_spanned_by(
enum_token,
"#[serde(default)] can only be used on structs with named fields",
),
syn::Data::Union(syn::DataUnion { union_token, .. }) => cx.error_spanned_by(
union_token,
"#[serde(default)] can only be used on structs with named fields",
),
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => {
let msg = "#[serde(default)] can only be used on structs with named fields";
cx.error_spanned_by(enum_token, msg);
}
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
let msg = "#[serde(default)] can only be used on structs with named fields";
cx.error_spanned_by(union_token, msg);
}
},
// Parse `#[serde(default = "...")]`
Meta(NameValue(m)) if m.path == DEFAULT => {
if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
match &item.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
match fields {
syn::Fields::Named(_) => {
default.set(&m.path, Default::Path(path));
}
syn::Fields::Unnamed(_) | syn::Fields::Unit => cx
.error_spanned_by(
fields,
"#[serde(default = \"...\")] can only be used on structs with named fields",
),
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
syn::Fields::Named(_) => {
default.set(&m.path, Default::Path(path));
}
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
cx.error_spanned_by(fields, msg);
}
},
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => {
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
cx.error_spanned_by(enum_token, msg);
}
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
cx.error_spanned_by(union_token, msg);
}
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx
.error_spanned_by(
enum_token,
"#[serde(default = \"...\")] can only be used on structs with named fields",
),
syn::Data::Union(syn::DataUnion {
union_token, ..
}) => cx.error_spanned_by(
union_token,
"#[serde(default = \"...\")] can only be used on structs with named fields",
),
}
}
}
@@ -447,16 +438,12 @@ impl Container {
untagged.set_true(word);
}
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
cx.error_spanned_by(
struct_token,
"#[serde(untagged)] can only be used on enums",
);
let msg = "#[serde(untagged)] can only be used on enums";
cx.error_spanned_by(struct_token, msg);
}
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
cx.error_spanned_by(
union_token,
"#[serde(untagged)] can only be used on enums",
);
let msg = "#[serde(untagged)] can only be used on enums";
cx.error_spanned_by(union_token, msg);
}
},
@@ -472,17 +459,13 @@ impl Container {
internal_tag.set(&m.path, s.value());
}
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
cx.error_spanned_by(
fields,
"#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
);
let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
cx.error_spanned_by(fields, msg);
}
},
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
cx.error_spanned_by(
union_token,
"#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
);
let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
cx.error_spanned_by(union_token, msg);
}
}
}
@@ -496,36 +479,32 @@ impl Container {
content.set(&m.path, s.value());
}
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
cx.error_spanned_by(
struct_token,
"#[serde(content = \"...\")] can only be used on enums",
);
let msg = "#[serde(content = \"...\")] can only be used on enums";
cx.error_spanned_by(struct_token, msg);
}
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
cx.error_spanned_by(
union_token,
"#[serde(content = \"...\")] can only be used on enums",
);
let msg = "#[serde(content = \"...\")] can only be used on enums";
cx.error_spanned_by(union_token, msg);
}
}
}
}
// Parse `#[serde(from = "Type")]
// Parse `#[serde(from = "Type")]`
Meta(NameValue(m)) if m.path == FROM => {
if let Ok(from_ty) = parse_lit_into_ty(cx, FROM, &m.lit) {
type_from.set_opt(&m.path, Some(from_ty));
}
}
// Parse `#[serde(try_from = "Type")]
// Parse `#[serde(try_from = "Type")]`
Meta(NameValue(m)) if m.path == TRY_FROM => {
if let Ok(try_from_ty) = parse_lit_into_ty(cx, TRY_FROM, &m.lit) {
type_try_from.set_opt(&m.path, Some(try_from_ty));
}
}
// Parse `#[serde(into = "Type")]
// Parse `#[serde(into = "Type")]`
Meta(NameValue(m)) if m.path == INTO => {
if let Ok(into_ty) = parse_lit_into_ty(cx, INTO, &m.lit) {
type_into.set_opt(&m.path, Some(into_ty));
@@ -573,21 +552,20 @@ impl Container {
.into_token_stream()
.to_string()
.replace(' ', "");
cx.error_spanned_by(
meta_item.path(),
format!("unknown serde container attribute `{}`", path),
);
let msg = format!("unknown serde container attribute `{}`", path);
cx.error_spanned_by(meta_item.path(), msg);
}
Lit(lit) => {
cx.error_spanned_by(lit, "unexpected literal in serde container attribute");
let msg = "unexpected literal in serde container attribute";
cx.error_spanned_by(lit, msg);
}
}
}
let mut is_packed = false;
for attr in &item.attrs {
if attr.path.is_ident("repr") {
if attr.path == REPR {
let _ = attr.parse_args_with(|input: ParseStream| {
while let Some(token) = input.parse()? {
if let TokenTree::Ident(ident) = token {
@@ -725,10 +703,9 @@ fn decide_tag(
syn::Fields::Named(_) | syn::Fields::Unit => {}
syn::Fields::Unnamed(fields) => {
if fields.unnamed.len() != 1 {
cx.error_spanned_by(
variant,
"#[serde(tag = \"...\")] cannot be used with tuple variants",
);
let msg =
"#[serde(tag = \"...\")] cannot be used with tuple variants";
cx.error_spanned_by(variant, msg);
break;
}
}
@@ -738,48 +715,28 @@ fn decide_tag(
TagType::Internal { tag }
}
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
cx.error_spanned_by(
untagged_tokens,
"enum cannot be both untagged and internally tagged",
);
cx.error_spanned_by(
tag_tokens,
"enum cannot be both untagged and internally tagged",
);
let msg = "enum cannot be both untagged and internally tagged";
cx.error_spanned_by(untagged_tokens, msg);
cx.error_spanned_by(tag_tokens, msg);
TagType::External // doesn't matter, will error
}
(None, None, Some((content_tokens, _))) => {
cx.error_spanned_by(
content_tokens,
"#[serde(tag = \"...\", content = \"...\")] must be used together",
);
let msg = "#[serde(tag = \"...\", content = \"...\")] must be used together";
cx.error_spanned_by(content_tokens, msg);
TagType::External
}
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
cx.error_spanned_by(
untagged_tokens,
"untagged enum cannot have #[serde(content = \"...\")]",
);
cx.error_spanned_by(
content_tokens,
"untagged enum cannot have #[serde(content = \"...\")]",
);
let msg = "untagged enum cannot have #[serde(content = \"...\")]";
cx.error_spanned_by(untagged_tokens, msg);
cx.error_spanned_by(content_tokens, msg);
TagType::External
}
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent { tag, content },
(Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
cx.error_spanned_by(
untagged_tokens,
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
);
cx.error_spanned_by(
tag_tokens,
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
);
cx.error_spanned_by(
content_tokens,
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
);
let msg = "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]";
cx.error_spanned_by(untagged_tokens, msg);
cx.error_spanned_by(tag_tokens, msg);
cx.error_spanned_by(content_tokens, msg);
TagType::External
}
}
@@ -798,44 +755,32 @@ fn decide_identifier(
) {
(_, None, None) => Identifier::No,
(_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
cx.error_spanned_by(
field_identifier_tokens,
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
);
cx.error_spanned_by(
variant_identifier_tokens,
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
);
let msg =
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set";
cx.error_spanned_by(field_identifier_tokens, msg);
cx.error_spanned_by(variant_identifier_tokens, msg);
Identifier::No
}
(syn::Data::Enum(_), Some(_), None) => Identifier::Field,
(syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), Some(_), None) => {
cx.error_spanned_by(
struct_token,
"#[serde(field_identifier)] can only be used on an enum",
);
let msg = "#[serde(field_identifier)] can only be used on an enum";
cx.error_spanned_by(struct_token, msg);
Identifier::No
}
(syn::Data::Union(syn::DataUnion { union_token, .. }), Some(_), None) => {
cx.error_spanned_by(
union_token,
"#[serde(field_identifier)] can only be used on an enum",
);
let msg = "#[serde(field_identifier)] can only be used on an enum";
cx.error_spanned_by(union_token, msg);
Identifier::No
}
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), None, Some(_)) => {
cx.error_spanned_by(
struct_token,
"#[serde(variant_identifier)] can only be used on an enum",
);
let msg = "#[serde(variant_identifier)] can only be used on an enum";
cx.error_spanned_by(struct_token, msg);
Identifier::No
}
(syn::Data::Union(syn::DataUnion { union_token, .. }), None, Some(_)) => {
cx.error_spanned_by(
union_token,
"#[serde(variant_identifier)] can only be used on an enum",
);
let msg = "#[serde(variant_identifier)] can only be used on an enum";
cx.error_spanned_by(union_token, msg);
Identifier::No
}
}
@@ -852,7 +797,12 @@ pub struct Variant {
other: bool,
serialize_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 {
@@ -1005,16 +955,39 @@ 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(m.path(), m.clone());
borrow.set(
word,
BorrowAttribute {
path: word.clone(),
lifetimes: None,
},
);
}
_ => {
cx.error_spanned_by(
variant,
"#[serde(borrow)] may only be used on newtype variants",
);
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, &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";
cx.error_spanned_by(variant, msg);
}
},
@@ -1024,14 +997,13 @@ impl Variant {
.into_token_stream()
.to_string()
.replace(' ', "");
cx.error_spanned_by(
meta_item.path(),
format!("unknown serde variant attribute `{}`", path),
);
let msg = format!("unknown serde variant attribute `{}`", path);
cx.error_spanned_by(meta_item.path(), msg);
}
Lit(lit) => {
cx.error_spanned_by(lit, "unexpected literal in serde variant attribute");
let msg = "unexpected literal in serde variant attribute";
cx.error_spanned_by(lit, msg);
}
}
}
@@ -1168,16 +1140,28 @@ impl Field {
None => index.to_string(),
};
let variant_borrow = attrs
.and_then(|variant| variant.borrow.as_ref())
.map(|borrow| Meta(borrow.clone()));
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()
.chain(variant_borrow)
{
match &meta_item {
// Parse `#[serde(rename = "foo")]`
@@ -1299,17 +1283,15 @@ impl Field {
// Parse `#[serde(borrow = "'a + 'b")]`
Meta(NameValue(m)) if m.path == BORROW => {
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, BORROW, &m.lit) {
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, &m.lit) {
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
for lifetime in &lifetimes {
if !borrowable.contains(lifetime) {
cx.error_spanned_by(
field,
format!(
"field `{}` does not have lifetime {}",
ident, lifetime
),
let msg = format!(
"field `{}` does not have lifetime {}",
ident, lifetime,
);
cx.error_spanned_by(field, msg);
}
}
borrowed_lifetimes.set(&m.path, lifetimes);
@@ -1335,14 +1317,13 @@ impl Field {
.into_token_stream()
.to_string()
.replace(' ', "");
cx.error_spanned_by(
meta_item.path(),
format!("unknown serde field attribute `{}`", path),
);
let msg = format!("unknown serde field attribute `{}`", path);
cx.error_spanned_by(meta_item.path(), msg);
}
Lit(lit) => {
cx.error_spanned_by(lit, "unexpected literal in serde field attribute");
let msg = "unexpected literal in serde field attribute";
cx.error_spanned_by(lit, msg);
}
}
}
@@ -1525,13 +1506,11 @@ where
}
_ => {
cx.error_spanned_by(
meta,
format!(
"malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
attr_name
),
let msg = format!(
"malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
attr_name,
);
cx.error_spanned_by(meta, msg);
return Err(());
}
}
@@ -1595,21 +1574,20 @@ fn get_lit_str2<'a>(
if let syn::Lit::Str(lit) = lit {
Ok(lit)
} else {
cx.error_spanned_by(
lit,
format!(
"expected serde {} attribute to be a string: `{} = \"...\"`",
attr_name, meta_item_name
),
let msg = format!(
"expected serde {} attribute to be a string: `{} = \"...\"`",
attr_name, meta_item_name,
);
cx.error_spanned_by(lit, msg);
Err(())
}
}
fn parse_lit_into_path(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Path, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
parse_lit_str(string).map_err(|_| {
cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
string.parse().map_err(|_| {
let msg = format!("failed to parse path: {:?}", string.value());
cx.error_spanned_by(lit, msg);
})
}
@@ -1619,8 +1597,9 @@ fn parse_lit_into_expr_path(
lit: &syn::Lit,
) -> Result<syn::ExprPath, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
parse_lit_str(string).map_err(|_| {
cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
string.parse().map_err(|_| {
let msg = format!("failed to parse path: {:?}", string.value());
cx.error_spanned_by(lit, msg);
})
}
@@ -1631,63 +1610,53 @@ fn parse_lit_into_where(
lit: &syn::Lit,
) -> Result<Vec<syn::WherePredicate>, ()> {
let string = get_lit_str2(cx, attr_name, meta_item_name, lit)?;
if string.value().is_empty() {
return Ok(Vec::new());
}
let where_string = syn::LitStr::new(&format!("where {}", string.value()), string.span());
parse_lit_str::<syn::WhereClause>(&where_string)
.map(|wh| wh.predicates.into_iter().collect())
string
.parse_with(Punctuated::<syn::WherePredicate, Token![,]>::parse_terminated)
.map(Vec::from_iter)
.map_err(|err| cx.error_spanned_by(lit, err))
}
fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Type, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
parse_lit_str(string).map_err(|_| {
cx.error_spanned_by(
lit,
format!("failed to parse type: {} = {:?}", attr_name, string.value()),
);
string.parse().map_err(|_| {
let msg = format!("failed to parse type: {} = {:?}", attr_name, string.value());
cx.error_spanned_by(lit, msg);
})
}
// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of
// lifetimes separated by `+`.
fn parse_lit_into_lifetimes(
cx: &Ctxt,
attr_name: Symbol,
lit: &syn::Lit,
) -> Result<BTreeSet<syn::Lifetime>, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
if string.value().is_empty() {
cx.error_spanned_by(lit, "at least one lifetime must be borrowed");
return Err(());
}
fn parse_lit_into_lifetimes(cx: &Ctxt, lit: &syn::Lit) -> Result<BTreeSet<syn::Lifetime>, ()> {
let string = get_lit_str(cx, BORROW, lit)?;
struct BorrowedLifetimes(Punctuated<syn::Lifetime, Token![+]>);
impl Parse for BorrowedLifetimes {
fn parse(input: ParseStream) -> parse::Result<Self> {
Punctuated::parse_separated_nonempty(input).map(BorrowedLifetimes)
}
}
if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
if let Ok(lifetimes) = string.parse_with(|input: ParseStream| {
let mut set = BTreeSet::new();
for lifetime in lifetimes {
while !input.is_empty() {
let lifetime: Lifetime = input.parse()?;
if !set.insert(lifetime.clone()) {
cx.error_spanned_by(lit, format!("duplicate borrowed lifetime `{}`", lifetime));
let msg = format!("duplicate borrowed lifetime `{}`", lifetime);
cx.error_spanned_by(lit, msg);
}
if input.is_empty() {
break;
}
input.parse::<Token![+]>()?;
}
return Ok(set);
Ok(set)
}) {
return if lifetimes.is_empty() {
let msg = "at least one lifetime must be borrowed";
cx.error_spanned_by(lit, msg);
Err(())
} else {
Ok(lifetimes)
};
}
cx.error_spanned_by(
lit,
format!("failed to parse borrowed lifetimes: {:?}", string.value()),
);
let msg = format!("failed to parse borrowed lifetimes: {:?}", string.value());
cx.error_spanned_by(lit, msg);
Err(())
}
@@ -1842,10 +1811,8 @@ fn borrowable_lifetimes(
let mut lifetimes = BTreeSet::new();
collect_lifetimes(&field.ty, &mut lifetimes);
if lifetimes.is_empty() {
cx.error_spanned_by(
field,
format!("field `{}` has no lifetimes to borrow", name),
);
let msg = format!("field `{}` has no lifetimes to borrow", name);
cx.error_spanned_by(field, msg);
Err(())
} else {
Ok(lifetimes)
@@ -1937,16 +1904,3 @@ fn collect_lifetimes_from_tokens(tokens: TokenStream, out: &mut BTreeSet<syn::Li
}
}
}
fn parse_lit_str<T>(s: &syn::LitStr) -> parse::Result<T>
where
T: Parse,
{
let tokens = spanned_tokens(s)?;
syn::parse2(tokens)
}
fn spanned_tokens(s: &syn::LitStr) -> parse::Result<TokenStream> {
let stream = syn::parse_str(&s.value())?;
Ok(respan(stream, s.span()))
}
+2 -1
View File
@@ -13,6 +13,7 @@ pub const DEFAULT: Symbol = Symbol("default");
pub const DENY_UNKNOWN_FIELDS: Symbol = Symbol("deny_unknown_fields");
pub const DESERIALIZE: Symbol = Symbol("deserialize");
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
pub const EXPECTING: Symbol = Symbol("expecting");
pub const FIELD_IDENTIFIER: Symbol = Symbol("field_identifier");
pub const FLATTEN: Symbol = Symbol("flatten");
pub const FROM: Symbol = Symbol("from");
@@ -22,6 +23,7 @@ pub const OTHER: Symbol = Symbol("other");
pub const REMOTE: Symbol = Symbol("remote");
pub const RENAME: Symbol = Symbol("rename");
pub const RENAME_ALL: Symbol = Symbol("rename_all");
pub const REPR: Symbol = Symbol("repr");
pub const SERDE: Symbol = Symbol("serde");
pub const SERIALIZE: Symbol = Symbol("serialize");
pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with");
@@ -35,7 +37,6 @@ pub const TRY_FROM: Symbol = Symbol("try_from");
pub const UNTAGGED: Symbol = Symbol("untagged");
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
pub const WITH: Symbol = Symbol("with");
pub const EXPECTING: Symbol = Symbol("expecting");
impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool {
+1 -1
View File
@@ -13,7 +13,7 @@
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.153")]
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.156")]
#![allow(unknown_lints, bare_trait_objects)]
// Ignored clippy lints
#![allow(
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_test"
version = "1.0.153" # remember to update html_root_url
version = "1.0.156" # remember to update html_root_url
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
build = "build.rs"
categories = ["development-tools::testing"]
+1 -1
View File
@@ -140,7 +140,7 @@
//! # }
//! ```
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.153")]
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.156")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
+1
View File
@@ -12,6 +12,7 @@ unstable = ["serde/unstable"]
serde = { path = "../serde" }
[dev-dependencies]
automod = "1.0"
fnv = "1.0"
rustversion = "1.0"
serde = { path = "../serde", features = ["rc", "derive"] }
+3
View File
@@ -0,0 +1,3 @@
mod regression {
automod::dir!("tests/regression");
}
+43
View File
@@ -0,0 +1,43 @@
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Nested;
#[derive(Deserialize)]
pub enum ExternallyTagged {
Flatten {
#[serde(flatten)]
nested: Nested,
string: &'static str,
},
}
#[derive(Deserialize)]
#[serde(tag = "tag")]
pub enum InternallyTagged {
Flatten {
#[serde(flatten)]
nested: Nested,
string: &'static str,
},
}
#[derive(Deserialize)]
#[serde(tag = "tag", content = "content")]
pub enum AdjacentlyTagged {
Flatten {
#[serde(flatten)]
nested: Nested,
string: &'static str,
},
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum UntaggedWorkaround {
Flatten {
#[serde(flatten)]
nested: Nested,
string: &'static str,
},
}
+10
View File
@@ -257,6 +257,16 @@ fn test_gen() {
}
assert::<VariantWithTraits2<X, X>>();
type PhantomDataAlias<T> = PhantomData<T>;
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
struct PhantomDataWrapper<T> {
#[serde(default)]
field: PhantomDataAlias<T>,
}
assert::<PhantomDataWrapper<X>>();
#[derive(Serialize, Deserialize)]
struct CowStr<'a>(Cow<'a, str>);
assert::<CowStr>();
@@ -1,5 +1,5 @@
error: duplicate serde attribute `borrow`
--> tests/ui/borrow/duplicate_variant.rs:8:13
--> tests/ui/borrow/duplicate_variant.rs:9:15
|
8 | #[serde(borrow)]
| ^^^^^^
9 | S(#[serde(borrow)] Str<'a>),
| ^^^^^^
@@ -3,6 +3,8 @@ use serde_derive::Deserialize;
#[derive(Deserialize)]
struct Test<'a> {
#[serde(borrow = "")]
r: &'a str,
#[serde(borrow = " ")]
s: &'a str,
}
@@ -3,3 +3,9 @@ error: at least one lifetime must be borrowed
|
5 | #[serde(borrow = "")]
| ^^
error: at least one lifetime must be borrowed
--> tests/ui/borrow/empty_lifetimes.rs:7:22
|
7 | #[serde(borrow = " ")]
| ^^^^