Compare commits

...

29 Commits

Author SHA1 Message Date
David Tolnay 48479e4bae Release 1.0.166 2023-07-03 11:33:19 -07:00
David Tolnay dfaf48bc09 Add no-std::no-alloc category 2023-07-03 11:32:55 -07:00
David Tolnay dcbc3e0162 Release 1.0.165 2023-07-03 04:21:59 -07:00
David Tolnay 0289d31724 Fix -Zminimal-versions build 2023-07-03 04:21:14 -07:00
David Tolnay 015e39776f No need for single-element vec for chaining one element 2023-07-02 21:11:09 -07:00
David Tolnay 6a9a21f178 Resolve useless_conversion clippy lint in test
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
       --> test_suite/tests/test_de.rs:202:12
        |
    202 |     .chain(vec![Token::MapEnd].into_iter())
        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![Token::MapEnd]`
        |
    note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
       --> /home/david/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:522:12
        |
    522 |         U: IntoIterator<Item = Self::Item>,
        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
        = note: `-D clippy::useless-conversion` implied by `-D clippy::all`
2023-07-02 21:10:06 -07:00
David Tolnay 81ac54b20d Resolve redundant_closure_call clippy lint
error: try not to call a closure in the expression where it is declared
        --> serde/src/de/impls.rs:1590:76
         |
    1590 |                       <(_, u16)>::deserialize(deserializer).map(|(ip, port)| $new(ip, port))
         |                                                                              ^^^^^^^^^^^^^^
    ...
    1620 | / parse_socket_impl!("IPv6 socket address" net::SocketAddrV6, |ip, port| net::SocketAddrV6::new(
    1621 | |     ip, port, 0, 0
    1622 | | ));
         | |__- in this macro invocation
         |
         = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
         = note: `-D clippy::redundant-closure-call` implied by `-D clippy::all`
         = note: this error originates in the macro `parse_socket_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
2023-07-02 21:08:50 -07:00
David Tolnay 6b4e75520a Resolve explicit_iter_loop pedantic clippy lint
error: it is more concise to loop over references to containers instead of using explicit iteration methods
        --> serde/src/private/de.rs:2761:22
         |
    2761 |         for entry in self.0.iter_mut() {
         |                      ^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *self.0`
         |
         = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
         = note: `-D clippy::explicit-iter-loop` implied by `-D clippy::pedantic`

    error: it is more concise to loop over references to containers instead of using explicit iteration methods
       --> serde_derive/src/internals/check.rs:202:20
        |
    202 |     for variant in variants.iter() {
        |                    ^^^^^^^^^^^^^^^ help: to write this more concisely, try: `variants`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
        = note: `-D clippy::explicit-iter-loop` implied by `-D clippy::pedantic`

    error: it is more concise to loop over references to containers instead of using explicit iteration methods
       --> serde_derive/src/bound.rs:262:28
        |
    262 |             for variant in variants.iter() {
        |                            ^^^^^^^^^^^^^^^ help: to write this more concisely, try: `variants`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
2023-07-02 21:08:44 -07:00
David Tolnay b053b4f492 Touch up early return in Enum checks 2023-07-02 21:08:44 -07:00
David Tolnay c0ba323166 Support a manual trigger on CI workflow 2023-06-23 22:50:52 -07:00
David Tolnay 20a48c9580 Remove .clippy.toml in favor of respecting rust-version from Cargo.toml 2023-06-15 18:31:38 -07:00
David Tolnay 09938803af Resolve redundant_static_lifetimes clippy lint 2023-06-15 18:31:38 -07:00
David Tolnay 6d0b43a220 Resolve redundant_field_names clippy lint 2023-06-15 18:31:38 -07:00
David Tolnay 8a4dfa7231 Merge pull request #2466 from Mingun/fix-de-count-of-field
Fix incorrect count of fields passed to tuple deserialization methods
2023-06-07 22:13:08 -07:00
David Tolnay 107018c628 Release 1.0.164 2023-06-07 22:05:07 -07:00
David Tolnay a398237930 Point out serde(untagged) variants which are out of order
Previously if someone wrote an enum containing:

- `A` (untagged)
- `B` (tagged)
- `C` (tagged)
- `D` (untagged)
- `E` (tagged)
- `F` (untagged)

serde_derive would produce errors referring to B and E only, saying
you're supposed to put untagged variants at the end. The choice of B and
E for this error doesn't make a lot of sense because in order to resolve
the issue, the user must either:

- move A and D down

or:

- move B, C, and E up.

This commit changes the error to appear on A and D instead.
2023-06-07 21:49:30 -07:00
David Tolnay b63c65d7f5 Merge pull request #2470 from dtolnay/contentref
Reuse a single ContentRefDeserializer throughout untagged enum deserialization
2023-06-07 21:38:49 -07:00
David Tolnay f60324e883 Reuse a single ContentRefDeserializer throughout untagged enum deserialization 2023-06-07 21:33:14 -07:00
David Tolnay 361c23a09a Simplify enumerate().find(...) -> Iterator::position 2023-06-07 21:23:31 -07:00
David Tolnay 43b23c7ea0 Format PR 2403 with rustfmt 2023-06-07 21:18:30 -07:00
David Tolnay 6081497506 Resolve semicolon_if_nothing_returned pedantic clippy lint
error: consider adding a `;` to the last statement for consistent formatting
       --> serde_derive/src/internals/ast.rs:161:13
        |
    161 |             seen_untagged = variant.attrs.untagged()
        |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `seen_untagged = variant.attrs.untagged();`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
        = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D clippy::pedantic`

    error: consider adding a `;` to the last statement for consistent formatting
       --> serde_derive/src/internals/ast.rs:159:17
        |
    159 | ...   cx.error_spanned_by(&variant.ident, "all variants with the #[serde(untagged)] attribute must be placed at the end of the enum")
        |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `cx.error_spanned_by(&variant.ident, "all variants with the #[serde(untagged)] attribute must be placed at the end of the enum");`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
2023-06-07 21:17:24 -07:00
David Ewert 48e5753e76 Allowed Enum variants to be individually marked as untagged (#2403) 2023-06-07 20:58:59 -07:00
David Tolnay bbba632ab3 Revert "Ui tests with compile_error resolved at call site"
This reverts commit e77db40b8d.
2023-06-07 20:50:51 -07:00
David Tolnay e77db40b8d Ui tests with compile_error resolved at call site 2023-06-07 20:02:04 -07:00
Mingun 2c1f62d4b4 Fix incorrect count of fields passed to tuple deserialization methods
This count should mean the number of fields expected in the serialized form,
so if some fields are skipped, they shouldn't be counted

Methods affected:
- Deserializer::deserialize_tuple
- Deserializer::deserialize_tuple_struct
- VariantAccess::tuple_variant
2023-05-28 23:17:05 +05:00
David Tolnay 1aebdc2760 Release serde_derive_internals 0.28.0 2023-05-25 08:20:10 -07:00
David Tolnay 705e58be8c Merge pull request #2464 from serde-rs/combine
Use syn::Error's combine() API instead of Vec<syn::Error>
2023-05-25 08:19:16 -07:00
David Tolnay 7c2c12aa43 Use syn::Error's combine() API instead of Vec<syn::Error> 2023-05-25 08:10:14 -07:00
David Tolnay a0f850f15b Show error details during miri setup in CI
Without this, if it fails, the only information printed is useless:

    Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)...
    fatal error: failed to build sysroot; run `cargo miri setup` to see the error details
2023-05-23 08:29:47 -07:00
30 changed files with 357 additions and 134 deletions
+2
View File
@@ -3,6 +3,7 @@ name: CI
on:
push:
pull_request:
workflow_dispatch:
schedule: [cron: "40 1 * * *"]
permissions:
@@ -149,6 +150,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@miri
- run: cargo miri setup
- run: cd serde && cargo miri test --features derive,rc,unstable
env:
MIRIFLAGS: -Zmiri-strict-provenance
-1
View File
@@ -1 +0,0 @@
msrv = "1.13.0"
+3 -3
View File
@@ -1,9 +1,9 @@
[package]
name = "serde"
version = "1.0.163" # remember to update html_root_url and serde_derive dependency
version = "1.0.166" # 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"]
categories = ["encoding", "no-std", "no-std::no-alloc"]
description = "A generic serialization/deserialization framework"
documentation = "https://docs.rs/serde"
homepage = "https://serde.rs"
@@ -15,7 +15,7 @@ repository = "https://github.com/serde-rs/serde"
rust-version = "1.19"
[dependencies]
serde_derive = { version = "=1.0.163", optional = true, path = "../serde_derive" }
serde_derive = { version = "=1.0.166", optional = true, path = "../serde_derive" }
[dev-dependencies]
serde_derive = { version = "1.0", path = "../serde_derive" }
+9 -11
View File
@@ -1587,7 +1587,7 @@ macro_rules! parse_socket_impl {
if deserializer.is_human_readable() {
deserializer.deserialize_str(FromStrVisitor::new($expecting))
} else {
<(_, u16)>::deserialize(deserializer).map(|(ip, port)| $new(ip, port))
<(_, u16)>::deserialize(deserializer).map($new)
}
}
}
@@ -1614,12 +1614,10 @@ impl<'de> Deserialize<'de> for net::SocketAddr {
}
#[cfg(feature = "std")]
parse_socket_impl!("IPv4 socket address" net::SocketAddrV4, net::SocketAddrV4::new);
parse_socket_impl!("IPv4 socket address" net::SocketAddrV4, |(ip, port)| net::SocketAddrV4::new(ip, port));
#[cfg(feature = "std")]
parse_socket_impl!("IPv6 socket address" net::SocketAddrV6, |ip, port| net::SocketAddrV6::new(
ip, port, 0, 0
));
parse_socket_impl!("IPv6 socket address" net::SocketAddrV6, |(ip, port)| net::SocketAddrV6::new(ip, port, 0, 0));
////////////////////////////////////////////////////////////////////////////////
@@ -2098,7 +2096,7 @@ impl<'de> Deserialize<'de> for Duration {
}
}
const FIELDS: &'static [&'static str] = &["secs", "nanos"];
const FIELDS: &[&str] = &["secs", "nanos"];
deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor)
}
}
@@ -2240,7 +2238,7 @@ impl<'de> Deserialize<'de> for SystemTime {
}
}
const FIELDS: &'static [&'static str] = &["secs_since_epoch", "nanos_since_epoch"];
const FIELDS: &[&str] = &["secs_since_epoch", "nanos_since_epoch"];
let duration = try!(deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor));
#[cfg(not(no_systemtime_checked_add))]
let ret = UNIX_EPOCH
@@ -2308,7 +2306,7 @@ mod range {
use de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor};
pub const FIELDS: &'static [&'static str] = &["start", "end"];
pub const FIELDS: &[&str] = &["start", "end"];
// If this were outside of the serde crate, it would just use:
//
@@ -2534,7 +2532,7 @@ where
}
}
const VARIANTS: &'static [&'static str] = &["Unbounded", "Included", "Excluded"];
const VARIANTS: &[&str] = &["Unbounded", "Included", "Excluded"];
deserializer.deserialize_enum("Bound", VARIANTS, BoundVisitor(PhantomData))
}
@@ -2642,7 +2640,7 @@ where
}
}
const VARIANTS: &'static [&'static str] = &["Ok", "Err"];
const VARIANTS: &[&str] = &["Ok", "Err"];
deserializer.deserialize_enum("Result", VARIANTS, ResultVisitor(PhantomData))
}
@@ -2708,7 +2706,7 @@ struct FromStrVisitor<T> {
impl<T> FromStrVisitor<T> {
fn new(expecting: &'static str) -> Self {
FromStrVisitor {
expecting: expecting,
expecting,
ty: PhantomData,
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ pub fn encode(c: char) -> Encode {
buf[3] = (code & 0x3F) as u8 | TAG_CONT;
0
};
Encode { buf: buf, pos: pos }
Encode { buf, pos }
}
pub struct Encode {
+14 -18
View File
@@ -251,7 +251,7 @@ macro_rules! primitive_deserializer {
#[allow(missing_docs)]
pub fn new(value: $ty) -> Self {
$name {
value: value,
value,
marker: PhantomData,
}
}
@@ -330,7 +330,7 @@ impl<E> U32Deserializer<E> {
#[allow(missing_docs)]
pub fn new(value: u32) -> Self {
U32Deserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -419,7 +419,7 @@ impl<'a, E> StrDeserializer<'a, E> {
#[allow(missing_docs)]
pub fn new(value: &'a str) -> Self {
StrDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -498,7 +498,7 @@ impl<'de, E> BorrowedStrDeserializer<'de, E> {
/// Create a new borrowed deserializer from the given string.
pub fn new(value: &'de str) -> BorrowedStrDeserializer<'de, E> {
BorrowedStrDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -598,7 +598,7 @@ impl<E> StringDeserializer<E> {
#[allow(missing_docs)]
pub fn new(value: String) -> Self {
StringDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -701,7 +701,7 @@ impl<'a, E> CowStrDeserializer<'a, E> {
#[allow(missing_docs)]
pub fn new(value: Cow<'a, str>) -> Self {
CowStrDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -783,7 +783,7 @@ impl<'a, E> BytesDeserializer<'a, E> {
/// Create a new deserializer from the given bytes.
pub fn new(value: &'a [u8]) -> Self {
BytesDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -842,7 +842,7 @@ impl<'de, E> BorrowedBytesDeserializer<'de, E> {
/// Create a new borrowed deserializer from the given borrowed bytes.
pub fn new(value: &'de [u8]) -> Self {
BorrowedBytesDeserializer {
value: value,
value,
marker: PhantomData,
}
}
@@ -1053,7 +1053,7 @@ pub struct SeqAccessDeserializer<A> {
impl<A> SeqAccessDeserializer<A> {
/// Construct a new `SeqAccessDeserializer<A>`.
pub fn new(seq: A) -> Self {
SeqAccessDeserializer { seq: seq }
SeqAccessDeserializer { seq }
}
}
@@ -1454,7 +1454,7 @@ pub struct MapAccessDeserializer<A> {
impl<A> MapAccessDeserializer<A> {
/// Construct a new `MapAccessDeserializer<A>`.
pub fn new(map: A) -> Self {
MapAccessDeserializer { map: map }
MapAccessDeserializer { map }
}
}
@@ -1519,7 +1519,7 @@ pub struct EnumAccessDeserializer<A> {
impl<A> EnumAccessDeserializer<A> {
/// Construct a new `EnumAccessDeserializer<A>`.
pub fn new(access: A) -> Self {
EnumAccessDeserializer { access: access }
EnumAccessDeserializer { access }
}
}
@@ -1613,7 +1613,7 @@ mod private {
}
pub fn map_as_enum<A>(map: A) -> MapAsEnum<A> {
MapAsEnum { map: map }
MapAsEnum { map }
}
impl<'de, A> VariantAccess<'de> for MapAsEnum<A>
@@ -1637,10 +1637,7 @@ mod private {
where
V: Visitor<'de>,
{
self.map.next_value_seed(SeedTupleVariant {
len: len,
visitor: visitor,
})
self.map.next_value_seed(SeedTupleVariant { len, visitor })
}
fn struct_variant<V>(
@@ -1651,8 +1648,7 @@ mod private {
where
V: Visitor<'de>,
{
self.map
.next_value_seed(SeedStructVariant { visitor: visitor })
self.map.next_value_seed(SeedStructVariant { visitor })
}
}
+1 -1
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.163")]
#![doc(html_root_url = "https://docs.rs/serde/1.0.166")]
// 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
+24 -16
View File
@@ -518,7 +518,7 @@ mod content {
impl<'de> TagOrContentVisitor<'de> {
fn new(name: &'static str) -> Self {
TagOrContentVisitor {
name: name,
name,
value: PhantomData,
}
}
@@ -815,7 +815,7 @@ mod content {
pub fn new(name: &'static str, expecting: &'static str) -> Self {
TaggedContentVisitor {
tag_name: name,
expecting: expecting,
expecting,
value: PhantomData,
}
}
@@ -859,7 +859,7 @@ mod content {
};
let rest = de::value::SeqAccessDeserializer::new(seq);
Ok(TaggedContent {
tag: tag,
tag,
content: try!(Content::deserialize(rest)),
})
}
@@ -887,7 +887,7 @@ mod content {
match tag {
None => Err(de::Error::missing_field(self.tag_name)),
Some(tag) => Ok(TaggedContent {
tag: tag,
tag,
content: Content::Map(vec),
}),
}
@@ -1464,7 +1464,7 @@ mod content {
/// private API, don't use
pub fn new(content: Content<'de>) -> Self {
ContentDeserializer {
content: content,
content,
err: PhantomData,
}
}
@@ -1485,8 +1485,8 @@ mod content {
{
pub fn new(variant: Content<'de>, value: Option<Content<'de>>) -> EnumDeserializer<'de, E> {
EnumDeserializer {
variant: variant,
value: value,
variant,
value,
err: PhantomData,
}
}
@@ -2142,8 +2142,8 @@ mod content {
};
visitor.visit_enum(EnumRefDeserializer {
variant: variant,
value: value,
variant,
value,
err: PhantomData,
})
}
@@ -2187,12 +2187,20 @@ mod content {
/// private API, don't use
pub fn new(content: &'a Content<'de>) -> Self {
ContentRefDeserializer {
content: content,
content,
err: PhantomData,
}
}
}
impl<'a, 'de: 'a, E> Copy for ContentRefDeserializer<'a, 'de, E> {}
impl<'a, 'de: 'a, E> Clone for ContentRefDeserializer<'a, 'de, E> {
fn clone(&self) -> Self {
*self
}
}
struct EnumRefDeserializer<'a, 'de: 'a, E>
where
E: de::Error,
@@ -2488,8 +2496,8 @@ mod content {
/// Not public API.
pub fn new(type_name: &'a str, variant_name: &'a str) -> Self {
InternallyTaggedUnitVisitor {
type_name: type_name,
variant_name: variant_name,
type_name,
variant_name,
}
}
}
@@ -2533,8 +2541,8 @@ mod content {
/// Not public API.
pub fn new(type_name: &'a str, variant_name: &'a str) -> Self {
UntaggedUnitVisitor {
type_name: type_name,
variant_name: variant_name,
type_name,
variant_name,
}
}
}
@@ -2750,7 +2758,7 @@ where
where
V: Visitor<'de>,
{
for entry in self.0.iter_mut() {
for entry in self.0 {
if let Some((key, value)) = flat_map_take_entry(entry, variants) {
return visitor.visit_enum(EnumDeserializer::new(key, Some(value)));
}
@@ -2785,7 +2793,7 @@ where
visitor.visit_map(FlatStructAccess {
iter: self.0.iter_mut(),
pending_content: None,
fields: fields,
fields,
_marker: PhantomData,
})
}
+18 -18
View File
@@ -27,10 +27,10 @@ where
T: Serialize,
{
value.serialize(TaggedSerializer {
type_ident: type_ident,
variant_ident: variant_ident,
tag: tag,
variant_name: variant_name,
type_ident,
variant_ident,
tag,
variant_name,
delegate: serializer,
})
}
@@ -350,8 +350,8 @@ mod content {
impl<M> SerializeTupleVariantAsMapValue<M> {
pub fn new(map: M, name: &'static str, len: usize) -> Self {
SerializeTupleVariantAsMapValue {
map: map,
name: name,
map,
name,
fields: Vec::with_capacity(len),
}
}
@@ -390,8 +390,8 @@ mod content {
impl<M> SerializeStructVariantAsMapValue<M> {
pub fn new(map: M, name: &'static str, len: usize) -> Self {
SerializeStructVariantAsMapValue {
map: map,
name: name,
map,
name,
fields: Vec::with_capacity(len),
}
}
@@ -711,7 +711,7 @@ mod content {
len: usize,
) -> Result<Self::SerializeTupleStruct, E> {
Ok(SerializeTupleStruct {
name: name,
name,
fields: Vec::with_capacity(len),
error: PhantomData,
})
@@ -725,9 +725,9 @@ mod content {
len: usize,
) -> Result<Self::SerializeTupleVariant, E> {
Ok(SerializeTupleVariant {
name: name,
variant_index: variant_index,
variant: variant,
name,
variant_index,
variant,
fields: Vec::with_capacity(len),
error: PhantomData,
})
@@ -747,7 +747,7 @@ mod content {
len: usize,
) -> Result<Self::SerializeStruct, E> {
Ok(SerializeStruct {
name: name,
name,
fields: Vec::with_capacity(len),
error: PhantomData,
})
@@ -761,9 +761,9 @@ mod content {
len: usize,
) -> Result<Self::SerializeStructVariant, E> {
Ok(SerializeStructVariant {
name: name,
variant_index: variant_index,
variant: variant,
name,
variant_index,
variant,
fields: Vec::with_capacity(len),
error: PhantomData,
})
@@ -1273,8 +1273,8 @@ where
{
fn new(map: &'a mut M, name: &'static str) -> FlatMapSerializeStructVariantAsMapValue<'a, M> {
FlatMapSerializeStructVariantAsMapValue {
map: map,
name: name,
map,
name,
fields: Vec::new(),
}
}
+1 -1
View File
@@ -713,7 +713,7 @@ impl Serialize for net::IpAddr {
}
#[cfg(feature = "std")]
const DEC_DIGITS_LUT: &'static [u8] = b"\
const DEC_DIGITS_LUT: &[u8] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
-1
View File
@@ -1 +0,0 @@
msrv = "1.31.0"
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "1.0.163" # remember to update html_root_url
version = "1.0.166" # 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)]"
@@ -24,7 +24,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "2.0.3"
syn = "2.0.21"
[dev-dependencies]
serde = { version = "1.0", path = "../serde" }
+1 -1
View File
@@ -259,7 +259,7 @@ pub fn with_bound(
};
match &cont.data {
Data::Enum(variants) => {
for variant in variants.iter() {
for variant in variants {
let relevant_fields = variant
.fields
.iter()
+52 -21
View File
@@ -15,9 +15,7 @@ use this;
use std::collections::BTreeSet;
use std::ptr;
pub fn expand_derive_deserialize(
input: &mut syn::DeriveInput,
) -> Result<TokenStream, Vec<syn::Error>> {
pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result<TokenStream> {
replace_receiver(input);
let ctxt = Ctxt::new();
@@ -447,14 +445,19 @@ fn deserialize_tuple(
cattrs: &attr::Container,
deserializer: Option<TokenStream>,
) -> Fragment {
assert!(!cattrs.has_flatten());
let field_count = fields
.iter()
.filter(|field| !field.attrs.skip_deserializing())
.count();
let this_type = &params.this_type;
let this_value = &params.this_value;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();
assert!(!cattrs.has_flatten());
// If there are getters (implying private fields), construct the local type
// and use an `Into` conversion to get the remote type. If there are no
// getters then construct the target type directly.
@@ -495,19 +498,18 @@ fn deserialize_tuple(
}
};
let dispatch = if let Some(deserializer) = deserializer {
quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr))
} else if is_enum {
quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
quote!(_serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr))
} else if nfields == 1 {
let type_name = cattrs.name().deserialize_name();
quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
} else {
let type_name = cattrs.name().deserialize_name();
quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr))
};
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
let visitor_var = if all_skipped {
let visitor_var = if field_count == 0 {
quote!(_)
} else {
quote!(mut __seq)
@@ -550,13 +552,18 @@ fn deserialize_tuple_in_place(
cattrs: &attr::Container,
deserializer: Option<TokenStream>,
) -> Fragment {
assert!(!cattrs.has_flatten());
let field_count = fields
.iter()
.filter(|field| !field.attrs.skip_deserializing())
.count();
let this_type = &params.this_type;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();
assert!(!cattrs.has_flatten());
let is_enum = variant_ident.is_some();
let expecting = match variant_ident {
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
@@ -582,19 +589,18 @@ fn deserialize_tuple_in_place(
};
let dispatch = if let Some(deserializer) = deserializer {
quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr))
} else if is_enum {
quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
quote!(_serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr))
} else if nfields == 1 {
let type_name = cattrs.name().deserialize_name();
quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
} else {
let type_name = cattrs.name().deserialize_name();
quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr))
};
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
let visitor_var = if all_skipped {
let visitor_var = if field_count == 0 {
quote!(_)
} else {
quote!(mut __seq)
@@ -1168,6 +1174,22 @@ fn deserialize_enum(
params: &Parameters,
variants: &[Variant],
cattrs: &attr::Container,
) -> Fragment {
// The variants have already been checked (in ast.rs) that all untagged variants appear at the end
match variants.iter().position(|var| var.attrs.untagged()) {
Some(variant_idx) => {
let (tagged, untagged) = variants.split_at(variant_idx);
let tagged_frag = Expr(deserialize_homogeneous_enum(params, tagged, cattrs));
deserialize_untagged_enum_after(params, untagged, cattrs, Some(tagged_frag))
}
None => deserialize_homogeneous_enum(params, variants, cattrs),
}
}
fn deserialize_homogeneous_enum(
params: &Parameters,
variants: &[Variant],
cattrs: &attr::Container,
) -> Fragment {
match cattrs.tag() {
attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs),
@@ -1668,6 +1690,16 @@ fn deserialize_untagged_enum(
params: &Parameters,
variants: &[Variant],
cattrs: &attr::Container,
) -> Fragment {
let first_attempt = None;
deserialize_untagged_enum_after(params, variants, cattrs, first_attempt)
}
fn deserialize_untagged_enum_after(
params: &Parameters,
variants: &[Variant],
cattrs: &attr::Container,
first_attempt: Option<Expr>,
) -> Fragment {
let attempts = variants
.iter()
@@ -1677,12 +1709,10 @@ fn deserialize_untagged_enum(
params,
variant,
cattrs,
quote!(
_serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content)
),
quote!(__deserializer),
))
});
let attempts = first_attempt.into_iter().chain(attempts);
// TODO this message could be better by saving the errors from the failed
// attempts. The heuristic used by TOML was to count the number of fields
// processed before an error, and use the error that happened after the
@@ -1697,6 +1727,7 @@ fn deserialize_untagged_enum(
quote_block! {
let __content = try!(<_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
let __deserializer = _serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content);
#(
if let _serde::__private::Ok(__ok) = #attempts {
+15 -2
View File
@@ -140,7 +140,7 @@ fn enum_from_ast<'a>(
variants: &'a Punctuated<syn::Variant, Token![,]>,
container_default: &attr::Default,
) -> Vec<Variant<'a>> {
variants
let variants: Vec<Variant> = variants
.iter()
.map(|variant| {
let attrs = attr::Variant::from_ast(cx, variant);
@@ -154,7 +154,20 @@ fn enum_from_ast<'a>(
original: variant,
}
})
.collect()
.collect();
let index_of_last_tagged_variant = variants
.iter()
.rposition(|variant| !variant.attrs.untagged());
if let Some(index_of_last_tagged_variant) = index_of_last_tagged_variant {
for variant in &variants[..index_of_last_tagged_variant] {
if variant.attrs.untagged() {
cx.error_spanned_by(&variant.ident, "all variants with the #[serde(untagged)] attribute must be placed at the end of the enum");
}
}
}
variants
}
fn struct_from_ast<'a>(
+9
View File
@@ -740,6 +740,7 @@ pub struct Variant {
serialize_with: Option<syn::ExprPath>,
deserialize_with: Option<syn::ExprPath>,
borrow: Option<BorrowAttribute>,
untagged: bool,
}
struct BorrowAttribute {
@@ -762,6 +763,7 @@ impl Variant {
let mut serialize_with = Attr::none(cx, SERIALIZE_WITH);
let mut deserialize_with = Attr::none(cx, DESERIALIZE_WITH);
let mut borrow = Attr::none(cx, BORROW);
let mut untagged = BoolAttr::none(cx, UNTAGGED);
for attr in &variant.attrs {
if attr.path() != SERDE {
@@ -879,6 +881,8 @@ impl Variant {
cx.error_spanned_by(variant, msg);
}
}
} else if meta.path == UNTAGGED {
untagged.set_true(&meta.path);
} else {
let path = meta.path.to_token_stream().to_string().replace(' ', "");
return Err(
@@ -905,6 +909,7 @@ impl Variant {
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
borrow: borrow.get(),
untagged: untagged.get(),
}
}
@@ -956,6 +961,10 @@ impl Variant {
pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
self.deserialize_with.as_ref()
}
pub fn untagged(&self) -> bool {
self.untagged
}
}
/// Represents field attribute information
+3 -7
View File
@@ -110,9 +110,7 @@ fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
fn check_identifier(cx: &Ctxt, cont: &Container) {
let variants = match &cont.data {
Data::Enum(variants) => variants,
Data::Struct(_, _) => {
return;
}
Data::Struct(_, _) => return,
};
for (i, variant) in variants.iter().enumerate() {
@@ -194,12 +192,10 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
let variants = match &cont.data {
Data::Enum(variants) => variants,
Data::Struct(_, _) => {
return;
}
Data::Struct(_, _) => return,
};
for variant in variants.iter() {
for variant in variants {
if variant.attrs.serialize_with().is_some() {
if variant.attrs.skip_serializing() {
cx.error_spanned_by(
+12 -5
View File
@@ -44,12 +44,19 @@ impl Ctxt {
}
/// Consume this object, producing a formatted error string if there are errors.
pub fn check(self) -> Result<(), Vec<syn::Error>> {
let errors = self.errors.borrow_mut().take().unwrap();
match errors.len() {
0 => Ok(()),
_ => Err(errors),
pub fn check(self) -> syn::Result<()> {
let mut errors = self.errors.borrow_mut().take().unwrap().into_iter();
let mut combined = match errors.next() {
Some(first) => first,
None => return Ok(()),
};
for rest in errors {
combined.combine(rest);
}
Err(combined)
}
}
+3 -8
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.163")]
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.166")]
#![allow(unknown_lints, bare_trait_objects)]
// Ignored clippy lints
#![allow(
@@ -92,7 +92,7 @@ mod try;
pub fn derive_serialize(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as DeriveInput);
ser::expand_derive_serialize(&mut input)
.unwrap_or_else(to_compile_errors)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
@@ -100,11 +100,6 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream {
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as DeriveInput);
de::expand_derive_deserialize(&mut input)
.unwrap_or_else(to_compile_errors)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
quote!(#(#compile_errors)*)
}
+8 -8
View File
@@ -10,9 +10,7 @@ use internals::{attr, replace_receiver, Ctxt, Derive};
use pretend;
use this;
pub fn expand_derive_serialize(
input: &mut syn::DeriveInput,
) -> Result<TokenStream, Vec<syn::Error>> {
pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result<TokenStream> {
replace_receiver(input);
let ctxt = Ctxt::new();
@@ -475,17 +473,19 @@ fn serialize_variant(
}
};
let body = Match(match cattrs.tag() {
attr::TagType::External => {
let body = Match(match (cattrs.tag(), variant.attrs.untagged()) {
(attr::TagType::External, false) => {
serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
}
attr::TagType::Internal { tag } => {
(attr::TagType::Internal { tag }, false) => {
serialize_internally_tagged_variant(params, variant, cattrs, tag)
}
attr::TagType::Adjacent { tag, content } => {
(attr::TagType::Adjacent { tag, content }, false) => {
serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content)
}
attr::TagType::None => serialize_untagged_variant(params, variant, cattrs),
(attr::TagType::None, _) | (_, true) => {
serialize_untagged_variant(params, variant, cattrs)
}
});
quote! {
-1
View File
@@ -1 +0,0 @@
msrv = "1.31.0"
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive_internals"
version = "0.27.0" # remember to update html_root_url
version = "0.28.0" # remember to update html_root_url
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
description = "AST representation used by Serde derive macros. Unstable."
documentation = "https://docs.rs/serde_derive_internals"
@@ -17,7 +17,7 @@ path = "lib.rs"
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", default-features = false, features = ["derive", "parsing", "printing", "clone-impls"] }
syn = { version = "2.0.21", default-features = false, features = ["derive", "parsing", "printing", "clone-impls"] }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
+1 -1
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.27.0")]
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.28.0")]
#![allow(unknown_lints, bare_trait_objects)]
// Ignored clippy lints
#![allow(
-1
View File
@@ -1 +0,0 @@
msrv = "1.13.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_test"
version = "1.0.163" # remember to update html_root_url
version = "1.0.166" # 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.163")]
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.166")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
+157 -1
View File
@@ -1533,7 +1533,7 @@ fn test_invalid_length_enum() {
Token::TupleVariant {
name: "InvalidLengthEnum",
variant: "B",
len: 3,
len: 2,
},
Token::I32(1),
Token::TupleVariantEnd,
@@ -2442,6 +2442,162 @@ fn test_untagged_enum_containing_flatten() {
);
}
#[test]
fn test_partially_untagged_enum() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Exp {
Lambda(u32, Box<Exp>),
#[serde(untagged)]
App(Box<Exp>, Box<Exp>),
#[serde(untagged)]
Var(u32),
}
use Exp::*;
let data = Lambda(0, Box::new(App(Box::new(Var(0)), Box::new(Var(0)))));
assert_tokens(
&data,
&[
Token::TupleVariant {
name: "Exp",
variant: "Lambda",
len: 2,
},
Token::U32(0),
Token::Tuple { len: 2 },
Token::U32(0),
Token::U32(0),
Token::TupleEnd,
Token::TupleVariantEnd,
],
);
}
#[test]
fn test_partially_untagged_enum_generic() {
trait Trait<T> {
type Assoc;
type Assoc2;
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum E<A, B, C>
where
A: Trait<C, Assoc2 = B>,
{
A(A::Assoc),
#[serde(untagged)]
B(A::Assoc2),
}
impl<T> Trait<T> for () {
type Assoc = T;
type Assoc2 = bool;
}
type MyE = E<(), bool, u32>;
use E::*;
assert_tokens::<MyE>(&B(true), &[Token::Bool(true)]);
assert_tokens::<MyE>(
&A(5),
&[
Token::NewtypeVariant {
name: "E",
variant: "A",
},
Token::U32(5),
],
);
}
#[test]
fn test_partially_untagged_enum_desugared() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Test {
A(u32, u32),
B(u32),
#[serde(untagged)]
C(u32),
#[serde(untagged)]
D(u32, u32),
}
use Test::*;
mod desugared {
use super::*;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub(super) enum Test {
A(u32, u32),
B(u32),
}
}
use desugared::Test as TestTagged;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum TestUntagged {
Tagged(TestTagged),
C(u32),
D(u32, u32),
}
impl From<Test> for TestUntagged {
fn from(test: Test) -> Self {
match test {
A(x, y) => TestUntagged::Tagged(TestTagged::A(x, y)),
B(x) => TestUntagged::Tagged(TestTagged::B(x)),
C(x) => TestUntagged::C(x),
D(x, y) => TestUntagged::D(x, y),
}
}
}
fn assert_tokens_desugared(value: Test, tokens: &[Token]) {
assert_tokens(&value, tokens);
let desugared: TestUntagged = value.into();
assert_tokens(&desugared, tokens);
}
assert_tokens_desugared(
A(0, 1),
&[
Token::TupleVariant {
name: "Test",
variant: "A",
len: 2,
},
Token::U32(0),
Token::U32(1),
Token::TupleVariantEnd,
],
);
assert_tokens_desugared(
B(1),
&[
Token::NewtypeVariant {
name: "Test",
variant: "B",
},
Token::U32(1),
],
);
assert_tokens_desugared(C(2), &[Token::U32(2)]);
assert_tokens_desugared(
D(3, 5),
&[
Token::Tuple { len: 2 },
Token::U32(3),
Token::U32(5),
Token::TupleEnd,
],
);
}
#[test]
fn test_flatten_untagged_enum() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
+2 -1
View File
@@ -14,6 +14,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::default::Default;
use std::ffi::{CStr, CString, OsString};
use std::fmt::Debug;
use std::iter;
use std::net;
use std::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
@@ -199,7 +200,7 @@ fn assert_de_tokens_ignore(ignorable_tokens: &[Token]) {
]
.into_iter()
.chain(ignorable_tokens.iter().copied())
.chain(vec![Token::MapEnd].into_iter())
.chain(iter::once(Token::MapEnd))
.collect();
let expected = IgnoreBase { a: 1 };
@@ -0,0 +1,10 @@
use serde_derive::Serialize;
#[derive(Serialize)]
enum E {
#[serde(untagged)]
A(u8),
B(String),
}
fn main() {}
@@ -0,0 +1,5 @@
error: all variants with the #[serde(untagged)] attribute must be placed at the end of the enum
--> tests/ui/enum-representation/partially_tagged_wrong_order.rs:6:5
|
6 | A(u8),
| ^