diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..750707701 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e924dfa4c..50139b6d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,12 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cd test_suite && cargo test --features unstable + - uses: actions/upload-artifact@v4 + if: always() + with: + name: Cargo.lock + path: Cargo.lock + continue-on-error: true windows: name: Test suite (windows) @@ -46,6 +52,7 @@ jobs: toolchain: ${{matrix.rust}} - run: cd serde && cargo build --features rc - run: cd serde && cargo build --no-default-features + - run: cd test_suite/no_std && cargo build nightly: name: Rust nightly ${{matrix.os == 'windows' && '(windows)' || ''}} @@ -84,17 +91,22 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + - run: sed -i '/"test_suite"/d' Cargo.toml - run: cd serde && cargo build --features rc - run: cd serde && cargo build --no-default-features - run: cd serde && cargo build derive: - name: Rust 1.56.0 + name: Rust 1.61.0 runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.56.0 + - uses: dtolnay/rust-toolchain@1.61.0 + - run: | + sed -i 's/proc-macro2 = { workspace = true/proc-macro2 = { version = "1"/' serde_derive*/Cargo.toml + sed -i 's/quote = { workspace = true/quote = { version = "1"/' serde_derive*/Cargo.toml + sed -i 's/syn = { workspace = true/syn = { version = "2"/' serde_derive*/Cargo.toml - run: cd serde && cargo check --no-default-features - run: cd serde && cargo check - run: cd serde_derive && cargo check @@ -106,6 +118,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.36.0 + - run: sed -i '/"test_suite"/d' Cargo.toml - run: cd serde && cargo build --no-default-features --features alloc minimal: @@ -118,6 +131,20 @@ jobs: - run: cargo generate-lockfile -Z minimal-versions - run: cargo check --locked --workspace + doc: + name: Documentation + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + RUSTDOCFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/install@cargo-docs-rs + - run: cargo docs-rs -p serde + - run: cargo docs-rs -p serde_derive + - run: cargo docs-rs -p serde_derive_internals + clippy: name: Clippy runs-on: ubuntu-latest @@ -154,5 +181,6 @@ jobs: timeout-minutes: 45 steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 diff --git a/.gitignore b/.gitignore index 165eb22d0..e9e21997b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -target/ -**/*.rs.bk -*.sw[po] -Cargo.lock +/target/ +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 727dc484f..41c11afaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,8 @@ members = [ [patch.crates-io] serde = { path = "serde" } + +[workspace.dependencies] +proc-macro2 = { version = "1.0.74", default-features = false } +quote = { version = "1.0.35", default-features = false } +syn = { version = "2.0.81", default-features = false } diff --git a/README.md b/README.md index 477fd3647..e3da0cbc8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# Serde   [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.31+]][Rust 1.31] [![serde_derive: rustc 1.56+]][Rust 1.56] +# Serde   [![Build Status]][actions] [![Latest Version]][crates.io] [![serde msrv]][Rust 1.31] [![serde_derive msrv]][Rust 1.61] [Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/serde/ci.yml?branch=master [actions]: https://github.com/serde-rs/serde/actions?query=branch%3Amaster [Latest Version]: https://img.shields.io/crates/v/serde.svg [crates.io]: https://crates.io/crates/serde -[serde: rustc 1.31+]: https://img.shields.io/badge/serde-rustc_1.31+-lightgray.svg -[serde_derive: rustc 1.56+]: https://img.shields.io/badge/serde_derive-rustc_1.56+-lightgray.svg +[serde msrv]: https://img.shields.io/crates/msrv/serde.svg?label=serde%20msrv&color=lightgray +[serde_derive msrv]: https://img.shields.io/crates/msrv/serde_derive.svg?label=serde_derive%20msrv&color=lightgray [Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html -[Rust 1.56]: https://blog.rust-lang.org/2021/10/21/Rust-1.56.0.html +[Rust 1.61]: https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html **Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.** diff --git a/crates-io.md b/crates-io.md index 187100358..b49a5483b 100644 --- a/crates-io.md +++ b/crates-io.md @@ -46,8 +46,8 @@ fn main() { Serde is one of the most widely used Rust libraries so any place that Rustaceans congregate will be able to help you out. For chat, consider trying the [#rust-questions] or [#rust-beginners] channels of the unofficial community -Discord (invite: , the [#rust-usage] or -[#beginners] channels of the official Rust Project Discord (invite: +Discord (invite: ), the [#rust-usage] +or [#beginners] channels of the official Rust Project Discord (invite: ), or the [#general][zulip] stream in Zulip. For asynchronous, consider the [\[rust\] tag on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned weekly easy questions post, or the Rust diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 5ecd948e9..948f24d19 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde" -version = "1.0.188" # remember to update html_root_url and serde_derive dependency +version = "1.0.218" authors = ["Erick Tryzelaar ", "David Tolnay "] build = "build.rs" categories = ["encoding", "no-std", "no-std::no-alloc"] @@ -20,16 +20,18 @@ serde_derive = { version = "1", optional = true, path = "../serde_derive" } [dev-dependencies] serde_derive = { version = "1", path = "../serde_derive" } -[lib] -doc-scrape-examples = false - [package.metadata.playground] features = ["derive", "rc"] [package.metadata.docs.rs] -features = ["derive"] +features = ["derive", "rc", "unstable"] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] # This cfg cannot be enabled, but it still forces Cargo to keep serde_derive's # version in lockstep with serde's, even if someone depends on the two crates @@ -37,7 +39,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] -serde_derive = { version = "=1.0.188", path = "../serde_derive" } +serde_derive = { version = "=1.0.218", path = "../serde_derive" } ### FEATURES ################################################################# diff --git a/serde/build.rs b/serde/build.rs index 2b10c5893..24aa0e6d2 100644 --- a/serde/build.rs +++ b/serde/build.rs @@ -1,6 +1,6 @@ use std::env; use std::process::Command; -use std::str::{self, FromStr}; +use std::str; // The rustc-cfg strings below are *not* public API. Please let us know by // opening a GitHub issue if your build environment requires some way to enable @@ -13,6 +13,23 @@ fn main() { None => return, }; + if minor >= 77 { + println!("cargo:rustc-check-cfg=cfg(no_core_cstr)"); + println!("cargo:rustc-check-cfg=cfg(no_core_error)"); + println!("cargo:rustc-check-cfg=cfg(no_core_net)"); + println!("cargo:rustc-check-cfg=cfg(no_core_num_saturating)"); + println!("cargo:rustc-check-cfg=cfg(no_core_try_from)"); + println!("cargo:rustc-check-cfg=cfg(no_diagnostic_namespace)"); + println!("cargo:rustc-check-cfg=cfg(no_float_copysign)"); + println!("cargo:rustc-check-cfg=cfg(no_num_nonzero_signed)"); + println!("cargo:rustc-check-cfg=cfg(no_relaxed_trait_bounds)"); + println!("cargo:rustc-check-cfg=cfg(no_serde_derive)"); + println!("cargo:rustc-check-cfg=cfg(no_std_atomic)"); + println!("cargo:rustc-check-cfg=cfg(no_std_atomic64)"); + println!("cargo:rustc-check-cfg=cfg(no_systemtime_checked_add)"); + println!("cargo:rustc-check-cfg=cfg(no_target_has_atomic)"); + } + let target = env::var("TARGET").unwrap(); let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten"; @@ -27,9 +44,10 @@ fn main() { println!("cargo:rustc-cfg=no_relaxed_trait_bounds"); } - // Current minimum supported version of serde_derive crate is Rust 1.56. - if minor < 56 { - println!("cargo:rustc-cfg=no_serde_derive"); + // f32::copysign and f64::copysign stabilized in Rust 1.35. + // https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html#copy-the-sign-of-a-floating-point-number-onto-another + if minor < 35 { + println!("cargo:rustc-cfg=no_float_copysign"); } // Support for #[cfg(target_has_atomic = "...")] stabilized in Rust 1.60. @@ -53,38 +71,49 @@ fn main() { } } + // Current minimum supported version of serde_derive crate is Rust 1.61. + if minor < 61 { + println!("cargo:rustc-cfg=no_serde_derive"); + } + // 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 { - let rustc = match env::var_os("RUSTC") { - Some(rustc) => rustc, - None => return None, - }; + // Support for core::num::Saturating and std::num::Saturating stabilized in Rust 1.74 + // https://blog.rust-lang.org/2023/11/16/Rust-1.74.0.html#stabilized-apis + if minor < 74 { + println!("cargo:rustc-cfg=no_core_num_saturating"); + } - let output = match Command::new(rustc).arg("--version").output() { - Ok(output) => output, - Err(_) => return None, - }; + // Support for core::net stabilized in Rust 1.77. + // https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html + if minor < 77 { + println!("cargo:rustc-cfg=no_core_net"); + } - let version = match str::from_utf8(&output.stdout) { - Ok(version) => version, - Err(_) => return None, - }; + // Support for the `#[diagnostic]` tool attribute namespace + // https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#diagnostic-attributes + if minor < 78 { + println!("cargo:rustc-cfg=no_diagnostic_namespace"); + } + + // The Error trait became available in core in 1.81. + // https://blog.rust-lang.org/2024/09/05/Rust-1.81.0.html#coreerrorerror + if minor < 81 { + println!("cargo:rustc-cfg=no_core_error"); + } +} +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; let mut pieces = version.split('.'); if pieces.next() != Some("rustc 1") { return None; } - - let next = match pieces.next() { - Some(next) => next, - None => return None, - }; - - u32::from_str(next).ok() + pieces.next()?.parse().ok() } diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index fbee1554b..2d8c99030 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -39,6 +39,7 @@ impl<'de> Deserialize<'de> for () { } #[cfg(feature = "unstable")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl<'de> Deserialize<'de> for ! { fn deserialize(_deserializer: D) -> Result where @@ -103,6 +104,28 @@ macro_rules! impl_deserialize_num { deserializer.$deserialize(NonZeroVisitor) } } + + #[cfg(not(no_core_num_saturating))] + impl<'de> Deserialize<'de> for Saturating<$primitive> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SaturatingVisitor; + + impl<'de> Visitor<'de> for SaturatingVisitor { + type Value = Saturating<$primitive>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("integer with support for saturating semantics") + } + + $($($method!(saturating $primitive $val : $visit);)*)* + } + + deserializer.$deserialize(SaturatingVisitor) + } + } }; ($primitive:ident, $deserialize:ident $($method:ident!($($val:ident : $visit:ident)*);)*) => { @@ -153,6 +176,15 @@ macro_rules! num_self { } } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + Ok(Saturating(v)) + } + }; } macro_rules! num_as_self { @@ -178,6 +210,37 @@ macro_rules! num_as_self { } } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + Ok(Saturating(v as $primitive)) + } + }; +} + +macro_rules! num_as_copysign_self { + ($ty:ident : $visit:ident) => { + #[inline] + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + #[cfg(any(no_float_copysign, not(feature = "std")))] + { + Ok(v as Self::Value) + } + + #[cfg(all(not(no_float_copysign), feature = "std"))] + { + // Preserve sign of NaN. The `as` produces a nondeterministic sign. + let sign = if v.is_sign_positive() { 1.0 } else { -1.0 }; + Ok((v as Self::Value).copysign(sign)) + } + } + }; } macro_rules! int_to_int { @@ -212,6 +275,21 @@ macro_rules! int_to_int { Err(Error::invalid_value(Unexpected::Signed(v as i64), &self)) } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + if (v as i64) < $primitive::MIN as i64 { + Ok(Saturating($primitive::MIN)) + } else if ($primitive::MAX as i64) < v as i64 { + Ok(Saturating($primitive::MAX)) + } else { + Ok(Saturating(v as $primitive)) + } + } + }; } macro_rules! int_to_uint { @@ -242,6 +320,21 @@ macro_rules! int_to_uint { Err(Error::invalid_value(Unexpected::Signed(v as i64), &self)) } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + if v < 0 { + Ok(Saturating(0)) + } else if ($primitive::MAX as u64) < v as u64 { + Ok(Saturating($primitive::MAX)) + } else { + Ok(Saturating(v as $primitive)) + } + } + }; } macro_rules! uint_to_self { @@ -272,6 +365,19 @@ macro_rules! uint_to_self { Err(Error::invalid_value(Unexpected::Unsigned(v as u64), &self)) } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + if v as u64 <= $primitive::MAX as u64 { + Ok(Saturating(v as $primitive)) + } else { + Ok(Saturating($primitive::MAX)) + } + } + }; } impl_deserialize_num! { @@ -351,7 +457,7 @@ impl_deserialize_num! { impl_deserialize_num! { f32, deserialize_f32 num_self!(f32:visit_f32); - num_as_self!(f64:visit_f64); + num_as_copysign_self!(f64:visit_f64); num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64); num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } @@ -359,7 +465,7 @@ impl_deserialize_num! { impl_deserialize_num! { f64, deserialize_f64 num_self!(f64:visit_f64); - num_as_self!(f32:visit_f32); + num_as_copysign_self!(f32:visit_f32); num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64); num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } @@ -404,6 +510,21 @@ macro_rules! num_128 { } } }; + + (saturating $primitive:ident $ty:ident : $visit:ident) => { + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + if (v as i128) < $primitive::MIN as i128 { + Ok(Saturating($primitive::MIN)) + } else if ($primitive::MAX as u128) < v as u128 { + Ok(Saturating($primitive::MAX)) + } else { + Ok(Saturating(v as $primitive)) + } + } + }; } impl_deserialize_num! { @@ -574,6 +695,7 @@ impl<'a, 'de> Visitor<'de> for StringInPlaceVisitor<'a> { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de> Deserialize<'de> for String { fn deserialize(deserializer: D) -> Result where @@ -717,6 +839,7 @@ impl<'de> Visitor<'de> for CStringVisitor { } #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de> Deserialize<'de> for CString { fn deserialize(deserializer: D) -> Result where @@ -728,10 +851,10 @@ impl<'de> Deserialize<'de> for CString { macro_rules! forwarded_impl { ( - $(#[doc = $doc:tt])* + $(#[$attr:meta])* ($($id:ident),*), $ty:ty, $func:expr ) => { - $(#[doc = $doc])* + $(#[$attr])* impl<'de $(, $id : Deserialize<'de>,)*> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -743,10 +866,15 @@ macro_rules! forwarded_impl { } } -#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] -forwarded_impl!((), Box, CString::into_boxed_c_str); +forwarded_impl! { + #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + (), Box, CString::into_boxed_c_str +} -forwarded_impl!((T), Reverse, Reverse); +forwarded_impl! { + (T), Reverse, Reverse +} //////////////////////////////////////////////////////////////////////////////// @@ -822,7 +950,10 @@ struct PhantomDataVisitor { marker: PhantomData, } -impl<'de, T: ?Sized> Visitor<'de> for PhantomDataVisitor { +impl<'de, T> Visitor<'de> for PhantomDataVisitor +where + T: ?Sized, +{ type Value = PhantomData; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -838,7 +969,10 @@ impl<'de, T: ?Sized> Visitor<'de> for PhantomDataVisitor { } } -impl<'de, T: ?Sized> Deserialize<'de> for PhantomData { +impl<'de, T> Deserialize<'de> for PhantomData +where + T: ?Sized, +{ fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -852,9 +986,9 @@ impl<'de, T: ?Sized> Deserialize<'de> for PhantomData { //////////////////////////////////////////////////////////////////////////////// -#[cfg(any(feature = "std", feature = "alloc"))] macro_rules! seq_impl { ( + $(#[$attr:meta])* $ty:ident , $access:ident, $clear:expr, @@ -862,6 +996,7 @@ macro_rules! seq_impl { $reserve:expr, $insert:expr ) => { + $(#[$attr])* impl<'de, T $(, $typaram)*> Deserialize<'de> for $ty where T: Deserialize<'de> $(+ $tbound1 $(+ $tbound2)*)*, @@ -949,8 +1084,9 @@ macro_rules! seq_impl { #[cfg(any(feature = "std", feature = "alloc"))] fn nop_reserve(_seq: T, _n: usize) {} -#[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BinaryHeap, seq, BinaryHeap::clear, @@ -959,8 +1095,9 @@ seq_impl!( BinaryHeap::push ); -#[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BTreeSet, seq, BTreeSet::clear, @@ -969,8 +1106,9 @@ seq_impl!( BTreeSet::insert ); -#[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] LinkedList, seq, LinkedList::clear, @@ -979,8 +1117,9 @@ seq_impl!( LinkedList::push_back ); -#[cfg(feature = "std")] seq_impl!( + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] HashSet, seq, HashSet::clear, @@ -989,8 +1128,9 @@ seq_impl!( HashSet::insert ); -#[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] VecDeque, seq, VecDeque::clear, @@ -1002,6 +1142,7 @@ seq_impl!( //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, T> Deserialize<'de> for Vec where T: Deserialize<'de>, @@ -1252,82 +1393,103 @@ array_impls! { macro_rules! tuple_impls { ($($len:tt => ($($n:tt $name:ident)+))+) => { $( - impl<'de, $($name: Deserialize<'de>),+> Deserialize<'de> for ($($name,)+) { + #[cfg_attr(docsrs, doc(hidden))] + impl<'de, $($name),+> Deserialize<'de> for ($($name,)+) + where + $($name: Deserialize<'de>,)+ + { + tuple_impl_body!($len => ($($n $name)+)); + } + )+ + }; +} + +macro_rules! tuple_impl_body { + ($len:tt => ($($n:tt $name:ident)+)) => { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct TupleVisitor<$($name,)+> { + marker: PhantomData<($($name,)+)>, + } + + impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<$($name,)+> { + type Value = ($($name,)+); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("a tuple of size ", $len)) + } + #[inline] - fn deserialize(deserializer: D) -> Result + #[allow(non_snake_case)] + fn visit_seq(self, mut seq: A) -> Result where - D: Deserializer<'de>, + A: SeqAccess<'de>, { - struct TupleVisitor<$($name,)+> { - marker: PhantomData<($($name,)+)>, - } + $( + let $name = match tri!(seq.next_element()) { + Some(value) => value, + None => return Err(Error::invalid_length($n, &self)), + }; + )+ - impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<$($name,)+> { - type Value = ($($name,)+); + Ok(($($name,)+)) + } + } - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(concat!("a tuple of size ", $len)) - } + deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData }) + } - #[inline] - #[allow(non_snake_case)] - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - $( - let $name = match tri!(seq.next_element()) { - Some(value) => value, - None => return Err(Error::invalid_length($n, &self)), - }; - )+ + #[inline] + fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + struct TupleInPlaceVisitor<'a, $($name: 'a,)+>(&'a mut ($($name,)+)); - Ok(($($name,)+)) - } - } + impl<'a, 'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleInPlaceVisitor<'a, $($name,)+> { + type Value = (); - deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData }) + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("a tuple of size ", $len)) } #[inline] - fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> + #[allow(non_snake_case)] + fn visit_seq(self, mut seq: A) -> Result where - D: Deserializer<'de>, + A: SeqAccess<'de>, { - struct TupleInPlaceVisitor<'a, $($name: 'a,)+>(&'a mut ($($name,)+)); - - impl<'a, 'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleInPlaceVisitor<'a, $($name,)+> { - type Value = (); - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(concat!("a tuple of size ", $len)) - } - - #[inline] - #[allow(non_snake_case)] - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - $( - if tri!(seq.next_element_seed(InPlaceSeed(&mut (self.0).$n))).is_none() { - return Err(Error::invalid_length($n, &self)); - } - )+ - - Ok(()) + $( + if tri!(seq.next_element_seed(InPlaceSeed(&mut (self.0).$n))).is_none() { + return Err(Error::invalid_length($n, &self)); } - } + )+ - deserializer.deserialize_tuple($len, TupleInPlaceVisitor(place)) + Ok(()) } } - )+ - } + + deserializer.deserialize_tuple($len, TupleInPlaceVisitor(place)) + } + }; +} + +#[cfg_attr(docsrs, doc(fake_variadic))] +#[cfg_attr( + docsrs, + doc = "This trait is implemented for tuples up to 16 items long." +)] +impl<'de, T> Deserialize<'de> for (T,) +where + T: Deserialize<'de>, +{ + tuple_impl_body!(1 => (0 T)); } tuple_impls! { - 1 => (0 T0) 2 => (0 T0 1 T1) 3 => (0 T0 1 T1 2 T2) 4 => (0 T0 1 T1 2 T2 3 T3) @@ -1347,13 +1509,14 @@ tuple_impls! { //////////////////////////////////////////////////////////////////////////////// -#[cfg(any(feature = "std", feature = "alloc"))] macro_rules! map_impl { ( + $(#[$attr:meta])* $ty:ident , $access:ident, - $with_capacity:expr + $with_capacity:expr, ) => { + $(#[$attr])* impl<'de, K, V $(, $typaram)*> Deserialize<'de> for $ty where K: Deserialize<'de> $(+ $kbound1 $(+ $kbound2)*)*, @@ -1402,21 +1565,27 @@ macro_rules! map_impl { } } -#[cfg(any(feature = "std", feature = "alloc"))] -map_impl!(BTreeMap, map, BTreeMap::new()); +map_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BTreeMap, + map, + BTreeMap::new(), +} -#[cfg(feature = "std")] -map_impl!( +map_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] HashMap, map, - HashMap::with_capacity_and_hasher(size_hint::cautious::<(K, V)>(map.size_hint()), S::default()) -); + HashMap::with_capacity_and_hasher(size_hint::cautious::<(K, V)>(map.size_hint()), S::default()), +} //////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! parse_ip_impl { - ($expecting:tt $ty:ty; $size:tt) => { + ($ty:ty, $expecting:expr, $size:tt) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -1432,7 +1601,7 @@ macro_rules! parse_ip_impl { }; } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! variant_identifier { ( $name_kind:ident ($($variant:ident; $bytes:expr; $index:expr),*) @@ -1507,7 +1676,7 @@ macro_rules! variant_identifier { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! deserialize_enum { ( $name:ident $name_kind:ident ($($variant:ident; $bytes:expr; $index:expr),*) @@ -1544,7 +1713,7 @@ macro_rules! deserialize_enum { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl<'de> Deserialize<'de> for net::IpAddr { fn deserialize(deserializer: D) -> Result where @@ -1563,15 +1732,18 @@ impl<'de> Deserialize<'de> for net::IpAddr { } } -#[cfg(feature = "std")] -parse_ip_impl!("IPv4 address" net::Ipv4Addr; 4); +#[cfg(any(feature = "std", not(no_core_net)))] +parse_ip_impl!(net::Ipv4Addr, "IPv4 address", 4); -#[cfg(feature = "std")] -parse_ip_impl!("IPv6 address" net::Ipv6Addr; 16); +#[cfg(any(feature = "std", not(no_core_net)))] +parse_ip_impl!(net::Ipv6Addr, "IPv6 address", 16); -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! parse_socket_impl { - ($expecting:tt $ty:ty, $new:expr) => { + ( + $ty:ty, $expecting:tt, + $new:expr, + ) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -1587,7 +1759,7 @@ macro_rules! parse_socket_impl { }; } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl<'de> Deserialize<'de> for net::SocketAddr { fn deserialize(deserializer: D) -> Result where @@ -1606,11 +1778,17 @@ impl<'de> Deserialize<'de> for net::SocketAddr { } } -#[cfg(feature = "std")] -parse_socket_impl!("IPv4 socket address" net::SocketAddrV4, |(ip, port)| net::SocketAddrV4::new(ip, port)); +#[cfg(any(feature = "std", not(no_core_net)))] +parse_socket_impl! { + net::SocketAddrV4, "IPv4 socket address", + |(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)); +#[cfg(any(feature = "std", not(no_core_net)))] +parse_socket_impl! { + net::SocketAddrV6, "IPv6 socket address", + |(ip, port)| net::SocketAddrV6::new(ip, port, 0, 0), +} //////////////////////////////////////////////////////////////////////////////// @@ -1643,6 +1821,7 @@ impl<'a> Visitor<'a> for PathVisitor { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de: 'a, 'a> Deserialize<'de> for &'a Path { fn deserialize(deserializer: D) -> Result where @@ -1697,6 +1876,7 @@ impl<'de> Visitor<'de> for PathBufVisitor { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de> Deserialize<'de> for PathBuf { fn deserialize(deserializer: D) -> Result where @@ -1706,8 +1886,11 @@ impl<'de> Deserialize<'de> for PathBuf { } } -#[cfg(feature = "std")] -forwarded_impl!((), Box, PathBuf::into_boxed_path); +forwarded_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + (), Box, PathBuf::into_boxed_path +} //////////////////////////////////////////////////////////////////////////////// @@ -1767,6 +1950,7 @@ impl<'de> Visitor<'de> for OsStringVisitor { } #[cfg(all(feature = "std", any(unix, windows)))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", any(unix, windows)))))] impl<'de> Deserialize<'de> for OsString { fn deserialize(deserializer: D) -> Result where @@ -1778,22 +1962,35 @@ impl<'de> Deserialize<'de> for OsString { //////////////////////////////////////////////////////////////////////////////// -#[cfg(any(feature = "std", feature = "alloc"))] -forwarded_impl!((T), Box, Box::new); +forwarded_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + (T), Box, Box::new +} -#[cfg(any(feature = "std", feature = "alloc"))] -forwarded_impl!((T), Box<[T]>, Vec::into_boxed_slice); +forwarded_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + (T), Box<[T]>, Vec::into_boxed_slice +} -#[cfg(any(feature = "std", feature = "alloc"))] -forwarded_impl!((), Box, String::into_boxed_str); +forwarded_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + (), Box, String::into_boxed_str +} -#[cfg(all(feature = "std", any(unix, windows)))] -forwarded_impl!((), Box, OsString::into_boxed_os_str); +forwarded_impl! { + #[cfg(all(feature = "std", any(unix, windows)))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "std", any(unix, windows)))))] + (), Box, OsString::into_boxed_os_str +} #[cfg(any(feature = "std", feature = "alloc"))] -impl<'de, 'a, T: ?Sized> Deserialize<'de> for Cow<'a, T> +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl<'de, 'a, T> Deserialize<'de> for Cow<'a, T> where - T: ToOwned, + T: ?Sized + ToOwned, T::Owned: Deserialize<'de>, { #[inline] @@ -1812,7 +2009,11 @@ where /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] -impl<'de, T: ?Sized> Deserialize<'de> for RcWeak +#[cfg_attr( + docsrs, + doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) +)] +impl<'de, T> Deserialize<'de> for RcWeak where T: Deserialize<'de>, { @@ -1830,7 +2031,11 @@ where /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] -impl<'de, T: ?Sized> Deserialize<'de> for ArcWeak +#[cfg_attr( + docsrs, + doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) +)] +impl<'de, T> Deserialize<'de> for ArcWeak where T: Deserialize<'de>, { @@ -1845,15 +2050,15 @@ where //////////////////////////////////////////////////////////////////////////////// -#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] macro_rules! box_forwarded_impl { ( - $(#[doc = $doc:tt])* + $(#[$attr:meta])* $t:ident ) => { - $(#[doc = $doc])* - impl<'de, T: ?Sized> Deserialize<'de> for $t + $(#[$attr])* + impl<'de, T> Deserialize<'de> for $t where + T: ?Sized, Box: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result @@ -1866,7 +2071,6 @@ macro_rules! box_forwarded_impl { }; } -#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] box_forwarded_impl! { /// This impl requires the [`"rc"`] Cargo feature of Serde. /// @@ -1875,10 +2079,11 @@ box_forwarded_impl! { /// will end up with a strong count of 1. /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc + #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] Rc } -#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] box_forwarded_impl! { /// This impl requires the [`"rc"`] Cargo feature of Serde. /// @@ -1887,6 +2092,8 @@ box_forwarded_impl! { /// will end up with a strong count of 1. /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc + #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] Arc } @@ -1904,13 +2111,21 @@ where } } -forwarded_impl!((T), RefCell, RefCell::new); +forwarded_impl! { + (T), RefCell, RefCell::new +} -#[cfg(feature = "std")] -forwarded_impl!((T), Mutex, Mutex::new); +forwarded_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + (T), Mutex, Mutex::new +} -#[cfg(feature = "std")] -forwarded_impl!((T), RwLock, RwLock::new); +forwarded_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + (T), RwLock, RwLock::new +} //////////////////////////////////////////////////////////////////////////////// @@ -2063,6 +2278,7 @@ impl<'de> Deserialize<'de> for Duration { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de> Deserialize<'de> for SystemTime { fn deserialize(deserializer: D) -> Result where @@ -2422,14 +2638,14 @@ mod range_from { use crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; - pub const FIELDS: &[&str] = &["end"]; + pub const FIELDS: &[&str] = &["start"]; // If this were outside of the serde crate, it would just use: // // #[derive(Deserialize)] // #[serde(field_identifier, rename_all = "lowercase")] enum Field { - End, + Start, } impl<'de> Deserialize<'de> for Field { @@ -2443,7 +2659,7 @@ mod range_from { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("`end`") + formatter.write_str("`start`") } fn visit_str(self, value: &str) -> Result @@ -2451,7 +2667,7 @@ mod range_from { E: Error, { match value { - "end" => Ok(Field::End), + "start" => Ok(Field::Start), _ => Err(Error::unknown_field(value, FIELDS)), } } @@ -2461,7 +2677,7 @@ mod range_from { E: Error, { match value { - b"end" => Ok(Field::End), + b"start" => Ok(Field::Start), _ => { let value = crate::__private::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) @@ -2493,35 +2709,35 @@ mod range_from { where A: SeqAccess<'de>, { - let end: Idx = match tri!(seq.next_element()) { + let start: Idx = match tri!(seq.next_element()) { Some(value) => value, None => { return Err(Error::invalid_length(0, &self)); } }; - Ok(end) + Ok(start) } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { - let mut end: Option = None; + let mut start: Option = None; while let Some(key) = tri!(map.next_key()) { match key { - Field::End => { - if end.is_some() { - return Err(::duplicate_field("end")); + Field::Start => { + if start.is_some() { + return Err(::duplicate_field("start")); } - end = Some(tri!(map.next_value())); + start = Some(tri!(map.next_value())); } } } - let end = match end { - Some(end) => end, - None => return Err(::missing_field("end")), + let start = match start { + Some(start) => start, + None => return Err(::missing_field("start")), }; - Ok(end) + Ok(start) } } } @@ -2533,7 +2749,7 @@ mod range_from { // #[derive(Deserialize)] // #[serde(deny_unknown_fields)] // struct RangeTo { -// start: Idx, +// end: Idx, // } impl<'de, Idx> Deserialize<'de> for RangeTo where @@ -2560,14 +2776,14 @@ mod range_to { use crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; - pub const FIELDS: &[&str] = &["start"]; + pub const FIELDS: &[&str] = &["end"]; // If this were outside of the serde crate, it would just use: // // #[derive(Deserialize)] // #[serde(field_identifier, rename_all = "lowercase")] enum Field { - Start, + End, } impl<'de> Deserialize<'de> for Field { @@ -2581,7 +2797,7 @@ mod range_to { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("`start`") + formatter.write_str("`end`") } fn visit_str(self, value: &str) -> Result @@ -2589,7 +2805,7 @@ mod range_to { E: Error, { match value { - "start" => Ok(Field::Start), + "end" => Ok(Field::End), _ => Err(Error::unknown_field(value, FIELDS)), } } @@ -2599,7 +2815,7 @@ mod range_to { E: Error, { match value { - b"start" => Ok(Field::Start), + b"end" => Ok(Field::End), _ => { let value = crate::__private::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) @@ -2631,35 +2847,35 @@ mod range_to { where A: SeqAccess<'de>, { - let start: Idx = match tri!(seq.next_element()) { + let end: Idx = match tri!(seq.next_element()) { Some(value) => value, None => { return Err(Error::invalid_length(0, &self)); } }; - Ok(start) + Ok(end) } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { - let mut start: Option = None; + let mut end: Option = None; while let Some(key) = tri!(map.next_key()) { match key { - Field::Start => { - if start.is_some() { - return Err(::duplicate_field("start")); + Field::End => { + if end.is_some() { + return Err(::duplicate_field("end")); } - start = Some(tri!(map.next_value())); + end = Some(tri!(map.next_value())); } } } - let start = match start { - Some(start) => start, - None => return Err(::missing_field("start")), + let end = match end { + Some(end) => end, + None => return Err(::missing_field("end")), }; - Ok(start) + Ok(end) } } } @@ -2898,6 +3114,7 @@ macro_rules! atomic_impl { ($($ty:ident $size:expr)*) => { $( #[cfg(any(no_target_has_atomic, target_has_atomic = $size))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_has_atomic = $size))))] impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -2929,13 +3146,13 @@ atomic_impl! { AtomicU64 "64" } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] struct FromStrVisitor { expecting: &'static str, ty: PhantomData, } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl FromStrVisitor { fn new(expecting: &'static str) -> Self { FromStrVisitor { @@ -2945,7 +3162,7 @@ impl FromStrVisitor { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl<'de, T> Visitor<'de> for FromStrVisitor where T: str::FromStr, diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index afbc23af6..540632f4a 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -64,8 +64,8 @@ //! - RefCell\ //! - Mutex\ //! - RwLock\ -//! - Rc\ *(if* features = ["rc"] *is enabled)* -//! - Arc\ *(if* features = ["rc"] *is enabled)* +//! - Rc\ *(if* features = \["rc"\] *is enabled)* +//! - Arc\ *(if* features = \["rc"\] *is enabled)* //! - **Collection types**: //! - BTreeMap\ //! - BTreeSet\ @@ -101,8 +101,8 @@ //! - SocketAddrV6 //! //! [Implementing `Deserialize`]: https://serde.rs/impl-deserialize.html -//! [`Deserialize`]: ../trait.Deserialize.html -//! [`Deserializer`]: ../trait.Deserializer.html +//! [`Deserialize`]: crate::Deserialize +//! [`Deserializer`]: crate::Deserializer //! [`LinkedHashMap`]: https://docs.rs/linked-hash-map/*/linked_hash_map/struct.LinkedHashMap.html //! [`postcard`]: https://github.com/jamesmunns/postcard //! [`linked-hash-map`]: https://crates.io/crates/linked-hash-map @@ -118,17 +118,16 @@ use crate::lib::*; pub mod value; -mod format; mod ignored_any; mod impls; pub(crate) mod size_hint; pub use self::ignored_any::IgnoredAny; -#[cfg(not(any(feature = "std", feature = "unstable")))] +#[cfg(all(not(feature = "std"), no_core_error))] #[doc(no_inline)] pub use crate::std_error::Error as StdError; -#[cfg(all(feature = "unstable", not(feature = "std")))] +#[cfg(not(any(feature = "std", no_core_error)))] #[doc(no_inline)] pub use core::error::Error as StdError; #[cfg(feature = "std")] @@ -402,20 +401,20 @@ impl<'a> fmt::Display for Unexpected<'a> { Bool(b) => write!(formatter, "boolean `{}`", b), Unsigned(i) => write!(formatter, "integer `{}`", i), Signed(i) => write!(formatter, "integer `{}`", i), - Float(f) => write!(formatter, "floating point `{}`", f), + Float(f) => write!(formatter, "floating point `{}`", WithDecimalPoint(f)), Char(c) => write!(formatter, "character `{}`", c), Str(s) => write!(formatter, "string {:?}", s), - Bytes(_) => write!(formatter, "byte array"), - Unit => write!(formatter, "unit value"), - Option => write!(formatter, "Option value"), - NewtypeStruct => write!(formatter, "newtype struct"), - Seq => write!(formatter, "sequence"), - Map => write!(formatter, "map"), - Enum => write!(formatter, "enum"), - UnitVariant => write!(formatter, "unit variant"), - NewtypeVariant => write!(formatter, "newtype variant"), - TupleVariant => write!(formatter, "tuple variant"), - StructVariant => write!(formatter, "struct variant"), + Bytes(_) => formatter.write_str("byte array"), + Unit => formatter.write_str("unit value"), + Option => formatter.write_str("Option value"), + NewtypeStruct => formatter.write_str("newtype struct"), + Seq => formatter.write_str("sequence"), + Map => formatter.write_str("map"), + Enum => formatter.write_str("enum"), + UnitVariant => formatter.write_str("unit variant"), + NewtypeVariant => formatter.write_str("newtype variant"), + TupleVariant => formatter.write_str("tuple variant"), + StructVariant => formatter.write_str("struct variant"), Other(other) => formatter.write_str(other), } } @@ -487,13 +486,13 @@ where } } -impl<'a> Expected for &'a str { +impl Expected for &str { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(self) } } -impl<'a> Display for Expected + 'a { +impl Display for Expected + '_ { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { Expected::fmt(self, formatter) } @@ -532,6 +531,13 @@ impl<'a> Display for Expected + 'a { /// deserializer lifetimes] for a more detailed explanation of these lifetimes. /// /// [Understanding deserializer lifetimes]: https://serde.rs/lifetimes.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + note = "for local types consider adding `#[derive(serde::Deserialize)]` to your `{Self}` type", + note = "for types from other crates check whether the crate offers a `serde` feature flag", + ) +)] pub trait Deserialize<'de>: Sized { /// Deserialize this value from the given Serde deserializer. /// @@ -1367,7 +1373,7 @@ pub trait Visitor<'de>: Sized { E: Error, { let mut buf = [0u8; 58]; - let mut writer = format::Buf::new(&mut buf); + let mut writer = crate::format::Buf::new(&mut buf); fmt::Write::write_fmt(&mut writer, format_args!("integer `{}` as i128", v)).unwrap(); Err(Error::invalid_type( Unexpected::Other(writer.as_str()), @@ -1429,7 +1435,7 @@ pub trait Visitor<'de>: Sized { E: Error, { let mut buf = [0u8; 57]; - let mut writer = format::Buf::new(&mut buf); + let mut writer = crate::format::Buf::new(&mut buf); fmt::Write::write_fmt(&mut writer, format_args!("integer `{}` as u128", v)).unwrap(); Err(Error::invalid_type( Unexpected::Other(writer.as_str()), @@ -1525,6 +1531,7 @@ pub trait Visitor<'de>: Sized { /// `String`. #[inline] #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] fn visit_string(self, v: String) -> Result where E: Error, @@ -1583,6 +1590,7 @@ pub trait Visitor<'de>: Sized { /// The default implementation forwards to `visit_bytes` and then drops the /// `Vec`. #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] fn visit_byte_buf(self, v: Vec) -> Result where E: Error, @@ -1733,9 +1741,9 @@ pub trait SeqAccess<'de> { } } -impl<'de, 'a, A: ?Sized> SeqAccess<'de> for &'a mut A +impl<'de, A> SeqAccess<'de> for &mut A where - A: SeqAccess<'de>, + A: ?Sized + SeqAccess<'de>, { type Error = A::Error; @@ -1886,9 +1894,9 @@ pub trait MapAccess<'de> { } } -impl<'de, 'a, A: ?Sized> MapAccess<'de> for &'a mut A +impl<'de, A> MapAccess<'de> for &mut A where - A: MapAccess<'de>, + A: ?Sized + MapAccess<'de>, { type Error = A::Error; @@ -2276,10 +2284,10 @@ impl Display for OneOf { 1 => write!(formatter, "`{}`", self.names[0]), 2 => write!(formatter, "`{}` or `{}`", self.names[0], self.names[1]), _ => { - tri!(write!(formatter, "one of ")); + tri!(formatter.write_str("one of ")); for (i, alt) in self.names.iter().enumerate() { if i > 0 { - tri!(write!(formatter, ", ")); + tri!(formatter.write_str(", ")); } tri!(write!(formatter, "`{}`", alt)); } @@ -2288,3 +2296,40 @@ impl Display for OneOf { } } } + +struct WithDecimalPoint(f64); + +impl Display for WithDecimalPoint { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + struct LookForDecimalPoint<'f, 'a> { + formatter: &'f mut fmt::Formatter<'a>, + has_decimal_point: bool, + } + + impl<'f, 'a> fmt::Write for LookForDecimalPoint<'f, 'a> { + fn write_str(&mut self, fragment: &str) -> fmt::Result { + self.has_decimal_point |= fragment.contains('.'); + self.formatter.write_str(fragment) + } + + fn write_char(&mut self, ch: char) -> fmt::Result { + self.has_decimal_point |= ch == '.'; + self.formatter.write_char(ch) + } + } + + if self.0.is_finite() { + let mut writer = LookForDecimalPoint { + formatter, + has_decimal_point: false, + }; + tri!(write!(writer, "{}", self.0)); + if !writer.has_decimal_point { + tri!(formatter.write_str(".0")); + } + } else { + tri!(write!(formatter, "{}", self.0)); + } + Ok(()) + } +} diff --git a/serde/src/de/value.rs b/serde/src/de/value.rs index 1234b8103..8f9c0b48e 100644 --- a/serde/src/de/value.rs +++ b/serde/src/de/value.rs @@ -112,6 +112,7 @@ impl Debug for Error { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl error::Error for Error { fn description(&self) -> &str { &self.err @@ -174,6 +175,17 @@ where } } +impl<'de, E> IntoDeserializer<'de, E> for UnitDeserializer +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl Debug for UnitDeserializer { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.debug_struct("UnitDeserializer").finish() @@ -184,12 +196,14 @@ impl Debug for UnitDeserializer { /// A deserializer that cannot be instantiated. #[cfg(feature = "unstable")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] pub struct NeverDeserializer { never: !, marker: PhantomData, } #[cfg(feature = "unstable")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl<'de, E> IntoDeserializer<'de, E> for ! where E: de::Error, @@ -222,6 +236,18 @@ where } } +#[cfg(feature = "unstable")] +impl<'de, E> IntoDeserializer<'de, E> for NeverDeserializer +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + //////////////////////////////////////////////////////////////////////////////// macro_rules! primitive_deserializer { @@ -276,6 +302,17 @@ macro_rules! primitive_deserializer { } } + impl<'de, E> IntoDeserializer<'de, E> for $name + where + E: de::Error, + { + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } + } + impl Debug for $name { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter @@ -366,6 +403,17 @@ where } } +impl<'de, E> IntoDeserializer<'de, E> for U32Deserializer +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, E> de::EnumAccess<'de> for U32Deserializer where E: de::Error, @@ -455,6 +503,17 @@ where } } +impl<'de, 'a, E> IntoDeserializer<'de, E> for StrDeserializer<'a, E> +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, 'a, E> de::EnumAccess<'de> for StrDeserializer<'a, E> where E: de::Error, @@ -534,6 +593,17 @@ where } } +impl<'de, E> IntoDeserializer<'de, E> for BorrowedStrDeserializer<'de, E> +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, E> de::EnumAccess<'de> for BorrowedStrDeserializer<'de, E> where E: de::Error, @@ -562,6 +632,7 @@ impl<'de, E> Debug for BorrowedStrDeserializer<'de, E> { /// A deserializer holding a `String`. #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] pub struct StringDeserializer { value: String, marker: PhantomData, @@ -578,6 +649,7 @@ impl Clone for StringDeserializer { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, E> IntoDeserializer<'de, E> for String where E: de::Error, @@ -635,6 +707,18 @@ where } } +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'de, E> IntoDeserializer<'de, E> for StringDeserializer +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + #[cfg(any(feature = "std", feature = "alloc"))] impl<'de, E> de::EnumAccess<'de> for StringDeserializer where @@ -665,6 +749,7 @@ impl Debug for StringDeserializer { /// A deserializer holding a `Cow`. #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] pub struct CowStrDeserializer<'a, E> { value: Cow<'a, str>, marker: PhantomData, @@ -681,6 +766,7 @@ impl<'a, E> Clone for CowStrDeserializer<'a, E> { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, 'a, E> IntoDeserializer<'de, E> for Cow<'a, str> where E: de::Error, @@ -741,6 +827,18 @@ where } } +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'de, 'a, E> IntoDeserializer<'de, E> for CowStrDeserializer<'a, E> +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + #[cfg(any(feature = "std", feature = "alloc"))] impl<'de, 'a, E> de::EnumAccess<'de> for CowStrDeserializer<'a, E> where @@ -818,6 +916,17 @@ where } } +impl<'de, 'a, E> IntoDeserializer<'de, E> for BytesDeserializer<'a, E> +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'a, E> Debug for BytesDeserializer<'a, E> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter @@ -866,6 +975,17 @@ where } } +impl<'de, E> IntoDeserializer<'de, E> for BorrowedBytesDeserializer<'de, E> +where + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, E> Debug for BorrowedBytesDeserializer<'de, E> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter @@ -945,6 +1065,19 @@ where } } +impl<'de, I, T, E> IntoDeserializer<'de, E> for SeqDeserializer +where + I: Iterator, + T: IntoDeserializer<'de, E>, + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, I, T, E> de::SeqAccess<'de> for SeqDeserializer where I: Iterator, @@ -976,7 +1109,7 @@ struct ExpectedInSeq(usize); impl Expected for ExpectedInSeq { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { if self.0 == 1 { - write!(formatter, "1 element in sequence") + formatter.write_str("1 element in sequence") } else { write!(formatter, "{} elements in sequence", self.0) } @@ -999,6 +1132,7 @@ where //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, T, E> IntoDeserializer<'de, E> for Vec where T: IntoDeserializer<'de, E>, @@ -1012,6 +1146,7 @@ where } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, T, E> IntoDeserializer<'de, E> for BTreeSet where T: IntoDeserializer<'de, E> + Eq + Ord, @@ -1025,6 +1160,7 @@ where } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de, T, S, E> IntoDeserializer<'de, E> for HashSet where T: IntoDeserializer<'de, E> + Eq + Hash, @@ -1073,6 +1209,17 @@ where } } +impl<'de, A> IntoDeserializer<'de, A::Error> for SeqAccessDeserializer +where + A: de::SeqAccess<'de>, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + //////////////////////////////////////////////////////////////////////////////// /// A deserializer that iterates over a map. @@ -1187,6 +1334,21 @@ where } } +impl<'de, I, E> IntoDeserializer<'de, E> for MapDeserializer<'de, I, E> +where + I: Iterator, + I::Item: private::Pair, + First: IntoDeserializer<'de, E>, + Second: IntoDeserializer<'de, E>, + E: de::Error, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, I, E> de::MapAccess<'de> for MapDeserializer<'de, I, E> where I: Iterator, @@ -1401,7 +1563,7 @@ struct ExpectedInMap(usize); impl Expected for ExpectedInMap { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { if self.0 == 1 { - write!(formatter, "1 element in map") + formatter.write_str("1 element in map") } else { write!(formatter, "{} elements in map", self.0) } @@ -1411,6 +1573,7 @@ impl Expected for ExpectedInMap { //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de, K, V, E> IntoDeserializer<'de, E> for BTreeMap where K: IntoDeserializer<'de, E> + Eq + Ord, @@ -1425,6 +1588,7 @@ where } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de, K, V, S, E> IntoDeserializer<'de, E> for HashMap where K: IntoDeserializer<'de, E> + Eq + Hash, @@ -1486,6 +1650,17 @@ where } } +impl<'de, A> IntoDeserializer<'de, A::Error> for MapAccessDeserializer +where + A: de::MapAccess<'de>, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + impl<'de, A> de::EnumAccess<'de> for MapAccessDeserializer where A: de::MapAccess<'de>, @@ -1539,6 +1714,17 @@ where } } +impl<'de, A> IntoDeserializer<'de, A::Error> for EnumAccessDeserializer +where + A: de::EnumAccess<'de>, +{ + type Deserializer = Self; + + fn into_deserializer(self) -> Self { + self + } +} + //////////////////////////////////////////////////////////////////////////////// mod private { diff --git a/serde/src/de/format.rs b/serde/src/format.rs similarity index 100% rename from serde/src/de/format.rs rename to serde/src/format.rs diff --git a/serde/src/lib.rs b/serde/src/lib.rs index f7d445749..cba89efcf 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -63,6 +63,7 @@ //! and from DynamoDB. //! - [Hjson], a syntax extension to JSON designed around human reading and //! editing. *(deserialization only)* +//! - [CSV], Comma-separated values is a tabular text file format. //! //! [JSON]: https://github.com/serde-rs/json //! [Postcard]: https://github.com/jamesmunns/postcard @@ -89,18 +90,22 @@ //! [DynamoDB Items]: https://docs.rs/serde_dynamo //! [rusoto_dynamodb]: https://docs.rs/rusoto_dynamodb //! [Hjson]: https://github.com/Canop/deser-hjson +//! [CSV]: https://docs.rs/csv //////////////////////////////////////////////////////////////////////////////// // Serde types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/serde/1.0.188")] +#![doc(html_root_url = "https://docs.rs/serde/1.0.218")] // Support using Serde without the standard library! #![cfg_attr(not(feature = "std"), no_std)] +// Show which crate feature enables conditionally compiled APIs in documentation. +#![cfg_attr(docsrs, feature(doc_cfg, rustdoc_internals))] +#![cfg_attr(docsrs, allow(internal_features))] // Unstable functionality only if the user asks for it. For tracking and // discussion of these features please refer to this issue: // // https://github.com/serde-rs/serde/issues/812 -#![cfg_attr(feature = "unstable", feature(error_in_core, never_type))] +#![cfg_attr(feature = "unstable", feature(never_type))] #![allow(unknown_lints, bare_trait_objects, deprecated)] // Ignored clippy and clippy_pedantic lints #![allow( @@ -114,11 +119,11 @@ // integer and float ser/de requires these sorts of casts clippy::cast_possible_truncation, clippy::cast_possible_wrap, + clippy::cast_precision_loss, clippy::cast_sign_loss, // things are often more readable this way clippy::cast_lossless, clippy::module_name_repetitions, - clippy::option_if_let_else, clippy::single_match_else, clippy::type_complexity, clippy::use_self, @@ -127,6 +132,7 @@ clippy::derive_partial_eq_without_eq, clippy::enum_glob_use, clippy::explicit_auto_deref, + clippy::incompatible_msrv, clippy::let_underscore_untyped, clippy::map_err_ignore, clippy::new_without_default, @@ -138,6 +144,7 @@ clippy::too_many_lines, // preference clippy::doc_markdown, + clippy::needless_lifetimes, clippy::unseparated_literal_suffix, // false positive clippy::needless_doctest_main, @@ -166,22 +173,25 @@ mod lib { pub use std::*; } - pub use self::core::{cmp, iter, mem, num, ptr, slice, str}; pub use self::core::{f32, f64}; pub use self::core::{i16, i32, i64, i8, isize}; + pub use self::core::{iter, num, ptr, str}; pub use self::core::{u16, u32, u64, u8, usize}; + #[cfg(any(feature = "std", feature = "alloc"))] + pub use self::core::{cmp, mem, slice}; + pub use self::core::cell::{Cell, RefCell}; - pub use self::core::clone::{self, Clone}; + pub use self::core::clone; pub use self::core::cmp::Reverse; - pub use self::core::convert::{self, From, Into}; - pub use self::core::default::{self, Default}; - pub use self::core::fmt::{self, Debug, Display}; + pub use self::core::convert; + pub use self::core::default; + pub use self::core::fmt::{self, Debug, Display, Write as FmtWrite}; pub use self::core::marker::{self, PhantomData}; pub use self::core::num::Wrapping; pub use self::core::ops::{Bound, Range, RangeFrom, RangeInclusive, RangeTo}; - pub use self::core::option::{self, Option}; - pub use self::core::result::{self, Result}; + pub use self::core::option; + pub use self::core::result; pub use self::core::time::Duration; #[cfg(all(feature = "alloc", not(feature = "std")))] @@ -229,8 +239,13 @@ mod lib { #[cfg(feature = "std")] pub use std::ffi::CString; + #[cfg(all(not(no_core_net), not(feature = "std")))] + pub use self::core::net; + #[cfg(feature = "std")] + pub use std::net; + #[cfg(feature = "std")] - pub use std::{error, net}; + pub use std::error; #[cfg(feature = "std")] pub use std::collections::{HashMap, HashSet}; @@ -267,6 +282,9 @@ mod lib { pub use std::sync::atomic::{AtomicI64, AtomicU64}; #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "ptr"))] pub use std::sync::atomic::{AtomicIsize, AtomicUsize}; + + #[cfg(not(no_core_num_saturating))] + pub use self::core::num::Saturating; } // None of this crate's error handling needs the `From::from` error conversion @@ -293,6 +311,8 @@ mod integer128; pub mod de; pub mod ser; +mod format; + #[doc(inline)] pub use crate::de::{Deserialize, Deserializer}; #[doc(inline)] @@ -306,7 +326,7 @@ pub mod __private; #[path = "de/seed.rs"] mod seed; -#[cfg(not(any(feature = "std", feature = "unstable")))] +#[cfg(all(not(feature = "std"), no_core_error))] mod std_error; // Re-export #[derive(Serialize, Deserialize)]. @@ -319,6 +339,7 @@ extern crate serde_derive; /// Derive macro available if serde is built with `features = ["derive"]`. #[cfg(feature = "serde_derive")] +#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub use serde_derive::{Deserialize, Serialize}; #[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))] diff --git a/serde/src/macros.rs b/serde/src/macros.rs index a8fd85a37..82a1105b1 100644 --- a/serde/src/macros.rs +++ b/serde/src/macros.rs @@ -104,9 +104,9 @@ /// # } /// ``` /// -/// [`Deserializer`]: trait.Deserializer.html -/// [`Visitor`]: de/trait.Visitor.html -/// [`Deserializer::deserialize_any`]: trait.Deserializer.html#tymethod.deserialize_any +/// [`Deserializer`]: crate::Deserializer +/// [`Visitor`]: crate::de::Visitor +/// [`Deserializer::deserialize_any`]: crate::Deserializer::deserialize_any #[macro_export(local_inner_macros)] macro_rules! forward_to_deserialize_any { (<$visitor:ident: Visitor<$lifetime:tt>> $($func:ident)*) => { @@ -123,7 +123,7 @@ macro_rules! forward_to_deserialize_any { macro_rules! forward_to_deserialize_any_method { ($func:ident<$l:tt, $v:ident>($($arg:ident : $ty:ty),*)) => { #[inline] - fn $func<$v>(self, $($arg: $ty,)* visitor: $v) -> $crate::__private::Result<$v::Value, Self::Error> + fn $func<$v>(self, $($arg: $ty,)* visitor: $v) -> $crate::__private::Result<$v::Value, >::Error> where $v: $crate::de::Visitor<$l>, { diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 883e6909c..50ae6ed15 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -313,6 +313,19 @@ mod content { } } + impl<'a, 'de, E> de::IntoDeserializer<'de, E> for &'a Content<'de> + where + E: de::Error, + { + type Deserializer = ContentRefDeserializer<'a, 'de, E>; + + fn into_deserializer(self) -> Self::Deserializer { + ContentRefDeserializer::new(self) + } + } + + /// Used to capture data in [`Content`] from other deserializers. + /// Cannot capture externally tagged enums, `i128` and `u128`. struct ContentVisitor<'de> { value: PhantomData>, } @@ -474,14 +487,16 @@ mod content { where D: Deserializer<'de>, { - Deserialize::deserialize(deserializer).map(|v| Content::Some(Box::new(v))) + let v = tri!(Deserialize::deserialize(deserializer)); + Ok(Content::Some(Box::new(v))) } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { - Deserialize::deserialize(deserializer).map(|v| Content::Newtype(Box::new(v))) + let v = tri!(Deserialize::deserialize(deserializer)); + Ok(Content::Newtype(Box::new(v))) } fn visit_seq(self, mut visitor: V) -> Result @@ -528,6 +543,8 @@ mod content { Content(Content<'de>), } + /// Serves as a seed for deserializing a key of internally tagged enum. + /// Cannot capture externally tagged enums, `i128` and `u128`. struct TagOrContentVisitor<'de> { name: &'static str, value: PhantomData>, @@ -814,6 +831,9 @@ mod content { /// Used by generated code to deserialize an internally tagged enum. /// + /// Captures map or sequence from the original deserializer and searches + /// a tag in it (in case of sequence, tag is the first element of sequence). + /// /// Not public API. pub struct TaggedContentVisitor { tag_name: &'static str, @@ -897,7 +917,9 @@ mod content { /// Not public API. pub struct TagOrContentFieldVisitor { + /// Name of the tag field of the adjacently tagged enum pub tag: &'static str, + /// Name of the content field of the adjacently tagged enum pub content: &'static str, } @@ -972,7 +994,9 @@ mod content { /// Not public API. pub struct TagContentOtherFieldVisitor { + /// Name of the tag field of the adjacently tagged enum pub tag: &'static str, + /// Name of the content field of the adjacently tagged enum pub content: &'static str, } @@ -1087,8 +1111,7 @@ mod content { V: Visitor<'de>, E: de::Error, { - let seq = content.into_iter().map(ContentDeserializer::new); - let mut seq_visitor = SeqDeserializer::new(seq); + let mut seq_visitor = SeqDeserializer::new(content.into_iter()); let value = tri!(visitor.visit_seq(&mut seq_visitor)); tri!(seq_visitor.end()); Ok(value) @@ -1102,10 +1125,7 @@ mod content { V: Visitor<'de>, E: de::Error, { - let map = content - .into_iter() - .map(|(k, v)| (ContentDeserializer::new(k), ContentDeserializer::new(v))); - let mut map_visitor = MapDeserializer::new(map); + let mut map_visitor = MapDeserializer::new(content.into_iter()); let value = tri!(visitor.visit_map(&mut map_visitor)); tri!(map_visitor.end()); Ok(value) @@ -1683,8 +1703,7 @@ mod content { V: Visitor<'de>, E: de::Error, { - let seq = content.iter().map(ContentRefDeserializer::new); - let mut seq_visitor = SeqDeserializer::new(seq); + let mut seq_visitor = SeqDeserializer::new(content.iter()); let value = tri!(visitor.visit_seq(&mut seq_visitor)); tri!(seq_visitor.end()); Ok(value) @@ -1698,12 +1717,13 @@ mod content { V: Visitor<'de>, E: de::Error, { - let map = content.iter().map(|(k, v)| { - ( - ContentRefDeserializer::new(k), - ContentRefDeserializer::new(v), - ) - }); + fn content_ref_deserializer_pair<'a, 'de>( + (k, v): &'a (Content<'de>, Content<'de>), + ) -> (&'a Content<'de>, &'a Content<'de>) { + (k, v) + } + + let map = content.iter().map(content_ref_deserializer_pair); let mut map_visitor = MapDeserializer::new(map); let value = tri!(visitor.visit_map(&mut map_visitor)); tri!(map_visitor.end()); @@ -1887,10 +1907,19 @@ mod content { where V: Visitor<'de>, { + // Covered by tests/test_enum_untagged.rs + // with_optional_field::* match *self.content { Content::None => visitor.visit_none(), Content::Some(ref v) => visitor.visit_some(ContentRefDeserializer::new(v)), Content::Unit => visitor.visit_unit(), + // This case is to support data formats which do not encode an + // indication whether a value is optional. An example of such a + // format is JSON, and a counterexample is RON. When requesting + // `deserialize_any` in JSON, the data format never performs + // `Visitor::visit_some` but we still must be able to + // deserialize the resulting Content into data structures with + // optional fields. _ => visitor.visit_some(self), } } @@ -1920,10 +1949,21 @@ mod content { where V: Visitor<'de>, { + // Covered by tests/test_enum_untagged.rs + // newtype_struct match *self.content { Content::Newtype(ref v) => { visitor.visit_newtype_struct(ContentRefDeserializer::new(v)) } + // This case is to support data formats that encode newtype + // structs and their underlying data the same, with no + // indication whether a newtype wrapper was present. For example + // JSON does this, while RON does not. In RON a newtype's name + // is included in the serialized representation and it knows to + // call `Visitor::visit_newtype_struct` from `deserialize_any`. + // JSON's `deserialize_any` never calls `visit_newtype_struct` + // but in this code we still must be able to deserialize the + // resulting Content into newtypes. _ => visitor.visit_newtype_struct(self), } } @@ -2128,6 +2168,10 @@ mod content { fn unit_variant(self) -> Result<(), E> { match self.value { Some(value) => de::Deserialize::deserialize(ContentRefDeserializer::new(value)), + // Covered by tests/test_annotations.rs + // test_partially_untagged_adjacently_tagged_enum + // Covered by tests/test_enum_untagged.rs + // newtype_enum::unit None => Ok(()), } } @@ -2137,6 +2181,11 @@ mod content { T: de::DeserializeSeed<'de>, { match self.value { + // Covered by tests/test_annotations.rs + // test_partially_untagged_enum_desugared + // test_partially_untagged_enum_generic + // Covered by tests/test_enum_untagged.rs + // newtype_enum::newtype Some(value) => seed.deserialize(ContentRefDeserializer::new(value)), None => Err(de::Error::invalid_type( de::Unexpected::UnitVariant, @@ -2150,9 +2199,13 @@ mod content { V: de::Visitor<'de>, { match self.value { - Some(Content::Seq(v)) => { - de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor) - } + // Covered by tests/test_annotations.rs + // test_partially_untagged_enum + // test_partially_untagged_enum_desugared + // Covered by tests/test_enum_untagged.rs + // newtype_enum::tuple0 + // newtype_enum::tuple2 + Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor), Some(other) => Err(de::Error::invalid_type( other.unexpected(), &"tuple variant", @@ -2173,12 +2226,13 @@ mod content { V: de::Visitor<'de>, { match self.value { - Some(Content::Map(v)) => { - de::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor) - } - Some(Content::Seq(v)) => { - de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor) - } + // Covered by tests/test_enum_untagged.rs + // newtype_enum::struct_from_map + Some(Content::Map(v)) => visit_content_map_ref(v, visitor), + // Covered by tests/test_enum_untagged.rs + // newtype_enum::struct_from_seq + // newtype_enum::empty_struct_from_seq + Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor), Some(other) => Err(de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -2191,158 +2245,6 @@ mod content { } } - struct SeqRefDeserializer<'a, 'de: 'a, E> - where - E: de::Error, - { - iter: <&'a [Content<'de>] as IntoIterator>::IntoIter, - err: PhantomData, - } - - impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - fn new(slice: &'a [Content<'de>]) -> Self { - SeqRefDeserializer { - iter: slice.iter(), - err: PhantomData, - } - } - } - - impl<'de, 'a, E> de::Deserializer<'de> for SeqRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - type Error = E; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = tri!(visitor.visit_seq(&mut self)); - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(de::Error::invalid_length(len, &"fewer elements in array")) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } - } - - impl<'de, 'a, E> de::SeqAccess<'de> for SeqRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - type Error = E; - - fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> - where - T: de::DeserializeSeed<'de>, - { - match self.iter.next() { - Some(value) => seed - .deserialize(ContentRefDeserializer::new(value)) - .map(Some), - None => Ok(None), - } - } - - fn size_hint(&self) -> Option { - size_hint::from_bounds(&self.iter) - } - } - - struct MapRefDeserializer<'a, 'de: 'a, E> - where - E: de::Error, - { - iter: <&'a [(Content<'de>, Content<'de>)] as IntoIterator>::IntoIter, - value: Option<&'a Content<'de>>, - err: PhantomData, - } - - impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - fn new(map: &'a [(Content<'de>, Content<'de>)]) -> Self { - MapRefDeserializer { - iter: map.iter(), - value: None, - err: PhantomData, - } - } - } - - impl<'de, 'a, E> de::MapAccess<'de> for MapRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - type Error = E; - - fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> - where - T: de::DeserializeSeed<'de>, - { - match self.iter.next() { - Some((key, value)) => { - self.value = Some(value); - seed.deserialize(ContentRefDeserializer::new(key)).map(Some) - } - None => Ok(None), - } - } - - fn next_value_seed(&mut self, seed: T) -> Result - where - T: de::DeserializeSeed<'de>, - { - match self.value.take() { - Some(value) => seed.deserialize(ContentRefDeserializer::new(value)), - None => Err(de::Error::custom("value is missing")), - } - } - - fn size_hint(&self) -> Option { - size_hint::from_bounds(&self.iter) - } - } - - impl<'de, 'a, E> de::Deserializer<'de> for MapRefDeserializer<'a, 'de, E> - where - E: de::Error, - { - type Error = E; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } - } - impl<'de, E> de::IntoDeserializer<'de, E> for ContentDeserializer<'de, E> where E: de::Error, @@ -2703,6 +2605,17 @@ where visitor.visit_unit() } + fn deserialize_unit_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } + fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -2727,7 +2640,6 @@ where deserialize_string() deserialize_bytes() deserialize_byte_buf() - deserialize_unit_struct(&'static str) deserialize_seq() deserialize_tuple(usize) deserialize_tuple_struct(&'static str, usize) diff --git a/serde/src/private/doc.rs b/serde/src/private/doc.rs index 1b18fe6b4..1f17c8db5 100644 --- a/serde/src/private/doc.rs +++ b/serde/src/private/doc.rs @@ -56,7 +56,10 @@ macro_rules! __serialize_unimplemented { #[macro_export] macro_rules! __serialize_unimplemented_method { ($func:ident $(<$t:ident>)* ($($arg:ty),*) -> $ret:ident) => { - fn $func $(<$t: ?Sized + $crate::Serialize>)* (self $(, _: $arg)*) -> $crate::__private::Result { + fn $func $(<$t>)* (self $(, _: $arg)*) -> $crate::__private::Result + where + $($t: ?Sized + $crate::Serialize,)* + { unimplemented!() } }; diff --git a/serde/src/private/ser.rs b/serde/src/private/ser.rs index 50bcb251e..2f2073901 100644 --- a/serde/src/private/ser.rs +++ b/serde/src/private/ser.rs @@ -51,11 +51,10 @@ enum Unsupported { String, ByteArray, Optional, - #[cfg(any(feature = "std", feature = "alloc"))] - UnitStruct, Sequence, Tuple, TupleStruct, + #[cfg(not(any(feature = "std", feature = "alloc")))] Enum, } @@ -69,11 +68,10 @@ impl Display for Unsupported { Unsupported::String => formatter.write_str("a string"), Unsupported::ByteArray => formatter.write_str("a byte array"), Unsupported::Optional => formatter.write_str("an optional"), - #[cfg(any(feature = "std", feature = "alloc"))] - Unsupported::UnitStruct => formatter.write_str("unit struct"), Unsupported::Sequence => formatter.write_str("a sequence"), Unsupported::Tuple => formatter.write_str("a tuple"), Unsupported::TupleStruct => formatter.write_str("a tuple struct"), + #[cfg(not(any(feature = "std", feature = "alloc")))] Unsupported::Enum => formatter.write_str("an enum"), } } @@ -174,9 +172,9 @@ where Err(self.bad_type(Unsupported::Optional)) } - fn serialize_some(self, _: &T) -> Result + fn serialize_some(self, _: &T) -> Result where - T: Serialize, + T: ?Sized + Serialize, { Err(self.bad_type(Unsupported::Optional)) } @@ -205,18 +203,18 @@ where map.end() } - fn serialize_newtype_struct( + fn serialize_newtype_struct( self, _: &'static str, value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(self) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, _: &'static str, _: u32, @@ -224,7 +222,7 @@ where inner_value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { let mut map = tri!(self.delegate.serialize_map(Some(2))); tri!(map.serialize_entry(self.tag, self.variant_name)); @@ -327,9 +325,9 @@ where } #[cfg(not(any(feature = "std", feature = "alloc")))] - fn collect_str(self, _: &T) -> Result + fn collect_str(self, _: &T) -> Result where - T: Display, + T: ?Sized + Display, { Err(self.bad_type(Unsupported::String)) } @@ -364,9 +362,9 @@ mod content { type Ok = M::Ok; type Error = M::Error; - fn serialize_field(&mut self, value: &T) -> Result<(), M::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), M::Error> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push(value); @@ -404,13 +402,9 @@ mod content { type Ok = M::Ok; type Error = M::Error; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), M::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), M::Error> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push((key, value)); @@ -635,9 +629,9 @@ mod content { Ok(Content::None) } - fn serialize_some(self, value: &T) -> Result + fn serialize_some(self, value: &T) -> Result where - T: Serialize, + T: ?Sized + Serialize, { Ok(Content::Some(Box::new(tri!(value.serialize(self))))) } @@ -659,13 +653,9 @@ mod content { Ok(Content::UnitVariant(name, variant_index, variant)) } - fn serialize_newtype_struct( - self, - name: &'static str, - value: &T, - ) -> Result + fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result where - T: Serialize, + T: ?Sized + Serialize, { Ok(Content::NewtypeStruct( name, @@ -673,7 +663,7 @@ mod content { )) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, @@ -681,7 +671,7 @@ mod content { value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { Ok(Content::NewtypeVariant( name, @@ -782,9 +772,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_element(&mut self, value: &T) -> Result<(), E> + fn serialize_element(&mut self, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.elements.push(value); @@ -808,9 +798,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_element(&mut self, value: &T) -> Result<(), E> + fn serialize_element(&mut self, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.elements.push(value); @@ -835,9 +825,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_field(&mut self, value: &T) -> Result<(), E> + fn serialize_field(&mut self, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push(value); @@ -864,9 +854,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_field(&mut self, value: &T) -> Result<(), E> + fn serialize_field(&mut self, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push(value); @@ -896,18 +886,18 @@ mod content { type Ok = Content; type Error = E; - fn serialize_key(&mut self, key: &T) -> Result<(), E> + fn serialize_key(&mut self, key: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let key = tri!(key.serialize(ContentSerializer::::new())); self.key = Some(key); Ok(()) } - fn serialize_value(&mut self, value: &T) -> Result<(), E> + fn serialize_value(&mut self, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let key = self .key @@ -922,10 +912,10 @@ mod content { Ok(Content::Map(self.entries)) } - fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), E> + fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), E> where - K: Serialize, - V: Serialize, + K: ?Sized + Serialize, + V: ?Sized + Serialize, { let key = tri!(key.serialize(ContentSerializer::::new())); let value = tri!(value.serialize(ContentSerializer::::new())); @@ -947,9 +937,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push((key, value)); @@ -976,9 +966,9 @@ mod content { type Ok = Content; type Error = E; - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push((key, value)); @@ -1088,9 +1078,9 @@ where Ok(()) } - fn serialize_some(self, value: &T) -> Result + fn serialize_some(self, value: &T) -> Result where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(self) } @@ -1100,30 +1090,30 @@ where } fn serialize_unit_struct(self, _: &'static str) -> Result { - Err(Self::bad_type(Unsupported::UnitStruct)) + Ok(()) } fn serialize_unit_variant( self, _: &'static str, _: u32, - _: &'static str, + variant: &'static str, ) -> Result { - Err(Self::bad_type(Unsupported::Enum)) + self.0.serialize_entry(variant, &()) } - fn serialize_newtype_struct( + fn serialize_newtype_struct( self, _: &'static str, value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(self) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, _: &'static str, _: u32, @@ -1131,10 +1121,9 @@ where value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { - tri!(self.0.serialize_key(variant)); - self.0.serialize_value(value) + self.0.serialize_entry(variant, value) } fn serialize_seq(self, _: Option) -> Result { @@ -1202,28 +1191,24 @@ where type Ok = (); type Error = M::Error; - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { self.0.serialize_key(key) } - fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { self.0.serialize_value(value) } - fn serialize_entry( - &mut self, - key: &K, - value: &V, - ) -> Result<(), Self::Error> + fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> where - K: Serialize, - V: Serialize, + K: ?Sized + Serialize, + V: ?Sized + Serialize, { self.0.serialize_entry(key, value) } @@ -1244,13 +1229,9 @@ where type Ok = (); type Error = M::Error; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { self.0.serialize_entry(key, value) } @@ -1289,9 +1270,9 @@ where type Ok = (); type Error = M::Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push(value); @@ -1335,13 +1316,9 @@ where type Ok = (); type Error = M::Error; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { let value = tri!(value.serialize(ContentSerializer::::new())); self.fields.push((key, value)); diff --git a/serde/src/ser/fmt.rs b/serde/src/ser/fmt.rs index 0650ab6f1..4b1549f08 100644 --- a/serde/src/ser/fmt.rs +++ b/serde/src/ser/fmt.rs @@ -35,7 +35,7 @@ macro_rules! fmt_primitives { /// } /// } /// ``` -impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { +impl<'a> Serializer for &mut fmt::Formatter<'a> { type Ok = (); type Error = fmt::Error; type SerializeSeq = Impossible<(), fmt::Error>; @@ -74,9 +74,9 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { Display::fmt(variant, self) } - fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> fmt::Result + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> fmt::Result where - T: Serialize, + T: ?Sized + Serialize, { Serialize::serialize(value, self) } @@ -89,9 +89,9 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { Err(fmt::Error) } - fn serialize_some(self, _value: &T) -> fmt::Result + fn serialize_some(self, _value: &T) -> fmt::Result where - T: Serialize, + T: ?Sized + Serialize, { Err(fmt::Error) } @@ -100,7 +100,7 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { Err(fmt::Error) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, @@ -108,7 +108,7 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { _value: &T, ) -> fmt::Result where - T: Serialize, + T: ?Sized + Serialize, { Err(fmt::Error) } @@ -161,9 +161,9 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> { Err(fmt::Error) } - fn collect_str(self, value: &T) -> fmt::Result + fn collect_str(self, value: &T) -> fmt::Result where - T: Display, + T: ?Sized + Display, { Display::fmt(value, self) } diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 39acba315..fb574eae1 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -48,6 +48,7 @@ impl Serialize for str { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl Serialize for String { #[inline] fn serialize(&self, serializer: S) -> Result @@ -70,6 +71,7 @@ impl<'a> Serialize for fmt::Arguments<'a> { //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", not(no_core_cstr)))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for CStr { #[inline] fn serialize(&self, serializer: S) -> Result @@ -81,6 +83,7 @@ impl Serialize for CStr { } #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl Serialize for CString { #[inline] fn serialize(&self, serializer: S) -> Result @@ -111,7 +114,10 @@ where //////////////////////////////////////////////////////////////////////////////// -impl Serialize for PhantomData { +impl Serialize for PhantomData +where + T: ?Sized, +{ #[inline] fn serialize(&self, serializer: S) -> Result where @@ -179,9 +185,13 @@ where } } -#[cfg(all(any(feature = "std", feature = "alloc"), not(no_relaxed_trait_bounds)))] +#[cfg(not(no_relaxed_trait_bounds))] macro_rules! seq_impl { - ($ty:ident ) => { + ( + $(#[$attr:meta])* + $ty:ident + ) => { + $(#[$attr])* impl Serialize for $ty where T: Serialize, @@ -197,9 +207,13 @@ macro_rules! seq_impl { } } -#[cfg(all(any(feature = "std", feature = "alloc"), no_relaxed_trait_bounds))] +#[cfg(no_relaxed_trait_bounds)] macro_rules! seq_impl { - ($ty:ident ) => { + ( + $(#[$attr:meta])* + $ty:ident + ) => { + $(#[$attr])* impl Serialize for $ty where T: Serialize $(+ $tbound1 $(+ $tbound2)*)*, @@ -216,23 +230,41 @@ macro_rules! seq_impl { } } -#[cfg(any(feature = "std", feature = "alloc"))] -seq_impl!(BinaryHeap); +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BinaryHeap +} -#[cfg(any(feature = "std", feature = "alloc"))] -seq_impl!(BTreeSet); +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BTreeSet +} -#[cfg(feature = "std")] -seq_impl!(HashSet); +seq_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + HashSet +} -#[cfg(any(feature = "std", feature = "alloc"))] -seq_impl!(LinkedList); +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + LinkedList +} -#[cfg(any(feature = "std", feature = "alloc"))] -seq_impl!(Vec); +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + Vec +} -#[cfg(any(feature = "std", feature = "alloc"))] -seq_impl!(VecDeque); +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + VecDeque +} //////////////////////////////////////////////////////////////////////////////// @@ -339,6 +371,7 @@ impl Serialize for () { } #[cfg(feature = "unstable")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl Serialize for ! { fn serialize(&self, _serializer: S) -> Result where @@ -353,28 +386,46 @@ impl Serialize for ! { macro_rules! tuple_impls { ($($len:expr => ($($n:tt $name:ident)+))+) => { $( + #[cfg_attr(docsrs, doc(hidden))] impl<$($name),+> Serialize for ($($name,)+) where $($name: Serialize,)+ { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut tuple = tri!(serializer.serialize_tuple($len)); - $( - tri!(tuple.serialize_element(&self.$n)); - )+ - tuple.end() - } + tuple_impl_body!($len => ($($n)+)); } )+ - } + }; +} + +macro_rules! tuple_impl_body { + ($len:expr => ($($n:tt)+)) => { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tuple = tri!(serializer.serialize_tuple($len)); + $( + tri!(tuple.serialize_element(&self.$n)); + )+ + tuple.end() + } + }; +} + +#[cfg_attr(docsrs, doc(fake_variadic))] +#[cfg_attr( + docsrs, + doc = "This trait is implemented for tuples up to 16 items long." +)] +impl Serialize for (T,) +where + T: Serialize, +{ + tuple_impl_body!(1 => (0)); } tuple_impls! { - 1 => (0 T0) 2 => (0 T0 1 T1) 3 => (0 T0 1 T1 2 T2) 4 => (0 T0 1 T1 2 T2 3 T3) @@ -394,9 +445,13 @@ tuple_impls! { //////////////////////////////////////////////////////////////////////////////// -#[cfg(all(any(feature = "std", feature = "alloc"), not(no_relaxed_trait_bounds)))] +#[cfg(not(no_relaxed_trait_bounds))] macro_rules! map_impl { - ($ty:ident ) => { + ( + $(#[$attr:meta])* + $ty:ident + ) => { + $(#[$attr])* impl Serialize for $ty where K: Serialize, @@ -413,9 +468,13 @@ macro_rules! map_impl { } } -#[cfg(all(any(feature = "std", feature = "alloc"), no_relaxed_trait_bounds))] +#[cfg(no_relaxed_trait_bounds)] macro_rules! map_impl { - ($ty:ident ) => { + ( + $(#[$attr:meta])* + $ty:ident + ) => { + $(#[$attr])* impl Serialize for $ty where K: Serialize $(+ $kbound1 $(+ $kbound2)*)*, @@ -433,20 +492,26 @@ macro_rules! map_impl { } } -#[cfg(any(feature = "std", feature = "alloc"))] -map_impl!(BTreeMap); +map_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BTreeMap +} -#[cfg(feature = "std")] -map_impl!(HashMap); +map_impl! { + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + HashMap +} //////////////////////////////////////////////////////////////////////////////// macro_rules! deref_impl { ( - $(#[doc = $doc:tt])* + $(#[$attr:meta])* <$($desc:tt)+ ) => { - $(#[doc = $doc])* + $(#[$attr])* impl <$($desc)+ { #[inline] fn serialize(&self, serializer: S) -> Result @@ -459,13 +524,20 @@ macro_rules! deref_impl { }; } -deref_impl!(<'a, T: ?Sized> Serialize for &'a T where T: Serialize); -deref_impl!(<'a, T: ?Sized> Serialize for &'a mut T where T: Serialize); +deref_impl! { + <'a, T> Serialize for &'a T where T: ?Sized + Serialize +} -#[cfg(any(feature = "std", feature = "alloc"))] -deref_impl!( Serialize for Box where T: Serialize); +deref_impl! { + <'a, T> Serialize for &'a mut T where T: ?Sized + Serialize +} + +deref_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + Serialize for Box where T: ?Sized + Serialize +} -#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] deref_impl! { /// This impl requires the [`"rc"`] Cargo feature of Serde. /// @@ -475,10 +547,11 @@ deref_impl! { /// repeated data. /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc - Serialize for Rc where T: Serialize + #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + Serialize for Rc where T: ?Sized + Serialize } -#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] deref_impl! { /// This impl requires the [`"rc"`] Cargo feature of Serde. /// @@ -488,11 +561,16 @@ deref_impl! { /// repeated data. /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc - Serialize for Arc where T: Serialize + #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + Serialize for Arc where T: ?Sized + Serialize } -#[cfg(any(feature = "std", feature = "alloc"))] -deref_impl!(<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned); +deref_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + <'a, T> Serialize for Cow<'a, T> where T: ?Sized + Serialize + ToOwned +} //////////////////////////////////////////////////////////////////////////////// @@ -500,9 +578,13 @@ deref_impl!(<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwne /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] -impl Serialize for RcWeak +#[cfg_attr( + docsrs, + doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) +)] +impl Serialize for RcWeak where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -516,9 +598,13 @@ where /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] -impl Serialize for ArcWeak +#[cfg_attr( + docsrs, + doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) +)] +impl Serialize for ArcWeak where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -576,9 +662,9 @@ where } } -impl Serialize for RefCell +impl Serialize for RefCell where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -592,9 +678,10 @@ where } #[cfg(feature = "std")] -impl Serialize for Mutex +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl Serialize for Mutex where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -608,9 +695,10 @@ where } #[cfg(feature = "std")] -impl Serialize for RwLock +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl Serialize for RwLock where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -661,6 +749,7 @@ impl Serialize for Duration { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for SystemTime { fn serialize(&self, serializer: S) -> Result where @@ -684,27 +773,17 @@ impl Serialize for SystemTime { /// statically known to never have more than a constant `MAX_LEN` bytes. /// /// Panics if the `Display` impl tries to write more than `MAX_LEN` bytes. -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! serialize_display_bounded_length { ($value:expr, $max:expr, $serializer:expr) => {{ let mut buffer = [0u8; $max]; - let remaining_len = { - let mut remaining = &mut buffer[..]; - write!(remaining, "{}", $value).unwrap(); - remaining.len() - }; - let written_len = buffer.len() - remaining_len; - let written = &buffer[..written_len]; - - // write! only provides fmt::Formatter to Display implementations, which - // has methods write_str and write_char but no method to write arbitrary - // bytes. Therefore `written` must be valid UTF-8. - let written_str = str::from_utf8(written).expect("must be valid UTF-8"); - $serializer.serialize_str(written_str) + let mut writer = crate::format::Buf::new(&mut buffer); + write!(&mut writer, "{}", $value).unwrap(); + $serializer.serialize_str(writer.as_str()) }}; } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::IpAddr { fn serialize(&self, serializer: S) -> Result where @@ -728,7 +807,7 @@ impl Serialize for net::IpAddr { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] const DEC_DIGITS_LUT: &[u8] = b"\ 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ @@ -736,7 +815,7 @@ const DEC_DIGITS_LUT: &[u8] = b"\ 6061626364656667686970717273747576777879\ 8081828384858687888990919293949596979899"; -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] #[inline] fn format_u8(mut n: u8, out: &mut [u8]) -> usize { if n >= 100 { @@ -757,7 +836,7 @@ fn format_u8(mut n: u8, out: &mut [u8]) -> usize { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] #[test] fn test_format_u8() { let mut i = 0u8; @@ -774,7 +853,7 @@ fn test_format_u8() { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::Ipv4Addr { fn serialize(&self, serializer: S) -> Result where @@ -798,7 +877,7 @@ impl Serialize for net::Ipv4Addr { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::Ipv6Addr { fn serialize(&self, serializer: S) -> Result where @@ -814,7 +893,7 @@ impl Serialize for net::Ipv6Addr { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddr { fn serialize(&self, serializer: S) -> Result where @@ -838,7 +917,7 @@ impl Serialize for net::SocketAddr { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddrV4 { fn serialize(&self, serializer: S) -> Result where @@ -854,7 +933,7 @@ impl Serialize for net::SocketAddrV4 { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddrV6 { fn serialize(&self, serializer: S) -> Result where @@ -876,6 +955,7 @@ impl Serialize for net::SocketAddrV6 { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for Path { fn serialize(&self, serializer: S) -> Result where @@ -889,6 +969,7 @@ impl Serialize for Path { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for PathBuf { fn serialize(&self, serializer: S) -> Result where @@ -899,6 +980,7 @@ impl Serialize for PathBuf { } #[cfg(all(feature = "std", any(unix, windows)))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", any(unix, windows)))))] impl Serialize for OsStr { #[cfg(unix)] fn serialize(&self, serializer: S) -> Result @@ -921,6 +1003,7 @@ impl Serialize for OsStr { } #[cfg(all(feature = "std", any(unix, windows)))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", any(unix, windows)))))] impl Serialize for OsString { fn serialize(&self, serializer: S) -> Result where @@ -945,6 +1028,20 @@ where } } +#[cfg(not(no_core_num_saturating))] +impl Serialize for Saturating +where + T: Serialize, +{ + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.serialize(serializer) + } +} + impl Serialize for Reverse where T: Serialize, @@ -965,6 +1062,7 @@ macro_rules! atomic_impl { ($($ty:ident $size:expr)*) => { $( #[cfg(any(no_target_has_atomic, target_has_atomic = $size))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_has_atomic = $size))))] impl Serialize for $ty { fn serialize(&self, serializer: S) -> Result where diff --git a/serde/src/ser/impossible.rs b/serde/src/ser/impossible.rs index 479be9407..e2566b29a 100644 --- a/serde/src/ser/impossible.rs +++ b/serde/src/ser/impossible.rs @@ -49,14 +49,14 @@ use crate::ser::{ /// } /// ``` /// -/// [`Serializer`]: trait.Serializer.html -/// [`SerializeSeq`]: trait.SerializeSeq.html -/// [`SerializeTuple`]: trait.SerializeTuple.html -/// [`SerializeTupleStruct`]: trait.SerializeTupleStruct.html -/// [`SerializeTupleVariant`]: trait.SerializeTupleVariant.html -/// [`SerializeMap`]: trait.SerializeMap.html -/// [`SerializeStruct`]: trait.SerializeStruct.html -/// [`SerializeStructVariant`]: trait.SerializeStructVariant.html +/// [`Serializer`]: crate::Serializer +/// [`SerializeSeq`]: crate::ser::SerializeSeq +/// [`SerializeTuple`]: crate::ser::SerializeTuple +/// [`SerializeTupleStruct`]: crate::ser::SerializeTupleStruct +/// [`SerializeTupleVariant`]: crate::ser::SerializeTupleVariant +/// [`SerializeMap`]: crate::ser::SerializeMap +/// [`SerializeStruct`]: crate::ser::SerializeStruct +/// [`SerializeStructVariant`]: crate::ser::SerializeStructVariant pub struct Impossible { void: Void, ok: PhantomData, @@ -72,9 +72,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_element(&mut self, value: &T) -> Result<(), Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = value; match self.void {} @@ -92,9 +92,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_element(&mut self, value: &T) -> Result<(), Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = value; match self.void {} @@ -112,9 +112,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = value; match self.void {} @@ -132,9 +132,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = value; match self.void {} @@ -152,17 +152,17 @@ where type Ok = Ok; type Error = Error; - fn serialize_key(&mut self, key: &T) -> Result<(), Error> + fn serialize_key(&mut self, key: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = key; match self.void {} } - fn serialize_value(&mut self, value: &T) -> Result<(), Error> + fn serialize_value(&mut self, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = value; match self.void {} @@ -180,9 +180,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = key; let _ = value; @@ -201,9 +201,9 @@ where type Ok = Ok; type Error = Error; - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> where - T: Serialize, + T: ?Sized + Serialize, { let _ = key; let _ = value; diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index f1820c20a..23418f9f8 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -61,8 +61,8 @@ //! - RefCell\ //! - Mutex\ //! - RwLock\ -//! - Rc\ *(if* features = ["rc"] *is enabled)* -//! - Arc\ *(if* features = ["rc"] *is enabled)* +//! - Rc\ *(if* features = \["rc"\] *is enabled)* +//! - Arc\ *(if* features = \["rc"\] *is enabled)* //! - **Collection types**: //! - BTreeMap\ //! - BTreeSet\ @@ -97,8 +97,8 @@ //! //! [Implementing `Serialize`]: https://serde.rs/impl-serialize.html //! [`LinkedHashMap`]: https://docs.rs/linked-hash-map/*/linked_hash_map/struct.LinkedHashMap.html -//! [`Serialize`]: ../trait.Serialize.html -//! [`Serializer`]: ../trait.Serializer.html +//! [`Serialize`]: crate::Serialize +//! [`Serializer`]: crate::Serializer //! [`postcard`]: https://github.com/jamesmunns/postcard //! [`linked-hash-map`]: https://crates.io/crates/linked-hash-map //! [`serde_derive`]: https://crates.io/crates/serde_derive @@ -115,10 +115,10 @@ mod impossible; pub use self::impossible::Impossible; -#[cfg(not(any(feature = "std", feature = "unstable")))] +#[cfg(all(not(feature = "std"), no_core_error))] #[doc(no_inline)] pub use crate::std_error::Error as StdError; -#[cfg(all(feature = "unstable", not(feature = "std")))] +#[cfg(not(any(feature = "std", no_core_error)))] #[doc(no_inline)] pub use core::error::Error as StdError; #[cfg(feature = "std")] @@ -173,8 +173,8 @@ macro_rules! declare_error_trait { /// } /// ``` /// - /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html - /// [`Serialize`]: ../trait.Serialize.html + /// [`Path`]: std::path::Path + /// [`Serialize`]: crate::Serialize fn custom(msg: T) -> Self where T: Display; @@ -215,6 +215,13 @@ declare_error_trait!(Error: Sized + Debug + Display); /// [`linked-hash-map`]: https://crates.io/crates/linked-hash-map /// [`serde_derive`]: https://crates.io/crates/serde_derive /// [derive section of the manual]: https://serde.rs/derive.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + note = "for local types consider adding `#[derive(serde::Serialize)]` to your `{Self}` type", + note = "for types from other crates check whether the crate offers a `serde` feature flag", + ) +)] pub trait Serialize { /// Serialize this value into the given Serde serializer. /// @@ -338,7 +345,7 @@ pub trait Serializer: Sized { /// in-memory data structures may be simplified by using `Ok` to propagate /// the data structure around. /// - /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`io::Write`]: std::io::Write type Ok; /// The error type when some error occurs during serialization. @@ -762,7 +769,7 @@ pub trait Serializer: Sized { /// # fn main() {} /// ``` /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`None`]: core::option::Option::None fn serialize_none(self) -> Result; /// Serialize a [`Some(T)`] value. @@ -795,10 +802,10 @@ pub trait Serializer: Sized { /// # fn main() {} /// ``` /// - /// [`Some(T)`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some - fn serialize_some(self, value: &T) -> Result + /// [`Some(T)`]: core::option::Option::Some + fn serialize_some(self, value: &T) -> Result where - T: Serialize; + T: ?Sized + Serialize; /// Serialize a `()` value. /// @@ -891,13 +898,13 @@ pub trait Serializer: Sized { /// } /// } /// ``` - fn serialize_newtype_struct( + fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result where - T: Serialize; + T: ?Sized + Serialize; /// Serialize a newtype variant like `E::N` in `enum E { N(u8) }`. /// @@ -925,7 +932,7 @@ pub trait Serializer: Sized { /// } /// } /// ``` - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, @@ -933,7 +940,7 @@ pub trait Serializer: Sized { value: &T, ) -> Result where - T: Serialize; + T: ?Sized + Serialize; /// Begin to serialize a variably sized sequence. This call must be /// followed by zero or more calls to `serialize_element`, then a call to @@ -1170,7 +1177,8 @@ pub trait Serializer: Sized { /// then a call to `end`. /// /// The `name` is the name of the struct and the `len` is the number of - /// data fields that will be serialized. + /// data fields that will be serialized. `len` does not include fields + /// which are skipped with [`SerializeStruct::skip_field`]. /// /// ```edition2021 /// use serde::ser::{Serialize, SerializeStruct, Serializer}; @@ -1207,6 +1215,8 @@ pub trait Serializer: Sized { /// The `name` is the name of the enum, the `variant_index` is the index of /// this variant within the enum, the `variant` is the name of the variant, /// and the `len` is the number of data fields that will be serialized. + /// `len` does not include fields which are skipped with + /// [`SerializeStructVariant::skip_field`]. /// /// ```edition2021 /// use serde::ser::{Serialize, SerializeStructVariant, Serializer}; @@ -1343,12 +1353,11 @@ pub trait Serializer: Sized { /// } /// ``` /// - /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html - /// [`serialize_str`]: #tymethod.serialize_str + /// [`serialize_str`]: Self::serialize_str #[cfg(any(feature = "std", feature = "alloc"))] - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display, + T: ?Sized + Display, { self.serialize_str(&value.to_string()) } @@ -1379,9 +1388,9 @@ pub trait Serializer: Sized { /// } /// ``` #[cfg(not(any(feature = "std", feature = "alloc")))] - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display; + T: ?Sized + Display; /// Determine whether `Serialize` implementations should serialize in /// human-readable form. @@ -1493,9 +1502,9 @@ pub trait SerializeSeq { type Error: Error; /// Serialize a sequence element. - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Finish serializing a sequence. fn end(self) -> Result; @@ -1593,9 +1602,9 @@ pub trait SerializeTuple { type Error: Error; /// Serialize a tuple element. - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Finish serializing a tuple. fn end(self) -> Result; @@ -1638,9 +1647,9 @@ pub trait SerializeTupleStruct { type Error: Error; /// Serialize a tuple struct field. - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Finish serializing a tuple struct. fn end(self) -> Result; @@ -1696,9 +1705,9 @@ pub trait SerializeTupleVariant { type Error: Error; /// Serialize a tuple variant field. - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Finish serializing a tuple variant. fn end(self) -> Result; @@ -1767,9 +1776,9 @@ pub trait SerializeMap { /// `serialize_entry` instead as it may be implemented more efficiently in /// some formats compared to a pair of calls to `serialize_key` and /// `serialize_value`. - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Serialize a map value. /// @@ -1777,9 +1786,9 @@ pub trait SerializeMap { /// /// Calling `serialize_value` before `serialize_key` is incorrect and is /// allowed to panic or produce bogus results. - fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Serialize a map entry consisting of a key and a value. /// @@ -1795,17 +1804,13 @@ pub trait SerializeMap { /// care about performance or are not able to optimize `serialize_entry` any /// better than this. /// - /// [`Serialize`]: ../trait.Serialize.html - /// [`serialize_key`]: #tymethod.serialize_key - /// [`serialize_value`]: #tymethod.serialize_value - fn serialize_entry( - &mut self, - key: &K, - value: &V, - ) -> Result<(), Self::Error> + /// [`Serialize`]: crate::Serialize + /// [`serialize_key`]: Self::serialize_key + /// [`serialize_value`]: Self::serialize_value + fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> where - K: Serialize, - V: Serialize, + K: ?Sized + Serialize, + V: ?Sized + Serialize, { tri!(self.serialize_key(key)); self.serialize_value(value) @@ -1856,15 +1861,13 @@ pub trait SerializeStruct { type Error: Error; /// Serialize a struct field. - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Indicate that a struct field has been skipped. + /// + /// The default implementation does nothing. #[inline] fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { let _ = key; @@ -1922,15 +1925,13 @@ pub trait SerializeStructVariant { type Error: Error; /// Serialize a struct variant field. - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize; + T: ?Sized + Serialize; /// Indicate that a struct variant field has been skipped. + /// + /// The default implementation does nothing. #[inline] fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { let _ = key; diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index 2f3520b78..393a5e0a3 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -1,16 +1,18 @@ [package] name = "serde_derive" -version = "1.0.188" # remember to update html_root_url +version = "1.0.218" authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["no-std", "no-std::no-alloc"] description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]" documentation = "https://serde.rs/derive.html" +edition = "2015" +exclude = ["build.rs"] homepage = "https://serde.rs" keywords = ["serde", "serialization", "no_std", "derive"] license = "MIT OR Apache-2.0" readme = "crates-io.md" repository = "https://github.com/serde-rs/serde" -rust-version = "1.56" +rust-version = "1.61" [features] default = [] @@ -21,13 +23,19 @@ name = "serde_derive" proc-macro = true [dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = "2.0.28" +proc-macro2 = { workspace = true, features = ["proc-macro"] } +quote = { workspace = true, features = ["proc-macro"] } +syn = { workspace = true, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] } [dev-dependencies] serde = { version = "1", path = "../serde" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", + "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", +] diff --git a/serde_derive/build.rs b/serde_derive/build.rs new file mode 100644 index 000000000..ebaa409a4 --- /dev/null +++ b/serde_derive/build.rs @@ -0,0 +1,8 @@ +fn main() { + // Warning: build.rs is not published to crates.io. + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rustc-cfg=check_cfg"); + println!("cargo:rustc-check-cfg=cfg(check_cfg)"); + println!("cargo:rustc-check-cfg=cfg(exhaustive)"); +} diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index dd51e6898..2ff6521fe 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -144,6 +144,7 @@ pub fn with_bound( fn visit_type(&mut self, ty: &'ast syn::Type) { match ty { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] syn::Type::Array(ty) => self.visit_type(&ty.elem), syn::Type::BareFn(ty) => { for arg in &ty.inputs { @@ -181,7 +182,6 @@ pub fn with_bound( syn::Type::Infer(_) | syn::Type::Never(_) | syn::Type::Verbatim(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => {} } } @@ -196,16 +196,13 @@ pub fn with_bound( syn::PathArguments::AngleBracketed(arguments) => { for arg in &arguments.args { match arg { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] syn::GenericArgument::Type(arg) => self.visit_type(arg), syn::GenericArgument::AssocType(arg) => self.visit_type(&arg.ty), syn::GenericArgument::Lifetime(_) | syn::GenericArgument::Const(_) | syn::GenericArgument::AssocConst(_) | syn::GenericArgument::Constraint(_) => {} - #[cfg_attr( - all(test, exhaustive), - deny(non_exhaustive_omitted_patterns) - )] _ => {} } } @@ -228,9 +225,11 @@ pub fn with_bound( fn visit_type_param_bound(&mut self, bound: &'ast syn::TypeParamBound) { match bound { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] syn::TypeParamBound::Trait(bound) => self.visit_path(&bound.path), - syn::TypeParamBound::Lifetime(_) | syn::TypeParamBound::Verbatim(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + syn::TypeParamBound::Lifetime(_) + | syn::TypeParamBound::PreciseCapture(_) + | syn::TypeParamBound::Verbatim(_) => {} _ => {} } } diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index ee8a23766..4967e35d1 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1,5 +1,6 @@ use crate::fragment::{Expr, Fragment, Match, Stmts}; use crate::internals::ast::{Container, Data, Field, Style, Variant}; +use crate::internals::name::Name; use crate::internals::{attr, replace_receiver, ungroup, Ctxt, Derive}; use crate::{bound, dummy, pretend, this}; use proc_macro2::{Literal, Span, TokenStream}; @@ -32,6 +33,7 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result(__deserializer: __D) -> #serde::__private::Result<#remote #ty_generics, __D::Error> where @@ -371,7 +373,11 @@ fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment { } else { let value = match field.attrs.default() { attr::Default::Default => quote!(_serde::__private::Default::default()), - attr::Default::Path(path) => quote!(#path()), + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + attr::Default::Path(path) => quote_spanned!(path.span()=> #path()), attr::Default::None => quote!(_serde::__private::PhantomData), }; quote!(#member: #value) @@ -419,6 +425,7 @@ fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fra lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -461,7 +468,10 @@ fn deserialize_tuple( cattrs: &attr::Container, form: TupleForm, ) -> Fragment { - assert!(!cattrs.has_flatten()); + assert!( + !has_flatten(fields), + "tuples and tuple variants cannot have flatten fields" + ); let field_count = fields .iter() @@ -551,6 +561,7 @@ fn deserialize_tuple( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -579,7 +590,10 @@ fn deserialize_tuple_in_place( fields: &[Field], cattrs: &attr::Container, ) -> Fragment { - assert!(!cattrs.has_flatten()); + assert!( + !has_flatten(fields), + "tuples and tuple variants cannot have flatten fields" + ); let field_count = fields .iter() @@ -647,6 +661,7 @@ fn deserialize_tuple_in_place( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause { type Value = (); @@ -751,7 +766,11 @@ fn deserialize_seq( attr::Default::Default => Some(quote!( let __default: Self::Value = _serde::__private::Default::default(); )), - attr::Default::Path(path) => Some(quote!( + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + attr::Default::Path(path) => Some(quote_spanned!(path.span()=> let __default: Self::Value = #path(); )), attr::Default::None => { @@ -833,7 +852,11 @@ fn deserialize_seq_in_place( attr::Default::Default => Some(quote!( let __default: #this_type #ty_generics = _serde::__private::Default::default(); )), - attr::Default::Path(path) => Some(quote!( + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + attr::Default::Path(path) => Some(quote_spanned!(path.span()=> let __default: #this_type #ty_generics = #path(); )), attr::Default::None => { @@ -857,18 +880,23 @@ fn deserialize_newtype_struct( ) -> TokenStream { let delife = params.borrowed.de_lifetime(); let field_ty = field.ty; + let deserializer_var = quote!(__e); let value = match field.attrs.deserialize_with() { None => { let span = field.original.span(); let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize); quote! { - #func(__e)? + #func(#deserializer_var)? } } Some(path) => { - quote! { - #path(__e)? + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(with = "...")] + // ^^^^^ + quote_spanned! {path.span()=> + #path(#deserializer_var)? } } }; @@ -884,7 +912,7 @@ fn deserialize_newtype_struct( quote! { #[inline] - fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::__private::Result + fn visit_newtype_struct<__E>(self, #deserializer_var: __E) -> _serde::__private::Result where __E: _serde::Deserializer<#delife>, { @@ -944,29 +972,28 @@ fn deserialize_struct( }; let expecting = cattrs.expecting().unwrap_or(&expecting); - let field_names_idents: Vec<_> = fields + let deserialized_fields: Vec<_> = fields .iter() .enumerate() // Skip fields that shouldn't be deserialized or that were flattened, // so they don't appear in the storage in their literal form .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) + .map(|(i, field)| FieldWithAliases { + ident: field_i(i), + aliases: field.attrs.aliases(), }) .collect(); - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs); + + let has_flatten = has_flatten(fields); + let field_visitor = deserialize_field_identifier(&deserialized_fields, cattrs, has_flatten); // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. let visit_seq = match form { StructForm::Untagged(..) => None, - _ if cattrs.has_flatten() => None, + _ if has_flatten => None, _ => { - let mut_seq = if field_names_idents.is_empty() { + let mut_seq = if deserialized_fields.is_empty() { quote!(_) } else { quote!(mut __seq) @@ -987,10 +1014,17 @@ fn deserialize_struct( }) } }; - let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs)); + let visit_map = Stmts(deserialize_map( + &type_path, + params, + fields, + cattrs, + has_flatten, + )); let visitor_seed = match form { - StructForm::ExternallyTagged(..) if cattrs.has_flatten() => Some(quote! { + StructForm::ExternallyTagged(..) if has_flatten => Some(quote! { + #[automatically_derived] impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -1005,12 +1039,10 @@ fn deserialize_struct( _ => None, }; - let fields_stmt = if cattrs.has_flatten() { + let fields_stmt = if has_flatten { None } else { - let field_names = field_names_idents - .iter() - .flat_map(|&(_, _, aliases)| aliases); + let field_names = deserialized_fields.iter().flat_map(|field| field.aliases); Some(quote! { #[doc(hidden)] @@ -1025,7 +1057,7 @@ fn deserialize_struct( } }; let dispatch = match form { - StructForm::Struct if cattrs.has_flatten() => quote! { + StructForm::Struct if has_flatten => quote! { _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) }, StructForm::Struct => { @@ -1034,7 +1066,7 @@ fn deserialize_struct( _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) } } - StructForm::ExternallyTagged(_) if cattrs.has_flatten() => quote! { + StructForm::ExternallyTagged(_) if has_flatten => quote! { _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) }, StructForm::ExternallyTagged(_) => quote! { @@ -1057,6 +1089,7 @@ fn deserialize_struct( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -1091,7 +1124,7 @@ fn deserialize_struct_in_place( ) -> Option { // for now we do not support in_place deserialization for structs that // are represented as map. - if cattrs.has_flatten() { + if has_flatten(fields) { return None; } @@ -1103,31 +1136,26 @@ fn deserialize_struct_in_place( let expecting = format!("struct {}", params.type_name()); let expecting = cattrs.expecting().unwrap_or(&expecting); - let field_names_idents: Vec<_> = fields + let deserialized_fields: Vec<_> = fields .iter() .enumerate() .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) + .map(|(i, field)| FieldWithAliases { + ident: field_i(i), + aliases: field.attrs.aliases(), }) .collect(); - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs); + let field_visitor = deserialize_field_identifier(&deserialized_fields, cattrs, false); - let mut_seq = if field_names_idents.is_empty() { + let mut_seq = if deserialized_fields.is_empty() { quote!(_) } else { quote!(mut __seq) }; let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting)); let visit_map = Stmts(deserialize_map_in_place(params, fields, cattrs)); - let field_names = field_names_idents - .iter() - .flat_map(|&(_, _, aliases)| aliases); + let field_names = deserialized_fields.iter().flat_map(|field| field.aliases); let type_name = cattrs.name().deserialize_name(); let in_place_impl_generics = de_impl_generics.in_place(); @@ -1143,6 +1171,7 @@ fn deserialize_struct_in_place( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause { type Value = (); @@ -1210,44 +1239,40 @@ fn deserialize_homogeneous_enum( } } -fn prepare_enum_variant_enum( - variants: &[Variant], - cattrs: &attr::Container, -) -> (TokenStream, Stmts) { - let mut deserialized_variants = variants +fn prepare_enum_variant_enum(variants: &[Variant]) -> (TokenStream, Stmts) { + let deserialized_variants = variants .iter() .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()); - - let variant_names_idents: Vec<_> = deserialized_variants - .clone() - .map(|(i, variant)| { - ( - variant.attrs.name().deserialize_name(), - field_i(i), - variant.attrs.aliases(), - ) - }) - .collect(); + .filter(|&(_i, variant)| !variant.attrs.skip_deserializing()); let fallthrough = deserialized_variants - .position(|(_, variant)| variant.attrs.other()) - .map(|other_idx| { - let ignore_variant = variant_names_idents[other_idx].1.clone(); + .clone() + .find(|(_i, variant)| variant.attrs.other()) + .map(|(i, _variant)| { + let ignore_variant = field_i(i); quote!(_serde::__private::Ok(__Field::#ignore_variant)) }); let variants_stmt = { - let variant_names = variant_names_idents.iter().map(|(name, _, _)| name); + let variant_names = deserialized_variants + .clone() + .flat_map(|(_i, variant)| variant.attrs.aliases()); quote! { #[doc(hidden)] const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; } }; + let deserialized_variants: Vec<_> = deserialized_variants + .map(|(i, variant)| FieldWithAliases { + ident: field_i(i), + aliases: variant.attrs.aliases(), + }) + .collect(); + let variant_visitor = Stmts(deserialize_generated_identifier( - &variant_names_idents, - cattrs, + &deserialized_variants, + false, // variant identifiers do not depend on the presence of flatten fields true, None, fallthrough, @@ -1270,7 +1295,7 @@ fn deserialize_externally_tagged_enum( let expecting = format!("enum {}", params.type_name()); let expecting = cattrs.expecting().unwrap_or(&expecting); - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); // Match arms to extract a variant from a string let variant_arms = variants @@ -1320,6 +1345,7 @@ fn deserialize_externally_tagged_enum( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -1355,7 +1381,7 @@ fn deserialize_internally_tagged_enum( cattrs: &attr::Container, tag: &str, ) -> Fragment { - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); // Match arms to extract a variant from a string let variant_arms = variants @@ -1409,7 +1435,7 @@ fn deserialize_adjacently_tagged_enum( split_with_de_lifetime(params); let delife = params.borrowed.de_lifetime(); - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); let variant_arms: &Vec<_> = &variants .iter() @@ -1581,6 +1607,7 @@ fn deserialize_adjacently_tagged_enum( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Seed #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -1600,6 +1627,7 @@ fn deserialize_adjacently_tagged_enum( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -1737,7 +1765,6 @@ fn deserialize_untagged_enum_after( 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 @@ -1750,10 +1777,23 @@ fn deserialize_untagged_enum_after( ); let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg); + // Ignore any error associated with non-untagged deserialization so that we + // can fall through to the untagged variants. This may be infallible so we + // need to provide the error type. + let first_attempt = first_attempt.map(|expr| { + quote! { + if let _serde::__private::Result::<_, __D::Error>::Ok(__ok) = (|| #expr)() { + return _serde::__private::Ok(__ok); + } + } + }); + quote_block! { let __content = <_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer)?; let __deserializer = _serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content); + #first_attempt + #( if let _serde::__private::Ok(__ok) = #attempts { return _serde::__private::Ok(__ok); @@ -1828,7 +1868,7 @@ fn deserialize_internally_tagged_variant( let this_value = ¶ms.this_value; let type_name = params.type_name(); let variant_name = variant.ident.to_string(); - let default = variant.fields.get(0).map(|field| { + let default = variant.fields.first().map(|field| { let default = Expr(expr_is_missing(field, cattrs)); quote!((#default)) }); @@ -1873,7 +1913,7 @@ fn deserialize_untagged_variant( let this_value = ¶ms.this_value; let type_name = params.type_name(); let variant_name = variant.ident.to_string(); - let default = variant.fields.get(0).map(|field| { + let default = variant.fields.first().map(|field| { let default = Expr(expr_is_missing(field, cattrs)); quote!((#default)) }); @@ -1971,27 +2011,35 @@ fn deserialize_untagged_newtype_variant( } } +struct FieldWithAliases<'a> { + ident: Ident, + aliases: &'a BTreeSet, +} + fn deserialize_generated_identifier( - fields: &[(&str, Ident, &BTreeSet)], - cattrs: &attr::Container, + deserialized_fields: &[FieldWithAliases], + has_flatten: bool, is_variant: bool, ignore_variant: Option, fallthrough: Option, ) -> Fragment { let this_value = quote!(__Field); - let field_idents: &Vec<_> = &fields.iter().map(|(_, ident, _)| ident).collect(); + let field_idents: &Vec<_> = &deserialized_fields + .iter() + .map(|field| &field.ident) + .collect(); let visitor_impl = Stmts(deserialize_identifier( &this_value, - fields, + deserialized_fields, is_variant, fallthrough, None, - !is_variant && cattrs.has_flatten(), + !is_variant && has_flatten, None, )); - let lifetime = if !is_variant && cattrs.has_flatten() { + let lifetime = if !is_variant && has_flatten { Some(quote!(<'de>)) } else { None @@ -2008,12 +2056,14 @@ fn deserialize_generated_identifier( #[doc(hidden)] struct __FieldVisitor; + #[automatically_derived] impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { type Value = __Field #lifetime; #visitor_impl } + #[automatically_derived] impl<'de> _serde::Deserialize<'de> for __Field #lifetime { #[inline] fn deserialize<__D>(__deserializer: __D) -> _serde::__private::Result @@ -2029,10 +2079,11 @@ fn deserialize_generated_identifier( /// Generates enum and its `Deserialize` implementation that represents each /// non-skipped field of the struct fn deserialize_field_identifier( - fields: &[(&str, Ident, &BTreeSet)], + deserialized_fields: &[FieldWithAliases], cattrs: &attr::Container, + has_flatten: bool, ) -> Stmts { - let (ignore_variant, fallthrough) = if cattrs.has_flatten() { + let (ignore_variant, fallthrough) = if has_flatten { let ignore_variant = quote!(__other(_serde::__private::de::Content<'de>),); let fallthrough = quote!(_serde::__private::Ok(__Field::__other(__value))); (Some(ignore_variant), Some(fallthrough)) @@ -2045,8 +2096,8 @@ fn deserialize_field_identifier( }; Stmts(deserialize_generated_identifier( - fields, - cattrs, + deserialized_fields, + has_flatten, false, ignore_variant, fallthrough, @@ -2103,18 +2154,15 @@ fn deserialize_custom_identifier( (variants, None, None) }; - let names_idents: Vec<_> = ordinary + let idents_aliases: Vec<_> = ordinary .iter() - .map(|variant| { - ( - variant.attrs.name().deserialize_name(), - variant.ident.clone(), - variant.attrs.aliases(), - ) + .map(|variant| FieldWithAliases { + ident: variant.ident.clone(), + aliases: variant.attrs.aliases(), }) .collect(); - let names = names_idents.iter().flat_map(|&(_, _, aliases)| aliases); + let names = idents_aliases.iter().flat_map(|variant| variant.aliases); let names_const = if fallthrough.is_some() { None @@ -2137,7 +2185,7 @@ fn deserialize_custom_identifier( let delife = params.borrowed.de_lifetime(); let visitor_impl = Stmts(deserialize_identifier( &this_value, - &names_idents, + &idents_aliases, is_variant, fallthrough, fallthrough_borrowed, @@ -2154,6 +2202,7 @@ fn deserialize_custom_identifier( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::de::Visitor<#delife> for __FieldVisitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -2170,23 +2219,35 @@ fn deserialize_custom_identifier( fn deserialize_identifier( this_value: &TokenStream, - fields: &[(&str, Ident, &BTreeSet)], + deserialized_fields: &[FieldWithAliases], is_variant: bool, fallthrough: Option, fallthrough_borrowed: Option, collect_other_fields: bool, expecting: Option<&str>, ) -> Fragment { - let str_mapping = fields.iter().map(|(_, ident, aliases)| { + let str_mapping = deserialized_fields.iter().map(|field| { + let ident = &field.ident; + let aliases = field.aliases; // `aliases` also contains a main name - quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident)) + quote! { + #( + #aliases => _serde::__private::Ok(#this_value::#ident), + )* + } }); - let bytes_mapping = fields.iter().map(|(_, ident, aliases)| { + let bytes_mapping = deserialized_fields.iter().map(|field| { + let ident = &field.ident; // `aliases` also contains a main name - let aliases = aliases + let aliases = field + .aliases .iter() - .map(|alias| Literal::byte_string(alias.as_bytes())); - quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident)) + .map(|alias| Literal::byte_string(alias.value.as_bytes())); + quote! { + #( + #aliases => _serde::__private::Ok(#this_value::#ident), + )* + } }); let expecting = expecting.unwrap_or(if is_variant { @@ -2336,8 +2397,9 @@ fn deserialize_identifier( } } } else { - let u64_mapping = fields.iter().enumerate().map(|(i, (_, ident, _))| { + let u64_mapping = deserialized_fields.iter().enumerate().map(|(i, field)| { let i = i as u64; + let ident = &field.ident; quote!(#i => _serde::__private::Ok(#this_value::#ident)) }); @@ -2346,7 +2408,11 @@ fn deserialize_identifier( fallthrough } else { let index_expecting = if is_variant { "variant" } else { "field" }; - let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len()); + let fallthrough_msg = format!( + "{} index 0 <= i < {}", + index_expecting, + deserialized_fields.len(), + ); u64_fallthrough_arm_tokens = quote! { _serde::__private::Err(_serde::de::Error::invalid_value( _serde::de::Unexpected::Unsigned(__value), @@ -2379,7 +2445,7 @@ fn deserialize_identifier( __E: _serde::de::Error, { match __value { - #(#str_mapping,)* + #(#str_mapping)* _ => { #value_as_borrowed_str_content #fallthrough_borrowed_arm @@ -2392,7 +2458,7 @@ fn deserialize_identifier( __E: _serde::de::Error, { match __value { - #(#bytes_mapping,)* + #(#bytes_mapping)* _ => { #bytes_to_str #value_as_borrowed_bytes_content @@ -2417,7 +2483,7 @@ fn deserialize_identifier( __E: _serde::de::Error, { match __value { - #(#str_mapping,)* + #(#str_mapping)* _ => { #value_as_str_content #fallthrough_arm @@ -2430,7 +2496,7 @@ fn deserialize_identifier( __E: _serde::de::Error, { match __value { - #(#bytes_mapping,)* + #(#bytes_mapping)* _ => { #bytes_to_str #value_as_bytes_content @@ -2448,6 +2514,7 @@ fn deserialize_map( params: &Parameters, fields: &[Field], cattrs: &attr::Container, + has_flatten: bool, ) -> Fragment { // Create the field names for the fields. let fields_names: Vec<_> = fields @@ -2468,7 +2535,7 @@ fn deserialize_map( }); // Collect contents for flatten fields into a buffer - let let_collect = if cattrs.has_flatten() { + let let_collect = if has_flatten { Some(quote! { let mut __collect = _serde::__private::Vec::<_serde::__private::Option<( _serde::__private::de::Content, @@ -2520,7 +2587,7 @@ fn deserialize_map( }); // Visit ignored values to consume them - let ignored_arm = if cattrs.has_flatten() { + let ignored_arm = if has_flatten { Some(quote! { __Field::__other(__name) => { __collect.push(_serde::__private::Some(( @@ -2590,7 +2657,7 @@ fn deserialize_map( } }); - let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() { + let collected_deny_unknown_fields = if has_flatten && cattrs.deny_unknown_fields() { Some(quote! { if let _serde::__private::Some(_serde::__private::Some((__key, _))) = __collect.into_iter().filter(_serde::__private::Option::is_some).next() @@ -2622,7 +2689,11 @@ fn deserialize_map( attr::Default::Default => Some(quote!( let __default: Self::Value = _serde::__private::Default::default(); )), - attr::Default::Path(path) => Some(quote!( + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + attr::Default::Path(path) => Some(quote_spanned!(path.span()=> let __default: Self::Value = #path(); )), attr::Default::None => { @@ -2666,7 +2737,10 @@ fn deserialize_map_in_place( fields: &[Field], cattrs: &attr::Container, ) -> Fragment { - assert!(!cattrs.has_flatten()); + assert!( + !has_flatten(fields), + "inplace deserialization of maps does not support flatten fields" + ); // Create the field names for the fields. let fields_names: Vec<_> = fields @@ -2789,7 +2863,11 @@ fn deserialize_map_in_place( attr::Default::Default => Some(quote!( let __default: #this_type #ty_generics = _serde::__private::Default::default(); )), - attr::Default::Path(path) => Some(quote!( + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + attr::Default::Path(path) => Some(quote_spanned!(path.span()=> let __default: #this_type #ty_generics = #path(); )), attr::Default::None => { @@ -2827,7 +2905,15 @@ fn wrap_deserialize_with( let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let delife = params.borrowed.de_lifetime(); - + let deserializer_var = quote!(__deserializer); + + // If #deserialize_with returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(with = "...")] + // ^^^^^ + let value = quote_spanned! {deserialize_with.span()=> + #deserialize_with(#deserializer_var)? + }; let wrapper = quote! { #[doc(hidden)] struct __DeserializeWith #de_impl_generics #where_clause { @@ -2836,13 +2922,14 @@ fn wrap_deserialize_with( lifetime: _serde::__private::PhantomData<&#delife ()>, } + #[automatically_derived] impl #de_impl_generics _serde::Deserialize<#delife> for __DeserializeWith #de_ty_generics #where_clause { - fn deserialize<__D>(__deserializer: __D) -> _serde::__private::Result + fn deserialize<__D>(#deserializer_var: __D) -> _serde::__private::Result where __D: _serde::Deserializer<#delife>, { _serde::__private::Ok(__DeserializeWith { - value: #deserialize_with(__deserializer)?, + value: #value, phantom: _serde::__private::PhantomData, lifetime: _serde::__private::PhantomData, }) @@ -2933,7 +3020,11 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { return quote_expr!(#func()); } attr::Default::Path(path) => { - return quote_expr!(#path()); + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ + return Fragment::Expr(quote_spanned!(path.span()=> #path())); } attr::Default::None => { /* below */ } } @@ -2976,6 +3067,10 @@ fn expr_is_missing_seq( return quote_spanned!(span=> #assign_to _serde::__private::Default::default()); } attr::Default::Path(path) => { + // If #path returns wrong type, error will be reported here (^^^^^). + // We attach span of the path to the function so it will be reported + // on the #[serde(default = "...")] + // ^^^^^ return quote_spanned!(path.span()=> #assign_to #path()); } attr::Default::None => { /* below */ } @@ -2999,6 +3094,14 @@ fn effective_style(variant: &Variant) -> Style { } } +/// True if there is any field with a `#[serde(flatten)]` attribute, other than +/// fields which are skipped. +fn has_flatten(fields: &[Field]) -> bool { + fields + .iter() + .any(|field| field.attrs.flatten() && !field.attrs.skip_deserializing()) +} + struct DeImplGenerics<'a>(&'a Parameters); #[cfg(feature = "deserialize_in_place")] struct InPlaceImplGenerics<'a>(&'a Parameters); diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index a28d3ae7e..3293823a7 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -63,7 +63,7 @@ impl<'a> Container<'a> { item: &'a syn::DeriveInput, derive: Derive, ) -> Option> { - let mut attrs = attr::Container::from_ast(cx, item); + let attrs = attr::Container::from_ast(cx, item); let mut data = match &item.data { syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())), @@ -77,15 +77,11 @@ impl<'a> Container<'a> { } }; - let mut has_flatten = false; match &mut data { Data::Enum(variants) => { for variant in variants { variant.attrs.rename_by_rules(attrs.rename_all_rules()); for field in &mut variant.fields { - if field.attrs.flatten() { - has_flatten = true; - } field.attrs.rename_by_rules( variant .attrs @@ -97,18 +93,11 @@ impl<'a> Container<'a> { } Data::Struct(_, fields) => { for field in fields { - if field.attrs.flatten() { - has_flatten = true; - } field.attrs.rename_by_rules(attrs.rename_all_rules()); } } } - if has_flatten { - attrs.mark_has_flatten(); - } - let mut item = Container { ident: item.ident.clone(), attrs, diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 0b25c7c0d..6d846ed01 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -1,3 +1,4 @@ +use crate::internals::name::{MultiName, Name}; use crate::internals::symbol::*; use crate::internals::{ungroup, Ctxt}; use proc_macro2::{Spacing, Span, TokenStream, TokenTree}; @@ -8,6 +9,7 @@ use std::iter::FromIterator; use syn::meta::ParseNestedMeta; use syn::parse::ParseStream; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::{parse_quote, token, Ident, Lifetime, Token}; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints @@ -20,7 +22,7 @@ use syn::{parse_quote, token, Ident, Lifetime, Token}; pub use crate::internals::case::RenameRule; -struct Attr<'c, T> { +pub(crate) struct Attr<'c, T> { cx: &'c Ctxt, name: Symbol, tokens: TokenStream, @@ -61,7 +63,7 @@ impl<'c, T> Attr<'c, T> { } } - fn get(self) -> Option { + pub(crate) fn get(self) -> Option { self.value } @@ -89,7 +91,7 @@ impl<'c> BoolAttr<'c> { } } -struct VecAttr<'c, T> { +pub(crate) struct VecAttr<'c, T> { cx: &'c Ctxt, name: Symbol, first_dup_tokens: TokenStream, @@ -124,69 +126,19 @@ impl<'c, T> VecAttr<'c, T> { } } - fn get(self) -> Vec { + pub(crate) fn get(self) -> Vec { self.values } } -pub struct Name { - serialize: String, - serialize_renamed: bool, - deserialize: String, - deserialize_renamed: bool, - deserialize_aliases: BTreeSet, -} - -fn unraw(ident: &Ident) -> String { - ident.to_string().trim_start_matches("r#").to_owned() -} - -impl Name { - fn from_attrs( - source_name: String, - ser_name: Attr, - de_name: Attr, - de_aliases: Option>, - ) -> Name { - let mut alias_set = BTreeSet::new(); - if let Some(de_aliases) = de_aliases { - for alias_name in de_aliases.get() { - alias_set.insert(alias_name); - } - } - - let ser_name = ser_name.get(); - let ser_renamed = ser_name.is_some(); - let de_name = de_name.get(); - let de_renamed = de_name.is_some(); - Name { - serialize: ser_name.unwrap_or_else(|| source_name.clone()), - serialize_renamed: ser_renamed, - deserialize: de_name.unwrap_or(source_name), - deserialize_renamed: de_renamed, - deserialize_aliases: alias_set, - } - } - - /// Return the container name for the container when serializing. - pub fn serialize_name(&self) -> &str { - &self.serialize - } - - /// Return the container name for the container when deserializing. - pub fn deserialize_name(&self) -> &str { - &self.deserialize - } - - fn deserialize_aliases(&self) -> &BTreeSet { - &self.deserialize_aliases - } +fn unraw(ident: &Ident) -> Ident { + Ident::new(ident.to_string().trim_start_matches("r#"), ident.span()) } #[derive(Copy, Clone)] pub struct RenameAllRules { - serialize: RenameRule, - deserialize: RenameRule, + pub serialize: RenameRule, + pub deserialize: RenameRule, } impl RenameAllRules { @@ -202,7 +154,7 @@ impl RenameAllRules { /// Represents struct or enum attribute information. pub struct Container { - name: Name, + name: MultiName, transparent: bool, deny_unknown_fields: bool, default: Default, @@ -216,7 +168,6 @@ pub struct Container { type_into: Option, remote: Option, identifier: Identifier, - has_flatten: bool, serde_path: Option, is_packed: bool, /// Error message generated when type can't be deserialized @@ -327,8 +278,8 @@ impl Container { // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] let (ser, de) = get_renames(cx, RENAME, &meta)?; - ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value)); - de_name.set_opt(&meta.path, de.as_ref().map(syn::LitStr::value)); + ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from)); + de_name.set_opt(&meta.path, de.as_ref().map(Name::from)); } else if meta.path == RENAME_ALL { // #[serde(rename_all = "foo")] // #[serde(rename_all(serialize = "foo", deserialize = "bar"))] @@ -567,7 +518,7 @@ impl Container { } Container { - name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None), + name: MultiName::from_attrs(Name::from(&unraw(&item.ident)), ser_name, de_name, None), transparent: transparent.get(), deny_unknown_fields: deny_unknown_fields.get(), default: default.get().unwrap_or(Default::None), @@ -587,7 +538,6 @@ impl Container { type_into: type_into.get(), remote: remote.get(), identifier: decide_identifier(cx, item, field_identifier, variant_identifier), - has_flatten: false, serde_path: serde_path.get(), is_packed, expecting: expecting.get(), @@ -595,7 +545,7 @@ impl Container { } } - pub fn name(&self) -> &Name { + pub fn name(&self) -> &MultiName { &self.name } @@ -655,14 +605,6 @@ impl Container { self.identifier } - pub fn has_flatten(&self) -> bool { - self.has_flatten - } - - pub fn mark_has_flatten(&mut self) { - self.has_flatten = true; - } - pub fn custom_serde_path(&self) -> Option<&syn::Path> { self.serde_path.as_ref() } @@ -790,7 +732,7 @@ fn decide_identifier( /// Represents variant attribute information pub struct Variant { - name: Name, + name: MultiName, rename_all_rules: RenameAllRules, ser_bound: Option>, de_bound: Option>, @@ -841,15 +783,15 @@ impl Variant { // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] let (ser, de) = get_multiple_renames(cx, &meta)?; - ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value)); + ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from)); for de_value in de { - de_name.set_if_none(de_value.value()); - de_aliases.insert(&meta.path, de_value.value()); + de_name.set_if_none(Name::from(&de_value)); + de_aliases.insert(&meta.path, Name::from(&de_value)); } } else if meta.path == ALIAS { // #[serde(alias = "foo")] if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { - de_aliases.insert(&meta.path, s.value()); + de_aliases.insert(&meta.path, Name::from(&s)); } } else if meta.path == RENAME_ALL { // #[serde(rename_all = "foo")] @@ -898,13 +840,13 @@ impl Variant { ser_path .path .segments - .push(Ident::new("serialize", Span::call_site()).into()); + .push(Ident::new("serialize", ser_path.span()).into()); serialize_with.set(&meta.path, ser_path); let mut de_path = path; de_path .path .segments - .push(Ident::new("deserialize", Span::call_site()).into()); + .push(Ident::new("deserialize", de_path.span()).into()); deserialize_with.set(&meta.path, de_path); } } else if meta.path == SERIALIZE_WITH { @@ -956,7 +898,12 @@ impl Variant { } Variant { - name: Name::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_aliases)), + name: MultiName::from_attrs( + Name::from(&unraw(&variant.ident)), + ser_name, + de_name, + Some(de_aliases), + ), rename_all_rules: RenameAllRules { serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), @@ -973,20 +920,23 @@ impl Variant { } } - pub fn name(&self) -> &Name { + pub fn name(&self) -> &MultiName { &self.name } - pub fn aliases(&self) -> &BTreeSet { + pub fn aliases(&self) -> &BTreeSet { self.name.deserialize_aliases() } pub fn rename_by_rules(&mut self, rules: RenameAllRules) { if !self.name.serialize_renamed { - self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize); + self.name.serialize.value = + rules.serialize.apply_to_variant(&self.name.serialize.value); } if !self.name.deserialize_renamed { - self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize); + self.name.deserialize.value = rules + .deserialize + .apply_to_variant(&self.name.deserialize.value); } self.name .deserialize_aliases @@ -1032,7 +982,7 @@ impl Variant { /// Represents field attribute information pub struct Field { - name: Name, + name: MultiName, skip_serializing: bool, skip_deserializing: bool, skip_serializing_if: Option, @@ -1091,8 +1041,11 @@ impl Field { let mut flatten = BoolAttr::none(cx, FLATTEN); let ident = match &field.ident { - Some(ident) => unraw(ident), - None => index.to_string(), + Some(ident) => Name::from(&unraw(ident)), + None => Name { + value: index.to_string(), + span: Span::call_site(), + }, }; if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) { @@ -1128,15 +1081,15 @@ impl Field { // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] let (ser, de) = get_multiple_renames(cx, &meta)?; - ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value)); + ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from)); for de_value in de { - de_name.set_if_none(de_value.value()); - de_aliases.insert(&meta.path, de_value.value()); + de_name.set_if_none(Name::from(&de_value)); + de_aliases.insert(&meta.path, Name::from(&de_value)); } } else if meta.path == ALIAS { // #[serde(alias = "foo")] if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { - de_aliases.insert(&meta.path, s.value()); + de_aliases.insert(&meta.path, Name::from(&s)); } } else if meta.path == DEFAULT { if meta.input.peek(Token![=]) { @@ -1180,13 +1133,13 @@ impl Field { ser_path .path .segments - .push(Ident::new("serialize", Span::call_site()).into()); + .push(Ident::new("serialize", ser_path.span()).into()); serialize_with.set(&meta.path, ser_path); let mut de_path = path; de_path .path .segments - .push(Ident::new("deserialize", Span::call_site()).into()); + .push(Ident::new("deserialize", de_path.span()).into()); deserialize_with.set(&meta.path, de_path); } } else if meta.path == BOUND { @@ -1299,7 +1252,7 @@ impl Field { } Field { - name: Name::from_attrs(ident, ser_name, de_name, Some(de_aliases)), + name: MultiName::from_attrs(ident, ser_name, de_name, Some(de_aliases)), skip_serializing: skip_serializing.get(), skip_deserializing: skip_deserializing.get(), skip_serializing_if: skip_serializing_if.get(), @@ -1315,20 +1268,22 @@ impl Field { } } - pub fn name(&self) -> &Name { + pub fn name(&self) -> &MultiName { &self.name } - pub fn aliases(&self) -> &BTreeSet { + pub fn aliases(&self) -> &BTreeSet { self.name.deserialize_aliases() } pub fn rename_by_rules(&mut self, rules: RenameAllRules) { if !self.name.serialize_renamed { - self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize); + self.name.serialize.value = rules.serialize.apply_to_field(&self.name.serialize.value); } if !self.name.deserialize_renamed { - self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize); + self.name.deserialize.value = rules + .deserialize + .apply_to_field(&self.name.deserialize.value); } self.name .deserialize_aliases @@ -1778,7 +1733,7 @@ fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool { // attribute on the field so there must be at least one borrowable lifetime. fn borrowable_lifetimes( cx: &Ctxt, - name: &str, + name: &Name, field: &syn::Field, ) -> Result, ()> { let mut lifetimes = BTreeSet::new(); @@ -1794,6 +1749,7 @@ fn borrowable_lifetimes( fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet) { match ty { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] syn::Type::Slice(ty) => { collect_lifetimes(&ty.elem, out); } @@ -1829,7 +1785,10 @@ fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet) { syn::GenericArgument::AssocType(binding) => { collect_lifetimes(&binding.ty, out); } - _ => {} + syn::GenericArgument::Const(_) + | syn::GenericArgument::AssocConst(_) + | syn::GenericArgument::Constraint(_) + | _ => {} } } } @@ -1851,7 +1810,6 @@ fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet) { | syn::Type::Infer(_) | syn::Type::Verbatim(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => {} } } diff --git a/serde_derive/src/internals/check.rs b/serde_derive/src/internals/check.rs index b01a8ca4d..da2a0fbdc 100644 --- a/serde_derive/src/internals/check.rs +++ b/serde_derive/src/internals/check.rs @@ -318,6 +318,9 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) { for variant in variants { match variant.style { Style::Struct => { + if variant.attrs.untagged() { + continue; + } for field in &variant.fields { let check_ser = !(field.attrs.skip_serializing() || variant.attrs.skip_serializing()); @@ -326,13 +329,13 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) { let name = field.attrs.name(); let ser_name = name.serialize_name(); - if check_ser && ser_name == tag { + if check_ser && ser_name.value == tag { diagnose_conflict(); return; } for de_name in field.attrs.aliases() { - if check_de && de_name == tag { + if check_de && de_name.value == tag { diagnose_conflict(); return; } diff --git a/serde_derive/src/internals/mod.rs b/serde_derive/src/internals/mod.rs index f98ef08e8..cd1e81052 100644 --- a/serde_derive/src/internals/mod.rs +++ b/serde_derive/src/internals/mod.rs @@ -1,5 +1,6 @@ pub mod ast; pub mod attr; +pub mod name; mod case; mod check; diff --git a/serde_derive/src/internals/name.rs b/serde_derive/src/internals/name.rs new file mode 100644 index 000000000..4c59f9636 --- /dev/null +++ b/serde_derive/src/internals/name.rs @@ -0,0 +1,113 @@ +use crate::internals::attr::{Attr, VecAttr}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::ToTokens; +use std::cmp::Ordering; +use std::collections::BTreeSet; +use std::fmt::{self, Display}; +use syn::LitStr; + +pub struct MultiName { + pub(crate) serialize: Name, + pub(crate) serialize_renamed: bool, + pub(crate) deserialize: Name, + pub(crate) deserialize_renamed: bool, + pub(crate) deserialize_aliases: BTreeSet, +} + +impl MultiName { + pub(crate) fn from_attrs( + source_name: Name, + ser_name: Attr, + de_name: Attr, + de_aliases: Option>, + ) -> Self { + let mut alias_set = BTreeSet::new(); + if let Some(de_aliases) = de_aliases { + for alias_name in de_aliases.get() { + alias_set.insert(alias_name); + } + } + + let ser_name = ser_name.get(); + let ser_renamed = ser_name.is_some(); + let de_name = de_name.get(); + let de_renamed = de_name.is_some(); + MultiName { + serialize: ser_name.unwrap_or_else(|| source_name.clone()), + serialize_renamed: ser_renamed, + deserialize: de_name.unwrap_or(source_name), + deserialize_renamed: de_renamed, + deserialize_aliases: alias_set, + } + } + + /// Return the container name for the container when serializing. + pub fn serialize_name(&self) -> &Name { + &self.serialize + } + + /// Return the container name for the container when deserializing. + pub fn deserialize_name(&self) -> &Name { + &self.deserialize + } + + pub(crate) fn deserialize_aliases(&self) -> &BTreeSet { + &self.deserialize_aliases + } +} + +#[derive(Clone)] +pub struct Name { + pub value: String, + pub span: Span, +} + +impl ToTokens for Name { + fn to_tokens(&self, tokens: &mut TokenStream) { + LitStr::new(&self.value, self.span).to_tokens(tokens); + } +} + +impl Ord for Name { + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.value, &other.value) + } +} + +impl PartialOrd for Name { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } +} + +impl Eq for Name {} + +impl PartialEq for Name { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} + +impl From<&Ident> for Name { + fn from(ident: &Ident) -> Self { + Name { + value: ident.to_string(), + span: ident.span(), + } + } +} + +impl From<&LitStr> for Name { + fn from(lit: &LitStr) -> Self { + Name { + value: lit.value(), + span: lit.span(), + } + } +} + +impl Display for Name { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.value, formatter) + } +} diff --git a/serde_derive/src/internals/receiver.rs b/serde_derive/src/internals/receiver.rs index 6273dfece..1e7fc54f1 100644 --- a/serde_derive/src/internals/receiver.rs +++ b/serde_derive/src/internals/receiver.rs @@ -84,7 +84,7 @@ impl ReplaceReceiver<'_> { self.visit_type_mut_impl(ty); return; }; - *ty = self.self_ty(span).into(); + *ty = Type::Path(self.self_ty(span)); } // `Self::Assoc` -> `::Assoc` @@ -107,6 +107,7 @@ impl ReplaceReceiver<'_> { fn visit_type_mut_impl(&mut self, ty: &mut Type) { match ty { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Type::Array(ty) => { self.visit_type_mut(&mut ty.elem); self.visit_expr_mut(&mut ty.len); @@ -147,7 +148,6 @@ impl ReplaceReceiver<'_> { Type::Infer(_) | Type::Never(_) | Type::Verbatim(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => {} } } @@ -178,13 +178,13 @@ impl ReplaceReceiver<'_> { PathArguments::AngleBracketed(arguments) => { for arg in &mut arguments.args { match arg { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] GenericArgument::Type(arg) => self.visit_type_mut(arg), GenericArgument::AssocType(arg) => self.visit_type_mut(&mut arg.ty), GenericArgument::Lifetime(_) | GenericArgument::Const(_) | GenericArgument::AssocConst(_) | GenericArgument::Constraint(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => {} } } @@ -207,9 +207,11 @@ impl ReplaceReceiver<'_> { fn visit_type_param_bound_mut(&mut self, bound: &mut TypeParamBound) { match bound { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TypeParamBound::Trait(bound) => self.visit_path_mut(&mut bound.path), - TypeParamBound::Lifetime(_) | TypeParamBound::Verbatim(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] + TypeParamBound::Lifetime(_) + | TypeParamBound::PreciseCapture(_) + | TypeParamBound::Verbatim(_) => {} _ => {} } } @@ -228,6 +230,7 @@ impl ReplaceReceiver<'_> { if let Some(where_clause) = &mut generics.where_clause { for predicate in &mut where_clause.predicates { match predicate { + #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] WherePredicate::Type(predicate) => { self.visit_type_mut(&mut predicate.bounded_ty); for bound in &mut predicate.bounds { @@ -235,7 +238,6 @@ impl ReplaceReceiver<'_> { } } WherePredicate::Lifetime(_) => {} - #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => {} } } diff --git a/serde_derive/src/internals/symbol.rs b/serde_derive/src/internals/symbol.rs index 572391a80..59ef8de7c 100644 --- a/serde_derive/src/internals/symbol.rs +++ b/serde_derive/src/internals/symbol.rs @@ -46,7 +46,7 @@ impl PartialEq for Ident { } } -impl<'a> PartialEq for &'a Ident { +impl PartialEq for &Ident { fn eq(&self, word: &Symbol) -> bool { *self == word.0 } @@ -58,7 +58,7 @@ impl PartialEq for Path { } } -impl<'a> PartialEq for &'a Path { +impl PartialEq for &Path { fn eq(&self, word: &Symbol) -> bool { self.is_ident(word.0) } diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index d094242f3..5216446ba 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -13,7 +13,8 @@ //! //! [https://serde.rs/derive.html]: https://serde.rs/derive.html -#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.188")] +#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.218")] +#![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] // Ignored clippy lints #![allow( // clippy false positive: https://github.com/rust-lang/rust-clippy/issues/7054 @@ -26,6 +27,7 @@ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6797 clippy::manual_map, clippy::match_like_matches_macro, + clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::too_many_arguments, clippy::trivially_copy_pass_by_ref, @@ -50,11 +52,11 @@ clippy::match_wildcard_for_single_variants, clippy::module_name_repetitions, clippy::must_use_candidate, - clippy::option_if_let_else, clippy::similar_names, clippy::single_match_else, clippy::struct_excessive_bools, clippy::too_many_lines, + clippy::uninlined_format_args, clippy::unseparated_literal_suffix, clippy::unused_self, clippy::use_self, diff --git a/serde_derive/src/pretend.rs b/serde_derive/src/pretend.rs index 04868753b..2c9e77936 100644 --- a/serde_derive/src/pretend.rs +++ b/serde_derive/src/pretend.rs @@ -64,14 +64,14 @@ pub fn pretend_used(cont: &Container, is_packed: bool) -> TokenStream { fn pretend_fields_used(cont: &Container, is_packed: bool) -> TokenStream { match &cont.data { Data::Enum(variants) => pretend_fields_used_enum(cont, variants), - Data::Struct(Style::Struct, fields) => { + Data::Struct(Style::Struct | Style::Tuple | Style::Newtype, fields) => { if is_packed { pretend_fields_used_struct_packed(cont, fields) } else { pretend_fields_used_struct(cont, fields) } } - Data::Struct(_, _) => quote!(), + Data::Struct(Style::Unit, _) => quote!(), } } @@ -115,13 +115,13 @@ fn pretend_fields_used_enum(cont: &Container, variants: &[Variant]) -> TokenStre let patterns = variants .iter() .filter_map(|variant| match variant.style { - Style::Struct => { + Style::Struct | Style::Tuple | Style::Newtype => { let variant_ident = &variant.ident; let members = variant.fields.iter().map(|field| &field.member); let placeholders = (0usize..).map(|i| format_ident!("__v{}", i)); Some(quote!(#type_ident::#variant_ident { #(#members: #placeholders),* })) } - _ => None, + Style::Unit => None, }) .collect::>(); diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 3be51ee52..c92800d51 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1,5 +1,6 @@ use crate::fragment::{Fragment, Match, Stmts}; use crate::internals::ast::{Container, Data, Field, Style, Variant}; +use crate::internals::name::Name; use crate::internals::{attr, replace_receiver, Ctxt, Derive}; use crate::{bound, dummy, pretend, this}; use proc_macro2::{Span, TokenStream}; @@ -28,6 +29,7 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result(__self: &#remote #ty_generics, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> where @@ -289,9 +291,18 @@ fn serialize_tuple_struct( } fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { - assert!(fields.len() as u64 <= u64::from(u32::max_value())); + assert!( + fields.len() as u64 <= u64::from(u32::MAX), + "too many fields in {}: {}, maximum supported count is {}", + cattrs.name().serialize_name(), + fields.len(), + u32::MAX, + ); - if cattrs.has_flatten() { + let has_non_skipped_flatten = fields + .iter() + .any(|field| field.attrs.flatten() && !field.attrs.skip_serializing()); + if has_non_skipped_flatten { serialize_struct_as_map(params, fields, cattrs) } else { serialize_struct_as_struct(params, fields, cattrs) @@ -370,26 +381,8 @@ fn serialize_struct_as_map( let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists); - let len = if cattrs.has_flatten() { - quote!(_serde::__private::None) - } else { - let len = serialized_fields - .map(|field| match field.attrs.skip_serializing_if() { - None => quote!(1), - Some(path) => { - let field_expr = get_member(params, field, &field.member); - quote!(if #path(#field_expr) { 0 } else { 1 }) - } - }) - .fold( - quote!(#tag_field_exists as usize), - |sum, expr| quote!(#sum + #expr), - ); - quote!(_serde::__private::Some(#len)) - }; - quote_block! { - let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, #len)?; + let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, _serde::__private::None)?; #tag_field #(#serialize_fields)* _serde::ser::SerializeMap::end(__serde_state) @@ -397,7 +390,7 @@ fn serialize_struct_as_map( } fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment { - assert!(variants.len() as u64 <= u64::from(u32::max_value())); + assert!(variants.len() as u64 <= u64::from(u32::MAX)); let self_var = ¶ms.self_var; @@ -741,6 +734,7 @@ fn serialize_adjacently_tagged_variant( phantom: _serde::__private::PhantomData<#this_type #ty_generics>, } + #[automatically_derived] impl #wrapper_impl_generics _serde::Serialize for __AdjacentlyTagged #wrapper_ty_generics #where_clause { fn serialize<__S>(&self, __serializer: __S) -> _serde::__private::Result<__S::Ok, __S::Error> where @@ -807,9 +801,9 @@ fn serialize_untagged_variant( enum TupleVariant<'a> { ExternallyTagged { - type_name: &'a str, + type_name: &'a Name, variant_index: u32, - variant_name: &'a str, + variant_name: &'a Name, }, Untagged, } @@ -876,11 +870,11 @@ fn serialize_tuple_variant( enum StructVariant<'a> { ExternallyTagged { variant_index: u32, - variant_name: &'a str, + variant_name: &'a Name, }, InternallyTagged { tag: &'a str, - variant_name: &'a str, + variant_name: &'a Name, }, Untagged, } @@ -889,7 +883,7 @@ fn serialize_struct_variant( context: StructVariant, params: &Parameters, fields: &[Field], - name: &str, + name: &Name, ) -> Fragment { if fields.iter().any(|field| field.attrs.flatten()) { return serialize_struct_variant_with_flatten(context, params, fields, name); @@ -973,7 +967,7 @@ fn serialize_struct_variant_with_flatten( context: StructVariant, params: &Parameters, fields: &[Field], - name: &str, + name: &Name, ) -> Fragment { let struct_trait = StructTrait::SerializeMap; let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait); @@ -1005,6 +999,7 @@ fn serialize_struct_variant_with_flatten( phantom: _serde::__private::PhantomData<#this_type #ty_generics>, } + #[automatically_derived] impl #wrapper_impl_generics _serde::Serialize for __EnumFlatten #wrapper_ty_generics #where_clause { fn serialize<__S>(&self, __serializer: __S) -> _serde::__private::Result<__S::Ok, __S::Error> where @@ -1229,6 +1224,17 @@ fn wrap_serialize_with( }) }); + let self_var = quote!(self); + let serializer_var = quote!(__s); + + // If #serialize_with returns wrong type, error will be reported on here. + // We attach span of the path to this piece so error will be reported + // on the #[serde(with = "...")] + // ^^^^^ + let wrapper_serialize = quote_spanned! {serialize_with.span()=> + #serialize_with(#(#self_var.values.#field_access, )* #serializer_var) + }; + quote!({ #[doc(hidden)] struct __SerializeWith #wrapper_impl_generics #where_clause { @@ -1236,12 +1242,13 @@ fn wrap_serialize_with( phantom: _serde::__private::PhantomData<#this_type #ty_generics>, } + #[automatically_derived] impl #wrapper_impl_generics _serde::Serialize for __SerializeWith #wrapper_ty_generics #where_clause { - fn serialize<__S>(&self, __s: __S) -> _serde::__private::Result<__S::Ok, __S::Error> + fn serialize<__S>(&#self_var, #serializer_var: __S) -> _serde::__private::Result<__S::Ok, __S::Error> where __S: _serde::Serializer, { - #serialize_with(#(self.values.#field_access, )* __s) + #wrapper_serialize } } diff --git a/serde_derive_internals/Cargo.toml b/serde_derive_internals/Cargo.toml index 4c57a3c3d..20bdbccc3 100644 --- a/serde_derive_internals/Cargo.toml +++ b/serde_derive_internals/Cargo.toml @@ -1,24 +1,31 @@ [package] name = "serde_derive_internals" -version = "0.29.0" # remember to update html_root_url +version = "0.29.1" authors = ["Erick Tryzelaar ", "David Tolnay "] description = "AST representation used by Serde derive macros. Unstable." documentation = "https://docs.rs/serde_derive_internals" +edition = "2015" exclude = ["build.rs"] homepage = "https://serde.rs" keywords = ["serde", "serialization"] license = "MIT OR Apache-2.0" repository = "https://github.com/serde-rs/serde" -rust-version = "1.56" +rust-version = "1.61" [lib] path = "lib.rs" [dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "2.0.28", default-features = false, features = ["clone-impls", "derive", "parsing", "printing"] } +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true, features = ["clone-impls", "derive", "parsing", "printing"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", + "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", +] diff --git a/serde_derive_internals/build.rs b/serde_derive_internals/build.rs index 25b5ef313..b2bbb3a56 100644 --- a/serde_derive_internals/build.rs +++ b/serde_derive_internals/build.rs @@ -1,9 +1,17 @@ use std::path::Path; fn main() { + // Warning: build.rs is not published to crates.io. + println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/mod.rs"); + println!("cargo:rustc-cfg=check_cfg"); + println!("cargo:rustc-check-cfg=cfg(check_cfg)"); + println!("cargo:rustc-check-cfg=cfg(exhaustive)"); + println!("cargo:rustc-check-cfg=cfg(serde_build_from_git)"); + println!("cargo:rustc-check-cfg=cfg(feature, values(\"deserialize_in_place\"))"); + // Sometimes on Windows the git checkout does not correctly wire up the // symlink from serde_derive_internals/src to serde_derive/src/internals. // When this happens we'll just build based on relative paths within the git diff --git a/serde_derive_internals/lib.rs b/serde_derive_internals/lib.rs index 1053b009a..d07485771 100644 --- a/serde_derive_internals/lib.rs +++ b/serde_derive_internals/lib.rs @@ -1,4 +1,5 @@ -#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.29.0")] +#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.29.1")] +#![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] // Ignored clippy lints #![allow( clippy::cognitive_complexity, @@ -8,6 +9,7 @@ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6797 clippy::manual_map, clippy::missing_panics_doc, + clippy::needless_lifetimes, clippy::redundant_field_names, clippy::result_unit_err, clippy::should_implement_trait, @@ -34,6 +36,7 @@ clippy::single_match_else, clippy::struct_excessive_bools, clippy::too_many_lines, + clippy::uninlined_format_args, clippy::unused_self, clippy::wildcard_imports )] diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index b7686baf2..4076e735a 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -13,9 +13,9 @@ serde = { path = "../serde" } [dev-dependencies] automod = "1.0.1" -fnv = "1.0" +foldhash = "0.1" rustversion = "1.0" serde = { path = "../serde", features = ["rc"] } serde_derive = { path = "../serde_derive", features = ["deserialize_in_place"] } serde_test = "1.0.176" -trybuild = { version = "1.0.66", features = ["diff"] } +trybuild = { version = "1.0.97", features = ["diff"] } diff --git a/test_suite/no_std/.gitignore b/test_suite/no_std/.gitignore new file mode 100644 index 000000000..e9e21997b --- /dev/null +++ b/test_suite/no_std/.gitignore @@ -0,0 +1,2 @@ +/target/ +/Cargo.lock diff --git a/test_suite/no_std/Cargo.toml b/test_suite/no_std/Cargo.toml index 5bf7eb2d1..9c8f6aa54 100644 --- a/test_suite/no_std/Cargo.toml +++ b/test_suite/no_std/Cargo.toml @@ -10,4 +10,10 @@ libc = { version = "0.2", default-features = false } serde = { path = "../../serde", default-features = false } serde_derive = { path = "../../serde_derive" } +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + [workspace] diff --git a/test_suite/no_std/src/main.rs b/test_suite/no_std/src/main.rs index 57b3f3151..71d553e10 100644 --- a/test_suite/no_std/src/main.rs +++ b/test_suite/no_std/src/main.rs @@ -1,15 +1,12 @@ -#![allow(internal_features)] -#![feature(lang_items, start)] #![no_std] +#![no_main] -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} +use core::ffi::c_int; -#[lang = "eh_personality"] #[no_mangle] -pub extern "C" fn rust_eh_personality() {} +extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int { + 0 +} #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { @@ -23,21 +20,21 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] -struct Unit; +pub struct Unit; #[derive(Serialize, Deserialize)] -struct Newtype(u8); +pub struct Newtype(u8); #[derive(Serialize, Deserialize)] -struct Tuple(u8, u8); +pub struct Tuple(u8, u8); #[derive(Serialize, Deserialize)] -struct Struct { +pub struct Struct { f: u8, } #[derive(Serialize, Deserialize)] -enum Enum { +pub enum Enum { Unit, Newtype(u8), Tuple(u8, u8), diff --git a/test_suite/tests/compiletest.rs b/test_suite/tests/compiletest.rs index 621660b08..8ab6342f2 100644 --- a/test_suite/tests/compiletest.rs +++ b/test_suite/tests/compiletest.rs @@ -1,6 +1,6 @@ -#[cfg_attr(target_os = "emscripten", ignore)] -#[rustversion::attr(not(nightly), ignore)] -#[cfg_attr(miri, ignore)] +#[cfg_attr(target_os = "emscripten", ignore = "disabled on Emscripten")] +#[rustversion::attr(not(nightly), ignore = "requires nightly")] +#[cfg_attr(miri, ignore = "incompatible with miri")] #[allow(unused_attributes)] #[test] fn ui() { diff --git a/test_suite/tests/macros/mod.rs b/test_suite/tests/macros/mod.rs index d66324464..44b98ae2f 100644 --- a/test_suite/tests/macros/mod.rs +++ b/test_suite/tests/macros/mod.rs @@ -34,9 +34,8 @@ macro_rules! hashset { $(set.insert($value);)+ set }}; - ($hasher:ident @ $($value:expr),+) => {{ - use std::hash::BuildHasherDefault; - let mut set = HashSet::with_hasher(BuildHasherDefault::<$hasher>::default()); + ($hasher:ty; $($value:expr),+) => {{ + let mut set = HashSet::<_, $hasher>::default(); $(set.insert($value);)+ set }}; @@ -51,9 +50,8 @@ macro_rules! hashmap { $(map.insert($key, $value);)+ map }}; - ($hasher:ident @ $($key:expr => $value:expr),+) => {{ - use std::hash::BuildHasherDefault; - let mut map = HashMap::with_hasher(BuildHasherDefault::<$hasher>::default()); + ($hasher:ty; $($key:expr => $value:expr),+) => {{ + let mut map = HashMap::<_, _, $hasher>::default(); $(map.insert($key, $value);)+ map }}; diff --git a/test_suite/tests/regression/issue1904.rs b/test_suite/tests/regression/issue1904.rs new file mode 100644 index 000000000..b1d5c7314 --- /dev/null +++ b/test_suite/tests/regression/issue1904.rs @@ -0,0 +1,66 @@ +#![allow(dead_code)] // we do not read enum fields + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +pub struct Nested; + +#[derive(Deserialize)] +pub enum ExternallyTagged1 { + Tuple(f64, String), + Flatten { + #[serde(flatten)] + nested: Nested, + }, +} + +#[derive(Deserialize)] +pub enum ExternallyTagged2 { + Flatten { + #[serde(flatten)] + nested: Nested, + }, + Tuple(f64, String), +} + +// Internally tagged enums cannot contain tuple variants so not tested here + +#[derive(Deserialize)] +#[serde(tag = "tag", content = "content")] +pub enum AdjacentlyTagged1 { + Tuple(f64, String), + Flatten { + #[serde(flatten)] + nested: Nested, + }, +} + +#[derive(Deserialize)] +#[serde(tag = "tag", content = "content")] +pub enum AdjacentlyTagged2 { + Flatten { + #[serde(flatten)] + nested: Nested, + }, + Tuple(f64, String), +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum Untagged1 { + Tuple(f64, String), + Flatten { + #[serde(flatten)] + nested: Nested, + }, +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum Untagged2 { + Flatten { + #[serde(flatten)] + nested: Nested, + }, + Tuple(f64, String), +} diff --git a/test_suite/tests/regression/issue2371.rs b/test_suite/tests/regression/issue2371.rs index e4832af38..7668af1db 100644 --- a/test_suite/tests/regression/issue2371.rs +++ b/test_suite/tests/regression/issue2371.rs @@ -7,7 +7,9 @@ pub struct Nested; pub enum ExternallyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -17,7 +19,9 @@ pub enum ExternallyTagged { pub enum InternallyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -27,7 +31,9 @@ pub enum InternallyTagged { pub enum AdjacentlyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -37,7 +43,9 @@ pub enum AdjacentlyTagged { pub enum UntaggedWorkaround { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } diff --git a/test_suite/tests/regression/issue2565.rs b/test_suite/tests/regression/issue2565.rs new file mode 100644 index 000000000..f13cacf04 --- /dev/null +++ b/test_suite/tests/regression/issue2565.rs @@ -0,0 +1,48 @@ +use serde_derive::{Deserialize, Serialize}; +use serde_test::{assert_tokens, Token}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +enum Enum { + Simple { + a: i32, + }, + Flatten { + #[serde(flatten)] + flatten: (), + a: i32, + }, +} + +#[test] +fn simple_variant() { + assert_tokens( + &Enum::Simple { a: 42 }, + &[ + Token::StructVariant { + name: "Enum", + variant: "Simple", + len: 1, + }, + Token::Str("a"), + Token::I32(42), + Token::StructVariantEnd, + ], + ); +} + +#[test] +fn flatten_variant() { + assert_tokens( + &Enum::Flatten { flatten: (), a: 42 }, + &[ + Token::NewtypeVariant { + name: "Enum", + variant: "Flatten", + }, + Token::Map { len: None }, + Token::Str("a"), + Token::I32(42), + Token::MapEnd, + ], + ); +} diff --git a/test_suite/tests/regression/issue2792.rs b/test_suite/tests/regression/issue2792.rs new file mode 100644 index 000000000..a8c1604ca --- /dev/null +++ b/test_suite/tests/regression/issue2792.rs @@ -0,0 +1,18 @@ +#![allow(dead_code)] // we do not read enum fields + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +pub enum A { + B { + c: String, + }, + D { + #[serde(flatten)] + e: E, + }, +} + +#[derive(Deserialize)] +pub struct E {} diff --git a/test_suite/tests/regression/issue2844.rs b/test_suite/tests/regression/issue2844.rs new file mode 100644 index 000000000..3ad2012b9 --- /dev/null +++ b/test_suite/tests/regression/issue2844.rs @@ -0,0 +1,33 @@ +#![allow(clippy::trivially_copy_pass_by_ref)] + +use serde_derive::{Deserialize, Serialize}; + +macro_rules! declare_in_macro { + ($with:literal) => { + #[derive(Serialize, Deserialize)] + pub struct S { + #[serde(with = $with)] + f: i32, + } + }; +} + +declare_in_macro!("with"); + +mod with { + use serde::{Deserializer, Serializer}; + + pub fn serialize(_: &i32, _: S) -> Result + where + S: Serializer, + { + unimplemented!() + } + + pub fn deserialize<'de, D>(_: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} diff --git a/test_suite/tests/regression/issue2846.rs b/test_suite/tests/regression/issue2846.rs new file mode 100644 index 000000000..c5258a5b3 --- /dev/null +++ b/test_suite/tests/regression/issue2846.rs @@ -0,0 +1,27 @@ +#![allow(clippy::trivially_copy_pass_by_ref)] + +use serde_derive::Deserialize; + +macro_rules! declare_in_macro { + ($with:literal) => { + #[derive(Deserialize)] + pub struct S( + #[serde(with = $with)] + #[allow(dead_code)] + i32, + ); + }; +} + +declare_in_macro!("with"); + +mod with { + use serde::Deserializer; + + pub fn deserialize<'de, D>(_: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index fa314cbca..878c88981 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -782,7 +782,7 @@ fn test_unknown_field_rename_enum() { variant: "SailorMoon", len: 3, }], - "unknown variant `SailorMoon`, expected `sailor_moon`", + "unknown variant `SailorMoon`, expected `sailor_moon` or `usagi_tsukino`", ); assert_de_tokens_error::( @@ -1607,623 +1607,6 @@ fn test_collect_other() { ); } -#[test] -fn test_unknown_field_in_flatten() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(deny_unknown_fields)] - struct Outer { - dummy: String, - #[serde(flatten)] - inner: Inner, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Inner { - foo: HashMap, - } - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "Outer", - len: 1, - }, - Token::Str("dummy"), - Token::Str("23"), - Token::Str("foo"), - Token::Map { len: None }, - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::U32(2), - Token::MapEnd, - Token::Str("bar"), - Token::U32(23), - Token::StructEnd, - ], - "unknown field `bar`", - ); -} - -#[test] -fn test_complex_flatten() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Outer { - y: u32, - #[serde(flatten)] - first: First, - #[serde(flatten)] - second: Second, - z: u32, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct First { - a: u32, - b: bool, - c: Vec, - d: String, - e: Option, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Second { - f: u32, - } - - assert_de_tokens( - &Outer { - y: 0, - first: First { - a: 1, - b: true, - c: vec!["a".into(), "b".into()], - d: "c".into(), - e: Some(2), - }, - second: Second { f: 3 }, - z: 4, - }, - &[ - Token::Map { len: None }, - Token::Str("y"), - Token::U32(0), - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::Bool(true), - Token::Str("c"), - Token::Seq { len: Some(2) }, - Token::Str("a"), - Token::Str("b"), - Token::SeqEnd, - Token::Str("d"), - Token::Str("c"), - Token::Str("e"), - Token::U64(2), - Token::Str("f"), - Token::U32(3), - Token::Str("z"), - Token::U32(4), - Token::MapEnd, - ], - ); - - assert_ser_tokens( - &Outer { - y: 0, - first: First { - a: 1, - b: true, - c: vec!["a".into(), "b".into()], - d: "c".into(), - e: Some(2), - }, - second: Second { f: 3 }, - z: 4, - }, - &[ - Token::Map { len: None }, - Token::Str("y"), - Token::U32(0), - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::Bool(true), - Token::Str("c"), - Token::Seq { len: Some(2) }, - Token::Str("a"), - Token::Str("b"), - Token::SeqEnd, - Token::Str("d"), - Token::Str("c"), - Token::Str("e"), - Token::Some, - Token::U64(2), - Token::Str("f"), - Token::U32(3), - Token::Str("z"), - Token::U32(4), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_map_twice() { - #[derive(Debug, PartialEq, Deserialize)] - struct Outer { - #[serde(flatten)] - first: BTreeMap, - #[serde(flatten)] - between: Inner, - #[serde(flatten)] - second: BTreeMap, - } - - #[derive(Debug, PartialEq, Deserialize)] - struct Inner { - y: String, - } - - assert_de_tokens( - &Outer { - first: { - let mut first = BTreeMap::new(); - first.insert("x".to_owned(), "X".to_owned()); - first.insert("y".to_owned(), "Y".to_owned()); - first - }, - between: Inner { y: "Y".to_owned() }, - second: { - let mut second = BTreeMap::new(); - second.insert("x".to_owned(), "X".to_owned()); - second - }, - }, - &[ - Token::Map { len: None }, - Token::Str("x"), - Token::Str("X"), - Token::Str("y"), - Token::Str("Y"), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_unit() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Response { - #[serde(flatten)] - data: T, - status: usize, - } - - assert_tokens( - &Response { - data: (), - status: 0, - }, - &[ - Token::Map { len: None }, - Token::Str("status"), - Token::U64(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_unsupported_type() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Outer { - outer: String, - #[serde(flatten)] - inner: String, - } - - assert_ser_tokens_error( - &Outer { - outer: "foo".into(), - inner: "bar".into(), - }, - &[ - Token::Map { len: None }, - Token::Str("outer"), - Token::Str("foo"), - ], - "can only flatten structs and maps (got a string)", - ); - assert_de_tokens_error::( - &[ - Token::Map { len: None }, - Token::Str("outer"), - Token::Str("foo"), - Token::Str("a"), - Token::Str("b"), - Token::MapEnd, - ], - "can only flatten structs and maps", - ); -} - -#[test] -fn test_non_string_keys() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct TestStruct { - name: String, - age: u32, - #[serde(flatten)] - mapping: HashMap, - } - - let mut mapping = HashMap::new(); - mapping.insert(0, 42); - assert_tokens( - &TestStruct { - name: "peter".into(), - age: 3, - mapping, - }, - &[ - Token::Map { len: None }, - Token::Str("name"), - Token::Str("peter"), - Token::Str("age"), - Token::U32(3), - Token::U32(0), - Token::U32(42), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_lifetime_propagation_for_flatten() { - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde(flatten)] - t: T, - } - - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct B<'a> { - #[serde(flatten, borrow)] - t: HashMap<&'a str, u32>, - } - - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct C<'a> { - #[serde(flatten, borrow)] - t: HashMap<&'a [u8], u32>, - } - - let mut owned_map = HashMap::new(); - owned_map.insert("x".to_string(), 42u32); - assert_tokens( - &A { t: owned_map }, - &[ - Token::Map { len: None }, - Token::Str("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - let mut borrowed_map = HashMap::new(); - borrowed_map.insert("x", 42u32); - assert_ser_tokens( - &B { - t: borrowed_map.clone(), - }, - &[ - Token::Map { len: None }, - Token::BorrowedStr("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &B { t: borrowed_map }, - &[ - Token::Map { len: None }, - Token::BorrowedStr("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - let mut borrowed_map = HashMap::new(); - borrowed_map.insert(&b"x"[..], 42u32); - assert_ser_tokens( - &C { - t: borrowed_map.clone(), - }, - &[ - Token::Map { len: None }, - Token::Seq { len: Some(1) }, - Token::U8(120), - Token::SeqEnd, - Token::U32(42), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &C { t: borrowed_map }, - &[ - Token::Map { len: None }, - Token::BorrowedBytes(b"x"), - Token::U32(42), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_externally_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::NewtypeVariant { - name: "Data", - variant: "A", - }, - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_enum_with_skipped_conflict() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t")] - enum Data { - A, - #[serde(skip)] - #[allow(dead_code)] - B { - t: String, - }, - C { - #[serde(default, skip)] - t: String, - }, - } - - let data = Data::C { t: String::new() }; - - assert_tokens( - &data, - &[ - Token::Struct { - name: "Data", - len: 1, - }, - Token::Str("t"), - Token::Str("C"), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t")] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Map { len: None }, - Token::Str("t"), - Token::Str("A"), - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_enum_new_type_with_unit() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t")] - enum Data { - A(()), - } - - assert_tokens( - &Data::A(()), - &[ - Token::Map { len: Some(1) }, - Token::Str("t"), - Token::Str("A"), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_adjacently_tagged_enum_bytes() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t", content = "c")] - enum Data { - A { a: i32 }, - } - - let data = Data::A { a: 0 }; - - assert_tokens( - &data, - &[ - Token::Struct { - name: "Data", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "Data", - variant: "A", - }, - Token::Str("c"), - Token::Struct { name: "A", len: 1 }, - Token::Str("a"), - Token::I32(0), - Token::StructEnd, - Token::StructEnd, - ], - ); - - assert_de_tokens( - &data, - &[ - Token::Struct { - name: "Data", - len: 2, - }, - Token::Bytes(b"t"), - Token::UnitVariant { - name: "Data", - variant: "A", - }, - Token::Bytes(b"c"), - Token::Struct { name: "A", len: 1 }, - Token::Str("a"), - Token::I32(0), - Token::StructEnd, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_adjacently_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t", content = "c")] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Struct { - name: "Data", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "Data", - variant: "A", - }, - Token::Str("c"), - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_untagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(untagged)] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - #[test] fn test_partially_untagged_enum() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2381,98 +1764,33 @@ fn test_partially_untagged_enum_desugared() { } #[test] -fn test_flatten_option() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner1: Option, - #[serde(flatten)] - inner2: Option, - } - +fn test_partially_untagged_internally_tagged_enum() { #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Inner1 { - inner1: i32, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Inner2 { - inner2: i32, - } - - assert_tokens( - &Outer { - inner1: Some(Inner1 { inner1: 1 }), - inner2: Some(Inner2 { inner2: 2 }), - }, - &[ - Token::Map { len: None }, - Token::Str("inner1"), - Token::I32(1), - Token::Str("inner2"), - Token::I32(2), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: Some(Inner1 { inner1: 1 }), - inner2: None, - }, - &[ - Token::Map { len: None }, - Token::Str("inner1"), - Token::I32(1), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: None, - inner2: Some(Inner2 { inner2: 2 }), - }, - &[ - Token::Map { len: None }, - Token::Str("inner2"), - Token::I32(2), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: None, - inner2: None, - }, - &[Token::Map { len: None }, Token::MapEnd], - ); -} - -#[test] -fn test_flatten_ignored_any() { - #[derive(Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner: IgnoredAny, + #[serde(tag = "t")] + enum Data { + A, + B, + #[serde(untagged)] + Var(u32), } - assert_de_tokens( - &Outer { inner: IgnoredAny }, - &[Token::Map { len: None }, Token::MapEnd], - ); + let data = Data::A; assert_de_tokens( - &Outer { inner: IgnoredAny }, + &data, &[ - Token::Struct { - name: "DoNotMatter", - len: 0, - }, - Token::StructEnd, + Token::Map { len: None }, + Token::Str("t"), + Token::Str("A"), + Token::MapEnd, ], ); + + let data = Data::Var(42); + + assert_de_tokens(&data, &[Token::U32(42)]); + + // TODO test error output } #[test] @@ -2513,152 +1831,6 @@ fn test_transparent_tuple_struct() { assert_tokens(&Transparent(false, 1, false, PhantomData), &[Token::U32(1)]); } -#[test] -fn test_internally_tagged_unit_enum_with_unknown_fields() { - #[derive(Deserialize, PartialEq, Debug)] - #[serde(tag = "t")] - enum Data { - A, - } - - let data = Data::A; - - assert_de_tokens( - &data, - &[ - Token::Map { len: None }, - Token::Str("t"), - Token::Str("A"), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_any_after_flatten_struct() { - #[derive(PartialEq, Debug)] - struct Any; - - impl<'de> Deserialize<'de> for Any { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct AnyVisitor; - - impl<'de> Visitor<'de> for AnyVisitor { - type Value = Any; - - fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() - } - - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - while let Some((Any, Any)) = map.next_entry()? {} - Ok(Any) - } - } - - deserializer.deserialize_any(AnyVisitor) - } - } - - #[derive(Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner: Inner, - #[serde(flatten)] - extra: Any, - } - - #[derive(Deserialize, PartialEq, Debug)] - struct Inner { - inner: i32, - } - - let s = Outer { - inner: Inner { inner: 0 }, - extra: Any, - }; - - assert_de_tokens( - &s, - &[ - Token::Map { len: None }, - Token::Str("inner"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_alias_in_flatten_context() { - #[derive(Debug, PartialEq, Deserialize)] - struct Outer { - #[serde(flatten)] - a: AliasStruct, - b: i32, - } - - assert_de_tokens( - &Outer { - a: AliasStruct { - a1: 1, - a2: 2, - a4: 4, - }, - b: 7, - }, - &[ - Token::Struct { - name: "Outer", - len: 4, - }, - Token::Str("a1"), - Token::I32(1), - Token::Str("a2"), - Token::I32(2), - Token::Str("a5"), - Token::I32(4), - Token::Str("b"), - Token::I32(7), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Outer { - a: AliasStruct { - a1: 1, - a2: 2, - a4: 4, - }, - b: 7, - }, - &[ - Token::Struct { - name: "Outer", - len: 4, - }, - Token::Str("a1"), - Token::I32(1), - Token::Str("a2"), - Token::I32(2), - Token::Str("a6"), - Token::I32(4), - Token::Str("b"), - Token::I32(7), - Token::StructEnd, - ], - ); -} - #[test] fn test_expecting_message() { #[derive(Deserialize, PartialEq, Debug)] @@ -2719,69 +1891,10 @@ fn test_expecting_message_externally_tagged_enum() { // Check that #[serde(expecting = "...")] doesn't affect variant identifier error message assert_de_tokens_error::( &[Token::Enum { name: "Enum" }, Token::Unit], - r#"invalid type: unit value, expected variant identifier"#, - ); -} - -#[test] -fn test_expecting_message_internally_tagged_enum() { - #[derive(Deserialize)] - #[serde(tag = "tag")] - #[serde(expecting = "something strange...")] - enum Enum { - InternallyTagged, - } - - assert_de_tokens_error::( - &[Token::Str("InternallyTagged")], - r#"invalid type: string "InternallyTagged", expected something strange..."#, - ); - - // Check that #[serde(expecting = "...")] doesn't affect variant identifier error message - assert_de_tokens_error::( - &[Token::Map { len: None }, Token::Str("tag"), Token::Unit], - r#"invalid type: unit value, expected variant identifier"#, - ); -} - -#[test] -fn test_expecting_message_adjacently_tagged_enum() { - #[derive(Deserialize)] - #[serde(tag = "tag", content = "content")] - #[serde(expecting = "something strange...")] - enum Enum { - AdjacentlyTagged, - } - - assert_de_tokens_error::( - &[Token::Str("AdjacentlyTagged")], - r#"invalid type: string "AdjacentlyTagged", expected something strange..."#, - ); - - assert_de_tokens_error::( - &[Token::Map { len: None }, Token::Unit], - r#"invalid type: unit value, expected "tag", "content", or other ignored fields"#, - ); - - // Check that #[serde(expecting = "...")] doesn't affect variant identifier error message - assert_de_tokens_error::( - &[Token::Map { len: None }, Token::Str("tag"), Token::Unit], - r#"invalid type: unit value, expected variant of enum Enum"#, + "invalid type: unit value, expected variant identifier", ); } -#[test] -fn test_expecting_message_untagged_tagged_enum() { - #[derive(Deserialize)] - #[serde(untagged)] - #[serde(expecting = "something strange...")] - enum Enum { - Untagged, - } - - assert_de_tokens_error::(&[Token::Str("Untagged")], r#"something strange..."#); -} - #[test] fn test_expecting_message_identifier_enum() { #[derive(Deserialize)] @@ -2800,7 +1913,7 @@ fn test_expecting_message_identifier_enum() { assert_de_tokens_error::( &[Token::Unit], - r#"invalid type: unit value, expected something strange..."#, + "invalid type: unit value, expected something strange...", ); assert_de_tokens_error::( @@ -2809,12 +1922,12 @@ fn test_expecting_message_identifier_enum() { Token::Str("Unknown"), Token::None, ], - r#"invalid type: map, expected something strange..."#, + "invalid type: map, expected something strange...", ); assert_de_tokens_error::( &[Token::Unit], - r#"invalid type: unit value, expected something strange..."#, + "invalid type: unit value, expected something strange...", ); assert_de_tokens_error::( @@ -2825,13 +1938,667 @@ fn test_expecting_message_identifier_enum() { Token::Str("Unknown"), Token::None, ], - r#"invalid type: map, expected something strange..."#, + "invalid type: map, expected something strange...", ); } mod flatten { use super::*; + #[test] + fn complex() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + y: u32, + #[serde(flatten)] + first: First, + #[serde(flatten)] + second: Second, + z: u32, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct First { + a: u32, + b: bool, + c: Vec, + d: String, + e: Option, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Second { + f: u32, + } + + assert_de_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { f: 3 }, + z: 4, + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); + + assert_ser_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { f: 3 }, + z: 4, + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::Some, + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); + } + + #[test] + fn map_twice() { + #[derive(Debug, PartialEq, Deserialize)] + struct Outer { + #[serde(flatten)] + first: BTreeMap, + #[serde(flatten)] + between: Inner, + #[serde(flatten)] + second: BTreeMap, + } + + #[derive(Debug, PartialEq, Deserialize)] + struct Inner { + y: String, + } + + assert_de_tokens( + &Outer { + first: { + let mut first = BTreeMap::new(); + first.insert("x".to_owned(), "X".to_owned()); + first.insert("y".to_owned(), "Y".to_owned()); + first + }, + between: Inner { y: "Y".to_owned() }, + second: { + let mut second = BTreeMap::new(); + second.insert("x".to_owned(), "X".to_owned()); + second + }, + }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::Str("X"), + Token::Str("y"), + Token::Str("Y"), + Token::MapEnd, + ], + ); + } + + #[test] + fn unsupported_type() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + outer: String, + #[serde(flatten)] + inner: String, + } + + assert_ser_tokens_error( + &Outer { + outer: "foo".into(), + inner: "bar".into(), + }, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + ], + "can only flatten structs and maps (got a string)", + ); + assert_de_tokens_error::( + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + Token::Str("a"), + Token::Str("b"), + Token::MapEnd, + ], + "can only flatten structs and maps", + ); + } + + #[test] + fn unknown_field() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + struct Outer { + dummy: String, + #[serde(flatten)] + inner: Inner, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Inner { + foo: HashMap, + } + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "Outer", + len: 1, + }, + Token::Str("dummy"), + Token::Str("23"), + Token::Str("foo"), + Token::Map { len: None }, + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::U32(2), + Token::MapEnd, + Token::Str("bar"), + Token::U32(23), + Token::StructEnd, + ], + "unknown field `bar`", + ); + } + + #[test] + fn non_string_keys() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct TestStruct { + name: String, + age: u32, + #[serde(flatten)] + mapping: HashMap, + } + + let mut mapping = HashMap::new(); + mapping.insert(0, 42); + assert_tokens( + &TestStruct { + name: "peter".into(), + age: 3, + mapping, + }, + &[ + Token::Map { len: None }, + Token::Str("name"), + Token::Str("peter"), + Token::Str("age"), + Token::U32(3), + Token::U32(0), + Token::U32(42), + Token::MapEnd, + ], + ); + } + + #[test] + fn lifetime_propagation() { + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde(flatten)] + t: T, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a str, u32>, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct C<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a [u8], u32>, + } + + let mut owned_map = HashMap::new(); + owned_map.insert("x".to_string(), 42u32); + assert_tokens( + &A { t: owned_map }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert("x", 42u32); + assert_ser_tokens( + &B { + t: borrowed_map.clone(), + }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &B { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert(&b"x"[..], 42u32); + assert_ser_tokens( + &C { + t: borrowed_map.clone(), + }, + &[ + Token::Map { len: None }, + Token::Seq { len: Some(1) }, + Token::U8(120), + Token::SeqEnd, + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &C { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedBytes(b"x"), + Token::U32(42), + Token::MapEnd, + ], + ); + } + + // Regression test for https://github.com/serde-rs/serde/issues/1904 + #[test] + fn enum_tuple_and_struct() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum Outer { + Tuple(f64, i32), + Flatten { + #[serde(flatten)] + nested: Nested, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Nested { + a: i32, + b: i32, + } + + assert_tokens( + &Outer::Tuple(1.2, 3), + &[ + Token::TupleVariant { + name: "Outer", + variant: "Tuple", + len: 2, + }, + Token::F64(1.2), + Token::I32(3), + Token::TupleVariantEnd, + ], + ); + assert_tokens( + &Outer::Flatten { + nested: Nested { a: 1, b: 2 }, + }, + &[ + Token::NewtypeVariant { + name: "Outer", + variant: "Flatten", + }, + Token::Map { len: None }, + Token::Str("a"), + Token::I32(1), + Token::Str("b"), + Token::I32(2), + Token::MapEnd, + ], + ); + } + + #[test] + fn option() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner1: Option, + #[serde(flatten)] + inner2: Option, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Inner1 { + inner1: i32, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Inner2 { + inner2: i32, + } + + assert_tokens( + &Outer { + inner1: Some(Inner1 { inner1: 1 }), + inner2: Some(Inner2 { inner2: 2 }), + }, + &[ + Token::Map { len: None }, + Token::Str("inner1"), + Token::I32(1), + Token::Str("inner2"), + Token::I32(2), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: Some(Inner1 { inner1: 1 }), + inner2: None, + }, + &[ + Token::Map { len: None }, + Token::Str("inner1"), + Token::I32(1), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: None, + inner2: Some(Inner2 { inner2: 2 }), + }, + &[ + Token::Map { len: None }, + Token::Str("inner2"), + Token::I32(2), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: None, + inner2: None, + }, + &[Token::Map { len: None }, Token::MapEnd], + ); + } + + #[test] + fn ignored_any() { + #[derive(Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner: IgnoredAny, + } + + assert_de_tokens( + &Outer { inner: IgnoredAny }, + &[Token::Map { len: None }, Token::MapEnd], + ); + + assert_de_tokens( + &Outer { inner: IgnoredAny }, + &[ + Token::Struct { + name: "DoNotMatter", + len: 0, + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn flatten_any_after_flatten_struct() { + #[derive(PartialEq, Debug)] + struct Any; + + impl<'de> Deserialize<'de> for Any { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AnyVisitor; + + impl<'de> Visitor<'de> for AnyVisitor { + type Value = Any; + + fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + while let Some((Any, Any)) = map.next_entry()? {} + Ok(Any) + } + } + + deserializer.deserialize_any(AnyVisitor) + } + } + + #[derive(Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner: Inner, + #[serde(flatten)] + extra: Any, + } + + #[derive(Deserialize, PartialEq, Debug)] + struct Inner { + inner: i32, + } + + let s = Outer { + inner: Inner { inner: 0 }, + extra: Any, + }; + + assert_de_tokens( + &s, + &[ + Token::Map { len: None }, + Token::Str("inner"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + + #[test] + fn alias() { + #[derive(Debug, PartialEq, Deserialize)] + struct Outer { + #[serde(flatten)] + a: AliasStruct, + b: i32, + } + + assert_de_tokens( + &Outer { + a: AliasStruct { + a1: 1, + a2: 2, + a4: 4, + }, + b: 7, + }, + &[ + Token::Struct { + name: "Outer", + len: 4, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a2"), + Token::I32(2), + Token::Str("a5"), + Token::I32(4), + Token::Str("b"), + Token::I32(7), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Outer { + a: AliasStruct { + a1: 1, + a2: 2, + a4: 4, + }, + b: 7, + }, + &[ + Token::Struct { + name: "Outer", + len: 4, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a2"), + Token::I32(2), + Token::Str("a6"), + Token::I32(4), + Token::Str("b"), + Token::I32(7), + Token::StructEnd, + ], + ); + } + + mod unit { + use super::*; + + #[test] + fn unit() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Response { + #[serde(flatten)] + data: T, + status: usize, + } + + assert_tokens( + &Response { + data: (), + status: 0, + }, + &[ + Token::Map { len: None }, + Token::Str("status"), + Token::U64(0), + Token::MapEnd, + ], + ); + } + + #[test] + fn unit_struct() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Response { + #[serde(flatten)] + data: T, + status: usize, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Unit; + + assert_tokens( + &Response { + data: Unit, + status: 0, + }, + &[ + Token::Map { len: None }, + Token::Str("status"), + Token::U64(0), + Token::MapEnd, + ], + ); + } + } + mod enum_ { use super::*; @@ -2839,6 +2606,44 @@ mod flatten { use super::*; use std::iter::FromIterator; + #[test] + fn straightforward() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::NewtypeVariant { + name: "Data", + variant: "A", + }, + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Flatten { #[serde(flatten)] @@ -2850,11 +2655,46 @@ mod flatten { #[derive(Debug, PartialEq, Serialize, Deserialize)] enum Enum { + Unit, Newtype(HashMap), Tuple(u32, u32), Struct { index: u32, value: u32 }, } + #[test] + fn unit() { + let value = Flatten { + data: Enum::Unit, + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; + assert_tokens( + &value, + &[ + Token::Map { len: None }, + // data + Token::Str("Unit"), // variant + Token::Unit, + // extra + Token::Str("extra_key"), + Token::Str("extra value"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // extra + Token::Str("extra_key"), + Token::Str("extra value"), + // data + Token::Str("Unit"), // variant + Token::Unit, + Token::MapEnd, + ], + ); + } + #[test] fn newtype() { assert_tokens( diff --git a/test_suite/tests/test_borrow.rs b/test_suite/tests/test_borrow.rs index f749ec323..a9eee4550 100644 --- a/test_suite/tests/test_borrow.rs +++ b/test_suite/tests/test_borrow.rs @@ -162,7 +162,7 @@ fn test_cow() { #[test] fn test_lifetimes() { #[derive(Deserialize)] - struct Cows<'a, 'b> { + pub struct Cows<'a, 'b> { _copied: Cow<'a, str>, #[serde(borrow)] @@ -178,7 +178,7 @@ fn test_lifetimes() { } #[derive(Deserialize)] - struct Wrap<'a, 'b> { + pub struct Wrap<'a, 'b> { #[serde(borrow = "'b")] _cows: Cows<'a, 'b>, } diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 33dc7351b..2e80fbaf9 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -10,7 +10,7 @@ )] #![cfg_attr(feature = "unstable", feature(never_type))] -use fnv::FnvHasher; +use serde::de::value::{F32Deserializer, F64Deserializer}; use serde::de::{Deserialize, DeserializeOwned, Deserializer, IntoDeserializer}; use serde_derive::Deserialize; use serde_test::{assert_de_tokens, Configure, Token}; @@ -22,7 +22,7 @@ use std::iter; use std::net; use std::num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, - NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Saturating, Wrapping, }; use std::ops::Bound; use std::path::{Path, PathBuf}; @@ -92,7 +92,7 @@ struct StructSkipDefault { #[derive(PartialEq, Debug, Deserialize)] #[serde(default)] -struct StructSkipDefaultGeneric { +pub struct StructSkipDefaultGeneric { #[serde(skip_deserializing)] t: T, } @@ -832,6 +832,26 @@ fn test_f64() { test(1.11, &[Token::F64(1.11)]); } +#[test] +fn test_nan() { + let f32_deserializer = F32Deserializer::::new; + let f64_deserializer = F64Deserializer::::new; + + let pos_f32_nan = f32_deserializer(f32::NAN.copysign(1.0)); + let pos_f64_nan = f64_deserializer(f64::NAN.copysign(1.0)); + assert!(f32::deserialize(pos_f32_nan).unwrap().is_sign_positive()); + assert!(f32::deserialize(pos_f64_nan).unwrap().is_sign_positive()); + assert!(f64::deserialize(pos_f32_nan).unwrap().is_sign_positive()); + assert!(f64::deserialize(pos_f64_nan).unwrap().is_sign_positive()); + + let neg_f32_nan = f32_deserializer(f32::NAN.copysign(-1.0)); + let neg_f64_nan = f64_deserializer(f64::NAN.copysign(-1.0)); + assert!(f32::deserialize(neg_f32_nan).unwrap().is_sign_negative()); + assert!(f32::deserialize(neg_f64_nan).unwrap().is_sign_negative()); + assert!(f64::deserialize(neg_f32_nan).unwrap().is_sign_negative()); + assert!(f64::deserialize(neg_f64_nan).unwrap().is_sign_negative()); +} + #[test] fn test_char() { test('a', &[Token::Char('a')]); @@ -1019,7 +1039,7 @@ fn test_hashset() { ], ); test( - hashset![FnvHasher @ 1, 2, 3], + hashset![foldhash::fast::FixedState; 1, 2, 3], &[ Token::Seq { len: Some(3) }, Token::I32(1), @@ -1254,7 +1274,7 @@ fn test_hashmap() { ], ); test( - hashmap![FnvHasher @ 1 => 2, 3 => 4], + hashmap![foldhash::fast::FixedState; 1 => 2, 3 => 4], &[ Token::Map { len: Some(2) }, Token::I32(1), @@ -1877,6 +1897,46 @@ fn test_range_inclusive() { ); } +#[test] +fn test_range_from() { + test( + 1u32.., + &[ + Token::Struct { + name: "RangeFrom", + len: 1, + }, + Token::Str("start"), + Token::U32(1), + Token::StructEnd, + ], + ); + test( + 1u32.., + &[Token::Seq { len: Some(1) }, Token::U32(1), Token::SeqEnd], + ); +} + +#[test] +fn test_range_to() { + test( + ..2u32, + &[ + Token::Struct { + name: "RangeTo", + len: 1, + }, + Token::Str("end"), + Token::U32(2), + Token::StructEnd, + ], + ); + test( + ..2u32, + &[Token::Seq { len: Some(1) }, Token::U32(2), Token::SeqEnd], + ); +} + #[test] fn test_bound() { test( @@ -2004,6 +2064,43 @@ fn test_wrapping() { test(Wrapping(1usize), &[Token::U64(1)]); } +#[test] +fn test_saturating() { + test(Saturating(1usize), &[Token::U32(1)]); + test(Saturating(1usize), &[Token::U64(1)]); + test(Saturating(0u8), &[Token::I8(0)]); + test(Saturating(0u16), &[Token::I16(0)]); + + // saturate input values at the minimum or maximum value + test(Saturating(u8::MAX), &[Token::U16(u16::MAX)]); + test(Saturating(u8::MAX), &[Token::U16(u8::MAX as u16 + 1)]); + test(Saturating(u16::MAX), &[Token::U32(u32::MAX)]); + test(Saturating(u32::MAX), &[Token::U64(u64::MAX)]); + test(Saturating(u8::MIN), &[Token::I8(i8::MIN)]); + test(Saturating(u16::MIN), &[Token::I16(i16::MIN)]); + test(Saturating(u32::MIN), &[Token::I32(i32::MIN)]); + test(Saturating(i8::MIN), &[Token::I16(i16::MIN)]); + test(Saturating(i16::MIN), &[Token::I32(i32::MIN)]); + test(Saturating(i32::MIN), &[Token::I64(i64::MIN)]); + + test(Saturating(u8::MIN), &[Token::I8(-1)]); + test(Saturating(u16::MIN), &[Token::I16(-1)]); + + #[cfg(target_pointer_width = "64")] + { + test(Saturating(usize::MIN), &[Token::U64(u64::MIN)]); + test(Saturating(usize::MAX), &[Token::U64(u64::MAX)]); + test(Saturating(isize::MIN), &[Token::I64(i64::MIN)]); + test(Saturating(isize::MAX), &[Token::I64(i64::MAX)]); + test(Saturating(0usize), &[Token::I64(i64::MIN)]); + + test( + Saturating(9_223_372_036_854_775_807usize), + &[Token::I64(i64::MAX)], + ); + } +} + #[test] fn test_rc_dst() { test(Rc::::from("s"), &[Token::Str("s")]); diff --git a/test_suite/tests/test_de_error.rs b/test_suite/tests/test_de_error.rs index d4449fdd8..cae5eddab 100644 --- a/test_suite/tests/test_de_error.rs +++ b/test_suite/tests/test_de_error.rs @@ -1434,7 +1434,15 @@ fn test_number_from_string() { fn test_integer_from_float() { assert_de_tokens_error::( &[Token::F32(0.0)], - "invalid type: floating point `0`, expected isize", + "invalid type: floating point `0.0`, expected isize", + ); +} + +#[test] +fn test_nan_no_decimal_point() { + assert_de_tokens_error::( + &[Token::F32(f32::NAN)], + "invalid type: floating point `NaN`, expected isize", ); } @@ -1459,7 +1467,7 @@ fn test_duration_overflow_seq() { assert_de_tokens_error::( &[ Token::Seq { len: Some(2) }, - Token::U64(u64::max_value()), + Token::U64(u64::MAX), Token::U32(1_000_000_000), Token::SeqEnd, ], @@ -1476,7 +1484,7 @@ fn test_duration_overflow_struct() { len: 2, }, Token::Str("secs"), - Token::U64(u64::max_value()), + Token::U64(u64::MAX), Token::Str("nanos"), Token::U32(1_000_000_000), Token::StructEnd, @@ -1490,7 +1498,7 @@ fn test_systemtime_overflow_seq() { assert_de_tokens_error::( &[ Token::Seq { len: Some(2) }, - Token::U64(u64::max_value()), + Token::U64(u64::MAX), Token::U32(1_000_000_000), Token::SeqEnd, ], @@ -1507,7 +1515,7 @@ fn test_systemtime_overflow_struct() { len: 2, }, Token::Str("secs_since_epoch"), - Token::U64(u64::max_value()), + Token::U64(u64::MAX), Token::Str("nanos_since_epoch"), Token::U32(1_000_000_000), Token::StructEnd, @@ -1516,13 +1524,12 @@ fn test_systemtime_overflow_struct() { ); } -#[cfg(systemtime_checked_add)] #[test] fn test_systemtime_overflow() { assert_de_tokens_error::( &[ Token::Seq { len: Some(2) }, - Token::U64(u64::max_value()), + Token::U64(u64::MAX), Token::U32(0), Token::SeqEnd, ], diff --git a/test_suite/tests/test_enum_adjacently_tagged.rs b/test_suite/tests/test_enum_adjacently_tagged.rs new file mode 100644 index 000000000..0bcdbc39e --- /dev/null +++ b/test_suite/tests/test_enum_adjacently_tagged.rs @@ -0,0 +1,799 @@ +#![deny(trivial_numeric_casts)] +#![allow( + clippy::derive_partial_eq_without_eq, + clippy::enum_variant_names, + clippy::redundant_field_names, + clippy::too_many_lines +)] + +use serde_derive::{Deserialize, Serialize}; +use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "t", content = "c")] +enum AdjacentlyTagged { + Unit, + Newtype(T), + Tuple(u8, u8), + Struct { f: u8 }, +} + +mod unit { + use super::*; + + #[test] + fn map_str_tag_only() { + // Map: tag only + assert_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 1, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + + // Map: tag only and incorrect hint for number of elements + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_int_tag_only() { + // Map: tag (as number) only + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 1, + }, + Token::U16(0), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_bytes_tag_only() { + // Map: tag only + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 1, + }, + Token::Bytes(b"t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + + // Map: tag only + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 1, + }, + Token::BorrowedBytes(b"t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_str_tag_content() { + // Map: tag + content + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::Str("c"), + Token::Unit, + Token::StructEnd, + ], + ); + // Map: content + tag + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::Unit, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + + // Map: tag + content + excess fields (f, g, h) + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("f"), + Token::Unit, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::Str("g"), + Token::Unit, + Token::Str("c"), + Token::Unit, + Token::Str("h"), + Token::Unit, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_int_tag_content() { + // Map: tag (as number) + content (as number) + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::U8(0), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::U8(1), + Token::Unit, + Token::StructEnd, + ], + ); + + // Map: content (as number) + tag (as number) + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::U64(1), + Token::Unit, + Token::U64(0), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_bytes_tag_content() { + // Map: tag + content + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::BorrowedBytes(b"t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::BorrowedBytes(b"c"), + Token::Unit, + Token::StructEnd, + ], + ); + + // Map: content + tag + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Bytes(b"c"), + Token::Unit, + Token::Bytes(b"t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn seq_tag_content() { + // Seq: tag and content + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Seq { len: Some(2) }, + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::Unit, + Token::SeqEnd, + ], + ); + + // Seq: tag (as string) and content + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Seq { len: None }, + Token::Str("Unit"), // tag + Token::Unit, // content + Token::SeqEnd, + ], + ); + + // Seq: tag (as borrowed string) and content + assert_de_tokens( + &AdjacentlyTagged::Unit::, + &[ + Token::Seq { len: None }, + Token::BorrowedStr("Unit"), // tag + Token::Unit, // content + Token::SeqEnd, + ], + ); + } +} + +mod newtype { + use super::*; + + #[test] + fn map_tag_only() { + // optional newtype with no content field + assert_de_tokens( + &AdjacentlyTagged::Newtype::>(None), + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 1, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Newtype", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn map_tag_content() { + let value = AdjacentlyTagged::Newtype::(1); + + // Map: tag + content + assert_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Newtype", + }, + Token::Str("c"), + Token::U8(1), + Token::StructEnd, + ], + ); + + // Map: content + tag + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::U8(1), + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Newtype", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn seq() { + let value = AdjacentlyTagged::Newtype::(1); + + // Seq: tag and content + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Newtype", + }, + Token::U8(1), + Token::SeqEnd, + ], + ); + + // Seq: tag (as string) and content + assert_de_tokens( + &value, + &[ + Token::Seq { len: None }, + Token::Str("Newtype"), // tag + Token::U8(1), // content + Token::SeqEnd, + ], + ); + + // Seq: tag (as borrowed string) and content + assert_de_tokens( + &value, + &[ + Token::Seq { len: None }, + Token::BorrowedStr("Newtype"), // tag + Token::U8(1), // content + Token::SeqEnd, + ], + ); + } +} + +#[test] +fn newtype_with_newtype() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct NewtypeStruct(u32); + + assert_de_tokens( + &AdjacentlyTagged::Newtype(NewtypeStruct(5)), + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::NewtypeStruct { + name: "NewtypeStruct", + }, + Token::U32(5), + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Newtype", + }, + Token::StructEnd, + ], + ); +} + +mod tuple { + use super::*; + + #[test] + fn map() { + let value = AdjacentlyTagged::Tuple::(1, 1); + + // Map: tag + content + assert_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Tuple", + }, + Token::Str("c"), + Token::Tuple { len: 2 }, + Token::U8(1), + Token::U8(1), + Token::TupleEnd, + Token::StructEnd, + ], + ); + + // Map: content + tag + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::Tuple { len: 2 }, + Token::U8(1), + Token::U8(1), + Token::TupleEnd, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Tuple", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn seq() { + let value = AdjacentlyTagged::Tuple::(1, 1); + + // Seq: tag + content + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Tuple", + }, + Token::Tuple { len: 2 }, + Token::U8(1), + Token::U8(1), + Token::TupleEnd, + Token::SeqEnd, + ], + ); + } +} + +mod struct_ { + use super::*; + + #[test] + fn map() { + let value = AdjacentlyTagged::Struct:: { f: 1 }; + + // Map: tag + content + assert_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Struct", + }, + Token::Str("c"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructEnd, + Token::StructEnd, + ], + ); + + // Map: content + tag + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructEnd, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Struct", + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn seq() { + let value = AdjacentlyTagged::Struct:: { f: 1 }; + + // Seq: tag + content + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Struct", + }, + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructEnd, + Token::SeqEnd, + ], + ); + } +} + +#[test] +fn struct_with_flatten() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t", content = "c")] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "Data", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "Data", + variant: "A", + }, + Token::Str("c"), + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + Token::StructEnd, + ], + ); +} + +#[test] +fn expecting_message() { + #[derive(Deserialize)] + #[serde(tag = "tag", content = "content")] + #[serde(expecting = "something strange...")] + enum Enum { + AdjacentlyTagged, + } + + assert_de_tokens_error::( + &[Token::Str("AdjacentlyTagged")], + r#"invalid type: string "AdjacentlyTagged", expected something strange..."#, + ); + + assert_de_tokens_error::( + &[Token::Map { len: None }, Token::Unit], + r#"invalid type: unit value, expected "tag", "content", or other ignored fields"#, + ); + + // Check that #[serde(expecting = "...")] doesn't affect variant identifier error message + assert_de_tokens_error::( + &[Token::Map { len: None }, Token::Str("tag"), Token::Unit], + "invalid type: unit value, expected variant of enum Enum", + ); +} + +#[test] +fn partially_untagged() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t", content = "c")] + enum Data { + A(u32), + B, + #[serde(untagged)] + Var(u32), + } + + let data = Data::A(7); + + assert_de_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("t"), + Token::Str("A"), + Token::Str("c"), + Token::U32(7), + Token::MapEnd, + ], + ); + + let data = Data::Var(42); + + assert_de_tokens(&data, &[Token::U32(42)]); + + // TODO test error output +} + +#[test] +fn deny_unknown_fields() { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(tag = "t", content = "c", deny_unknown_fields)] + enum AdjacentlyTagged { + Unit, + } + + assert_de_tokens( + &AdjacentlyTagged::Unit, + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::Str("c"), + Token::Unit, + Token::StructEnd, + ], + ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::Str("c"), + Token::Unit, + Token::Str("h"), + ], + r#"invalid value: string "h", expected "t" or "c""#, + ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("h"), + ], + r#"invalid value: string "h", expected "t" or "c""#, + ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Str("c"), + Token::Unit, + Token::Str("h"), + ], + r#"invalid value: string "h", expected "t" or "c""#, + ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::U64(0), // tag field + Token::UnitVariant { + name: "AdjacentlyTagged", + variant: "Unit", + }, + Token::U64(3), + ], + r#"invalid value: integer `3`, expected "t" or "c""#, + ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AdjacentlyTagged", + len: 2, + }, + Token::Bytes(b"c"), + Token::Unit, + Token::Bytes(b"h"), + ], + r#"invalid value: byte array, expected "t" or "c""#, + ); +} diff --git a/test_suite/tests/test_enum_internally_tagged.rs b/test_suite/tests/test_enum_internally_tagged.rs new file mode 100644 index 000000000..b4d428c4d --- /dev/null +++ b/test_suite/tests/test_enum_internally_tagged.rs @@ -0,0 +1,1478 @@ +#![deny(trivial_numeric_casts)] +#![allow( + clippy::derive_partial_eq_without_eq, + clippy::enum_variant_names, + clippy::redundant_field_names, + clippy::too_many_lines +)] + +mod bytes; + +use serde_derive::{Deserialize, Serialize}; +use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; +use std::collections::BTreeMap; +use std::iter::FromIterator; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Unit; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Newtype(BTreeMap); + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Struct { + f: u8, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +enum Enum { + Unit, + Newtype(u8), + Tuple(u8, u8), + Struct { f: u8 }, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "tag")] +enum InternallyTagged { + Unit, + NewtypeUnit(()), + NewtypeUnitStruct(Unit), + NewtypeNewtype(Newtype), + NewtypeMap(BTreeMap), + NewtypeStruct(Struct), + NewtypeEnum(Enum), + Struct { a: u8 }, + StructEnum { enum_: Enum }, +} + +#[test] +fn unit() { + assert_tokens( + &InternallyTagged::Unit, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::Str("tag"), + Token::Str("Unit"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &InternallyTagged::Unit, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("Unit"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::Unit, + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("Unit"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &InternallyTagged::Unit, + &[ + Token::Map { len: Some(1) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("Unit"), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::Unit, + &[ + Token::Seq { len: Some(1) }, + Token::Str("Unit"), // tag + Token::SeqEnd, + ], + ); + assert_de_tokens( + &InternallyTagged::Unit, + &[ + Token::Seq { len: Some(1) }, + Token::BorrowedStr("Unit"), // tag + Token::SeqEnd, + ], + ); +} + +#[test] +fn newtype_unit() { + let value = InternallyTagged::NewtypeUnit(()); + + assert_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("NewtypeUnit"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeUnit"), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::Str("tag"), + Token::Str("NewtypeUnit"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeUnit"), + Token::StructEnd, + ], + ); +} + +#[test] +fn newtype_unit_struct() { + let value = InternallyTagged::NewtypeUnitStruct(Unit); + + assert_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("NewtypeUnitStruct"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeUnitStruct"), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::Str("tag"), + Token::Str("NewtypeUnitStruct"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 1, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeUnitStruct"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(1) }, + Token::Str("NewtypeUnitStruct"), // tag + Token::SeqEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(1) }, + Token::BorrowedStr("NewtypeUnitStruct"), // tag + Token::SeqEnd, + ], + ); +} + +#[test] +fn newtype_newtype() { + assert_tokens( + &InternallyTagged::NewtypeNewtype(Newtype(BTreeMap::new())), + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("NewtypeNewtype"), + Token::MapEnd, + ], + ); +} + +#[test] +fn newtype_map() { + let value = InternallyTagged::NewtypeMap(BTreeMap::new()); + + // Special case: empty map + assert_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("NewtypeMap"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(1) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeMap"), + Token::MapEnd, + ], + ); + + let value = InternallyTagged::NewtypeMap(BTreeMap::from_iter([( + "field".to_string(), + "value".to_string(), + )])); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeMap"), + Token::Str("field"), + Token::Str("value"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeMap"), + Token::BorrowedStr("field"), + Token::BorrowedStr("value"), + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("field"), + Token::Str("value"), + Token::Str("tag"), + Token::Str("NewtypeMap"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("field"), + Token::BorrowedStr("value"), + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeMap"), + Token::MapEnd, + ], + ); + + assert_de_tokens_error::( + &[ + Token::Seq { len: Some(2) }, + Token::Str("NewtypeMap"), // tag + Token::Map { len: Some(0) }, + Token::MapEnd, + Token::SeqEnd, + ], + "invalid type: sequence, expected a map", + ); +} + +#[test] +fn newtype_struct() { + let value = InternallyTagged::NewtypeStruct(Struct { f: 6 }); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Struct { + name: "Struct", + len: 2, + }, + Token::Str("tag"), + Token::Str("NewtypeStruct"), + Token::Str("f"), + Token::U8(6), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "Struct", + len: 2, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeStruct"), + Token::BorrowedStr("f"), + Token::U8(6), + Token::StructEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "Struct", + len: 2, + }, + Token::Str("f"), + Token::U8(6), + Token::Str("tag"), + Token::Str("NewtypeStruct"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "Struct", + len: 2, + }, + Token::BorrowedStr("f"), + Token::U8(6), + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeStruct"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::Str("NewtypeStruct"), // tag + Token::U8(6), + Token::SeqEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::BorrowedStr("NewtypeStruct"), // tag + Token::U8(6), + Token::SeqEnd, + ], + ); +} + +mod newtype_enum { + use super::*; + + #[test] + fn unit() { + let value = InternallyTagged::NewtypeEnum(Enum::Unit); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::Str("Unit"), + Token::Unit, + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::BorrowedStr("Unit"), + Token::Unit, + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("Unit"), + Token::Unit, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("Unit"), + Token::Unit, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::MapEnd, + ], + ); + } + + #[test] + fn newtype() { + let value = InternallyTagged::NewtypeEnum(Enum::Newtype(1)); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::Str("Newtype"), + Token::U8(1), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::BorrowedStr("Newtype"), + Token::U8(1), + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("Newtype"), + Token::U8(1), + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("Newtype"), + Token::U8(1), + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::MapEnd, + ], + ); + } + + #[test] + fn tuple() { + let value = InternallyTagged::NewtypeEnum(Enum::Tuple(1, 1)); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::Str("Tuple"), + Token::TupleStruct { + name: "Tuple", + len: 2, + }, + Token::U8(1), + Token::U8(1), + Token::TupleStructEnd, + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::BorrowedStr("Tuple"), + Token::TupleStruct { + name: "Tuple", + len: 2, + }, + Token::U8(1), + Token::U8(1), + Token::TupleStructEnd, + Token::MapEnd, + ], + ); + + // Special case: tag field ("tag") is not the first field + // Reaches crate::private::de::content::VariantDeserializer::tuple_variant + // Content::Seq case + // via ContentDeserializer::deserialize_enum + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("Tuple"), + Token::TupleStruct { + name: "Tuple", + len: 2, + }, + Token::U8(1), + Token::U8(1), + Token::TupleStructEnd, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("Tuple"), + Token::TupleStruct { + name: "Tuple", + len: 2, + }, + Token::U8(1), + Token::U8(1), + Token::TupleStructEnd, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::MapEnd, + ], + ); + } + + #[test] + fn struct_() { + let value = InternallyTagged::NewtypeEnum(Enum::Struct { f: 1 }); + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::Str("Struct"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructEnd, + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::BorrowedStr("Struct"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::BorrowedStr("f"), + Token::U8(1), + Token::StructEnd, + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + // Reaches crate::private::de::content::VariantDeserializer::struct_variant + // Content::Map case + // via ContentDeserializer::deserialize_enum + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("Struct"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructEnd, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("Struct"), + Token::Struct { + name: "Struct", + len: 1, + }, + Token::BorrowedStr("f"), + Token::U8(1), + Token::StructEnd, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::MapEnd, + ], + ); + + // Special case: tag field ("tag") is the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::Str("Struct"), + Token::Seq { len: Some(1) }, + Token::U8(1), // f + Token::SeqEnd, + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::BorrowedStr("Struct"), + Token::Seq { len: Some(1) }, + Token::U8(1), // f + Token::SeqEnd, + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + // Reaches crate::private::de::content::VariantDeserializer::struct_variant + // Content::Seq case + // via ContentDeserializer::deserialize_enum + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("Struct"), + Token::Seq { len: Some(1) }, + Token::U8(1), // f + Token::SeqEnd, + Token::Str("tag"), + Token::Str("NewtypeEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("Struct"), + Token::Seq { len: Some(1) }, + Token::U8(1), // f + Token::SeqEnd, + Token::BorrowedStr("tag"), + Token::BorrowedStr("NewtypeEnum"), + Token::MapEnd, + ], + ); + } +} + +#[test] +fn struct_() { + let value = InternallyTagged::Struct { a: 1 }; + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("tag"), + Token::Str("Struct"), + Token::Str("a"), + Token::U8(1), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("Struct"), + Token::BorrowedStr("a"), + Token::U8(1), + Token::StructEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("a"), + Token::U8(1), + Token::Str("tag"), + Token::Str("Struct"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::BorrowedStr("a"), + Token::U8(1), + Token::BorrowedStr("tag"), + Token::BorrowedStr("Struct"), + Token::StructEnd, + ], + ); + + // Special case: tag field ("tag") is the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("Struct"), + Token::Str("a"), + Token::U8(1), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("Struct"), + Token::BorrowedStr("a"), + Token::U8(1), + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("a"), + Token::U8(1), + Token::Str("tag"), + Token::Str("Struct"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("a"), + Token::U8(1), + Token::BorrowedStr("tag"), + Token::BorrowedStr("Struct"), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::Str("Struct"), // tag + Token::U8(1), + Token::SeqEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::BorrowedStr("Struct"), // tag + Token::U8(1), + Token::SeqEnd, + ], + ); +} + +mod struct_enum { + use super::*; + + #[test] + fn unit() { + assert_de_tokens( + &Enum::Unit, + &[ + Token::Enum { name: "Enum" }, + Token::BorrowedStr("Unit"), + Token::Unit, + ], + ); + + let value = InternallyTagged::StructEnum { enum_: Enum::Unit }; + + // Special case: tag field ("tag") is the first field + assert_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("tag"), + Token::Str("StructEnum"), + Token::Str("enum_"), + Token::Enum { name: "Enum" }, + Token::Str("Unit"), + Token::Unit, + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("StructEnum"), + Token::BorrowedStr("enum_"), + Token::Enum { name: "Enum" }, + Token::BorrowedStr("Unit"), + Token::Unit, + Token::StructEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("enum_"), + Token::Enum { name: "Enum" }, + Token::Str("Unit"), + Token::Unit, + Token::Str("tag"), + Token::Str("StructEnum"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::BorrowedStr("enum_"), + Token::Enum { name: "Enum" }, + Token::BorrowedStr("Unit"), + Token::Unit, + Token::BorrowedStr("tag"), + Token::BorrowedStr("StructEnum"), + Token::StructEnd, + ], + ); + + // Special case: tag field ("tag") is the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("StructEnum"), + Token::Str("enum_"), + Token::Enum { name: "Enum" }, + Token::Str("Unit"), + Token::Unit, + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("StructEnum"), + Token::BorrowedStr("enum_"), + Token::Enum { name: "Enum" }, + Token::BorrowedStr("Unit"), + Token::Unit, + Token::MapEnd, + ], + ); + + // General case: tag field ("tag") is not the first field + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::Str("enum_"), + Token::Enum { name: "Enum" }, + Token::Str("Unit"), + Token::Unit, + Token::Str("tag"), + Token::Str("StructEnum"), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: Some(2) }, + Token::BorrowedStr("enum_"), + Token::Enum { name: "Enum" }, + Token::BorrowedStr("Unit"), + Token::Unit, + Token::BorrowedStr("tag"), + Token::BorrowedStr("StructEnum"), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::Str("StructEnum"), // tag + Token::Enum { name: "Enum" }, // enum_ + Token::Str("Unit"), + Token::Unit, + Token::SeqEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::BorrowedStr("StructEnum"), // tag + Token::Enum { name: "Enum" }, // enum_ + Token::BorrowedStr("Unit"), + Token::Unit, + Token::SeqEnd, + ], + ); + } +} + +#[test] +fn wrong_tag() { + assert_de_tokens_error::( + &[Token::Map { len: Some(0) }, Token::MapEnd], + "missing field `tag`", + ); + + assert_de_tokens_error::( + &[ + Token::Map { len: Some(1) }, + Token::Str("tag"), + Token::Str("Z"), + Token::MapEnd, + ], + "unknown variant `Z`, expected one of \ + `Unit`, \ + `NewtypeUnit`, \ + `NewtypeUnitStruct`, \ + `NewtypeNewtype`, \ + `NewtypeMap`, \ + `NewtypeStruct`, \ + `NewtypeEnum`, \ + `Struct`, \ + `StructEnum`", + ); +} + +#[test] +fn untagged_variant() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Tagged { + a: u8, + }, + #[serde(untagged)] + Untagged { + tag: String, + b: u8, + }, + } + + assert_de_tokens( + &InternallyTagged::Tagged { a: 1 }, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("Tagged"), + Token::Str("a"), + Token::U8(1), + Token::MapEnd, + ], + ); + + assert_tokens( + &InternallyTagged::Tagged { a: 1 }, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("tag"), + Token::Str("Tagged"), + Token::Str("a"), + Token::U8(1), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::Untagged { + tag: "Foo".to_owned(), + b: 2, + }, + &[ + Token::Map { len: Some(2) }, + Token::Str("tag"), + Token::Str("Foo"), + Token::Str("b"), + Token::U8(2), + Token::MapEnd, + ], + ); + + assert_tokens( + &InternallyTagged::Untagged { + tag: "Foo".to_owned(), + b: 2, + }, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("tag"), + Token::Str("Foo"), + Token::Str("b"), + Token::U8(2), + Token::StructEnd, + ], + ); + + assert_tokens( + &InternallyTagged::Untagged { + tag: "Tagged".to_owned(), + b: 2, + }, + &[ + Token::Struct { + name: "InternallyTagged", + len: 2, + }, + Token::Str("tag"), + Token::Str("Tagged"), + Token::Str("b"), + Token::U8(2), + Token::StructEnd, + ], + ); +} + +mod string_and_bytes { + use super::*; + + #[derive(Debug, PartialEq, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + String { + string: String, + }, + Bytes { + #[serde(with = "bytes")] + bytes: Vec, + }, + } + + #[test] + fn string_from_string() { + assert_de_tokens( + &InternallyTagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "String", + len: 2, + }, + Token::Str("tag"), + Token::Str("String"), + Token::Str("string"), + Token::Str("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "String", + len: 2, + }, + Token::Str("tag"), + Token::Str("String"), + Token::Str("string"), + Token::String("\0"), + Token::StructEnd, + ], + ); + } + + #[test] + fn string_from_bytes() { + assert_de_tokens( + &InternallyTagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "String", + len: 2, + }, + Token::Str("tag"), + Token::Str("String"), + Token::Str("string"), + Token::Bytes(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "String", + len: 2, + }, + Token::Str("tag"), + Token::Str("String"), + Token::Str("string"), + Token::ByteBuf(b"\0"), + Token::StructEnd, + ], + ); + } + + #[test] + fn bytes_from_string() { + assert_de_tokens( + &InternallyTagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Bytes", + len: 2, + }, + Token::Str("tag"), + Token::Str("Bytes"), + Token::Str("bytes"), + Token::Str("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Bytes", + len: 2, + }, + Token::Str("tag"), + Token::Str("Bytes"), + Token::Str("bytes"), + Token::String("\0"), + Token::StructEnd, + ], + ); + } + + #[test] + fn bytes_from_bytes() { + assert_de_tokens( + &InternallyTagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Bytes", + len: 2, + }, + Token::Str("tag"), + Token::Str("Bytes"), + Token::Str("bytes"), + Token::Bytes(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &InternallyTagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Bytes", + len: 2, + }, + Token::Str("tag"), + Token::Str("Bytes"), + Token::Str("bytes"), + Token::ByteBuf(b"\0"), + Token::StructEnd, + ], + ); + } + + #[test] + fn bytes_from_seq() { + assert_de_tokens( + &InternallyTagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Bytes", + len: 2, + }, + Token::Str("tag"), + Token::Str("Bytes"), + Token::Str("bytes"), + Token::Seq { len: Some(1) }, + Token::U8(0), + Token::SeqEnd, + Token::StructEnd, + ], + ); + } +} + +#[test] +fn borrow() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Input<'a> { + Package { name: &'a str }, + } + + assert_tokens( + &Input::Package { name: "borrowed" }, + &[ + Token::Struct { + name: "Input", + len: 2, + }, + Token::BorrowedStr("tag"), + Token::BorrowedStr("Package"), + Token::BorrowedStr("name"), + Token::BorrowedStr("borrowed"), + Token::StructEnd, + ], + ); +} + +#[test] +fn with_skipped_conflict() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Data { + A, + #[serde(skip)] + #[allow(dead_code)] + B { + t: String, + }, + C { + #[serde(default, skip)] + t: String, + }, + } + + let data = Data::C { t: String::new() }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "Data", + len: 1, + }, + Token::Str("tag"), + Token::Str("C"), + Token::StructEnd, + ], + ); +} + +#[test] +fn containing_flatten() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("tag"), + Token::Str("A"), + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); +} + +#[test] +fn unit_variant_with_unknown_fields() { + let value = InternallyTagged::Unit; + + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + Token::Str("tag"), + Token::Str("Unit"), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); + + // Unknown elements are not allowed in sequences + assert_de_tokens_error::( + &[ + Token::Seq { len: None }, + Token::Str("Unit"), // tag + Token::I32(0), + Token::SeqEnd, + ], + "invalid length 1, expected 0 elements in sequence", + ); +} + +#[test] +fn expecting_message() { + #[derive(Deserialize)] + #[serde(tag = "tag")] + #[serde(expecting = "something strange...")] + enum Enum { + InternallyTagged, + } + + assert_de_tokens_error::( + &[Token::Str("InternallyTagged")], + r#"invalid type: string "InternallyTagged", expected something strange..."#, + ); + + // Check that #[serde(expecting = "...")] doesn't affect variant identifier error message + assert_de_tokens_error::( + &[Token::Map { len: None }, Token::Str("tag"), Token::Unit], + "invalid type: unit value, expected variant identifier", + ); +} diff --git a/test_suite/tests/test_enum_untagged.rs b/test_suite/tests/test_enum_untagged.rs new file mode 100644 index 000000000..48f50c9c2 --- /dev/null +++ b/test_suite/tests/test_enum_untagged.rs @@ -0,0 +1,583 @@ +#![deny(trivial_numeric_casts)] +#![allow( + clippy::derive_partial_eq_without_eq, + clippy::enum_variant_names, + clippy::redundant_field_names, + clippy::too_many_lines +)] + +mod bytes; + +use serde_derive::{Deserialize, Serialize}; +use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; +use std::collections::BTreeMap; + +#[test] +fn complex() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + A { a: u8 }, + B { b: u8 }, + C, + D(u8), + E(String), + F(u8, u8), + } + + assert_tokens( + &Untagged::A { a: 1 }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("a"), + Token::U8(1), + Token::StructEnd, + ], + ); + + assert_tokens( + &Untagged::B { b: 2 }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("b"), + Token::U8(2), + Token::StructEnd, + ], + ); + + // Serializes to unit, deserializes from either depending on format's + // preference. + assert_tokens(&Untagged::C, &[Token::Unit]); + assert_de_tokens(&Untagged::C, &[Token::None]); + + assert_tokens(&Untagged::D(4), &[Token::U8(4)]); + assert_tokens(&Untagged::E("e".to_owned()), &[Token::Str("e")]); + + assert_tokens( + &Untagged::F(1, 2), + &[ + Token::Tuple { len: 2 }, + Token::U8(1), + Token::U8(2), + Token::TupleEnd, + ], + ); + + assert_de_tokens_error::( + &[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd], + "data did not match any variant of untagged enum Untagged", + ); + + assert_de_tokens_error::( + &[ + Token::Tuple { len: 3 }, + Token::U8(1), + Token::U8(2), + Token::U8(3), + Token::TupleEnd, + ], + "data did not match any variant of untagged enum Untagged", + ); +} + +#[test] +fn newtype_unit_and_empty_map() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Unit; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum Message { + Unit(Unit), + Map(BTreeMap), + } + + assert_tokens( + &Message::Map(BTreeMap::new()), + &[Token::Map { len: Some(0) }, Token::MapEnd], + ); +} + +// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_newtype_struct +#[test] +fn newtype_struct() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct NewtypeStruct(u32); + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum E { + Newtype(NewtypeStruct), + Null, + } + + let value = E::Newtype(NewtypeStruct(5)); + + // Content::Newtype case + assert_tokens( + &value, + &[ + Token::NewtypeStruct { + name: "NewtypeStruct", + }, + Token::U32(5), + ], + ); + + // _ case + assert_de_tokens(&value, &[Token::U32(5)]); +} + +mod newtype_enum { + use super::*; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum Outer { + Inner(Inner), + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + enum Inner { + Unit, + Newtype(u8), + Tuple0(), + Tuple2(u8, u8), + Struct { f: u8 }, + EmptyStruct {}, + } + + // Reaches crate::private::de::content::VariantRefDeserializer::unit_variant + #[test] + fn unit() { + assert_tokens( + &Outer::Inner(Inner::Unit), + &[Token::UnitVariant { + name: "Inner", + variant: "Unit", + }], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::newtype_variant_seed + #[test] + fn newtype() { + assert_tokens( + &Outer::Inner(Inner::Newtype(1)), + &[ + Token::NewtypeVariant { + name: "Inner", + variant: "Newtype", + }, + Token::U8(1), + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant + #[test] + fn tuple0() { + assert_tokens( + &Outer::Inner(Inner::Tuple0()), + &[ + Token::TupleVariant { + name: "Inner", + variant: "Tuple0", + len: 0, + }, + Token::TupleVariantEnd, + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant + #[test] + fn tuple2() { + assert_tokens( + &Outer::Inner(Inner::Tuple2(1, 1)), + &[ + Token::TupleVariant { + name: "Inner", + variant: "Tuple2", + len: 2, + }, + Token::U8(1), + Token::U8(1), + Token::TupleVariantEnd, + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::struct_variant + // Content::Map case + #[test] + fn struct_from_map() { + assert_tokens( + &Outer::Inner(Inner::Struct { f: 1 }), + &[ + Token::StructVariant { + name: "Inner", + variant: "Struct", + len: 1, + }, + Token::Str("f"), + Token::U8(1), + Token::StructVariantEnd, + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::struct_variant + // Content::Seq case + #[test] + fn struct_from_seq() { + assert_de_tokens( + &Outer::Inner(Inner::Struct { f: 1 }), + &[ + Token::Map { len: Some(1) }, + // tag + Token::Str("Struct"), + // content + Token::Seq { len: Some(1) }, + Token::U8(1), + Token::SeqEnd, + Token::MapEnd, + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::struct_variant + // Content::Map case + // Special case - empty map + #[test] + fn empty_struct_from_map() { + assert_de_tokens( + &Outer::Inner(Inner::EmptyStruct {}), + &[ + Token::Map { len: Some(1) }, + // tag + Token::Str("EmptyStruct"), + // content + Token::Map { len: Some(0) }, + Token::MapEnd, + Token::MapEnd, + ], + ); + } + + // Reaches crate::private::de::content::VariantRefDeserializer::struct_variant + // Content::Seq case + // Special case - empty seq + #[test] + fn empty_struct_from_seq() { + assert_de_tokens( + &Outer::Inner(Inner::EmptyStruct {}), + &[ + Token::Map { len: Some(1) }, + // tag + Token::Str("EmptyStruct"), + // content + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::MapEnd, + ], + ); + } +} + +// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_option +mod with_optional_field { + use super::*; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum Enum { + Struct { optional: Option }, + Null, + } + + #[test] + fn some() { + assert_tokens( + &Enum::Struct { optional: Some(42) }, + &[ + Token::Struct { + name: "Enum", + len: 1, + }, + Token::Str("optional"), + Token::Some, + Token::U32(42), + Token::StructEnd, + ], + ); + } + + #[test] + fn some_without_marker() { + assert_de_tokens( + &Enum::Struct { optional: Some(42) }, + &[ + Token::Struct { + name: "Enum", + len: 1, + }, + Token::Str("optional"), + Token::U32(42), + Token::StructEnd, + ], + ); + } + + #[test] + fn none() { + assert_tokens( + &Enum::Struct { optional: None }, + &[ + Token::Struct { + name: "Enum", + len: 1, + }, + Token::Str("optional"), + Token::None, + Token::StructEnd, + ], + ); + } + + #[test] + fn unit() { + assert_de_tokens( + &Enum::Struct { optional: None }, + &[ + Token::Map { len: None }, + Token::Str("optional"), + Token::Unit, + Token::MapEnd, + ], + ); + } +} + +#[test] +fn string_and_bytes() { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(untagged)] + enum Untagged { + String { + string: String, + }, + Bytes { + #[serde(with = "bytes")] + bytes: Vec, + }, + } + + assert_de_tokens( + &Untagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("string"), + Token::Str("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("string"), + Token::String("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("string"), + Token::Bytes(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::String { + string: "\0".to_owned(), + }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("string"), + Token::ByteBuf(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("bytes"), + Token::Str("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("bytes"), + Token::String("\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("bytes"), + Token::Bytes(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("bytes"), + Token::ByteBuf(b"\0"), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Untagged::Bytes { bytes: vec![0] }, + &[ + Token::Struct { + name: "Untagged", + len: 1, + }, + Token::Str("bytes"), + Token::Seq { len: Some(1) }, + Token::U8(0), + Token::SeqEnd, + Token::StructEnd, + ], + ); +} + +#[test] +fn contains_flatten() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(untagged)] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); +} + +#[test] +fn contains_flatten_with_integer_key() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + pub enum Untagged { + Variant { + #[serde(flatten)] + map: BTreeMap, + }, + } + + assert_tokens( + &Untagged::Variant { + map: { + let mut map = BTreeMap::new(); + map.insert(100, "BTreeMap".to_owned()); + map + }, + }, + &[ + Token::Map { len: None }, + Token::U64(100), + Token::Str("BTreeMap"), + Token::MapEnd, + ], + ); +} + +#[test] +fn expecting_message() { + #[derive(Deserialize)] + #[serde(untagged)] + #[serde(expecting = "something strange...")] + enum Enum { + Untagged, + } + + assert_de_tokens_error::(&[Token::Str("Untagged")], "something strange..."); +} diff --git a/test_suite/tests/test_gen.rs b/test_suite/tests/test_gen.rs index 9a7c99e79..e7338e359 100644 --- a/test_suite/tests/test_gen.rs +++ b/test_suite/tests/test_gen.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![allow( + confusable_idents, unknown_lints, mixed_script_confusables, clippy::derive_partial_eq_without_eq, @@ -19,6 +20,7 @@ clippy::trivially_copy_pass_by_ref, clippy::type_repetition_in_bounds )] +#![deny(clippy::collection_is_never_read)] use serde::de::{Deserialize, DeserializeOwned, Deserializer}; use serde::ser::{Serialize, Serializer}; @@ -287,60 +289,60 @@ fn test_gen() { assert::(); #[derive(Serialize, Deserialize)] - struct NonAsciiIdents { + pub struct NonAsciiIdents { σ: f64, } #[derive(Serialize, Deserialize)] - struct EmptyBraced {} + pub struct EmptyBraced {} #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct EmptyBracedDenyUnknown {} + pub struct EmptyBracedDenyUnknown {} #[derive(Serialize, Deserialize)] - struct BracedSkipAll { + pub struct BracedSkipAll { #[serde(skip_deserializing)] f: u8, } #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct BracedSkipAllDenyUnknown { + pub struct BracedSkipAllDenyUnknown { #[serde(skip_deserializing)] f: u8, } #[derive(Serialize, Deserialize)] - struct EmptyTuple(); + pub struct EmptyTuple(); #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct EmptyTupleDenyUnknown(); + pub struct EmptyTupleDenyUnknown(); #[derive(Serialize, Deserialize)] - struct TupleSkipAll(#[serde(skip_deserializing)] u8); + pub struct TupleSkipAll(#[serde(skip_deserializing)] u8); #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct TupleSkipAllDenyUnknown(#[serde(skip_deserializing)] u8); + pub struct TupleSkipAllDenyUnknown(#[serde(skip_deserializing)] u8); #[derive(Serialize, Deserialize)] - enum EmptyEnum {} + pub enum EmptyEnum {} #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - enum EmptyEnumDenyUnknown {} + pub enum EmptyEnumDenyUnknown {} #[derive(Serialize, Deserialize)] - enum EnumSkipAll { + pub enum EnumSkipAll { #[serde(skip_deserializing)] #[allow(dead_code)] Variant, } #[derive(Serialize, Deserialize)] - enum EmptyVariants { + pub enum EmptyVariants { Braced {}, Tuple(), BracedSkip { @@ -352,7 +354,7 @@ fn test_gen() { #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - enum EmptyVariantsDenyUnknown { + pub enum EmptyVariantsDenyUnknown { Braced {}, Tuple(), BracedSkip { @@ -364,21 +366,21 @@ fn test_gen() { #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct UnitDenyUnknown; + pub struct UnitDenyUnknown; #[derive(Serialize, Deserialize)] - struct EmptyArray { + pub struct EmptyArray { empty: [X; 0], } - enum Or { + pub enum Or { A(A), B(B), } #[derive(Serialize, Deserialize)] #[serde(untagged, remote = "Or")] - enum OrDef { + pub enum OrDef { A(A), B(B), } @@ -390,7 +392,7 @@ fn test_gen() { struct StrDef<'a>(&'a str); #[derive(Serialize, Deserialize)] - struct Remote<'a> { + pub struct Remote<'a> { #[serde(with = "OrDef")] or: Or, #[serde(borrow, with = "StrDef")] @@ -398,7 +400,7 @@ fn test_gen() { } #[derive(Serialize, Deserialize)] - enum BorrowVariant<'a> { + pub enum BorrowVariant<'a> { #[serde(borrow, with = "StrDef")] S(Str<'a>), } @@ -415,14 +417,14 @@ fn test_gen() { // This would not work if SDef::serialize / deserialize are private. #[derive(Serialize, Deserialize)] - struct RemoteVisibility { + pub struct RemoteVisibility { #[serde(with = "vis::SDef")] s: vis::S, } #[derive(Serialize, Deserialize)] #[serde(remote = "Self")] - struct RemoteSelf; + pub struct RemoteSelf; #[derive(Serialize, Deserialize)] enum ExternallyTaggedVariantWith { @@ -545,27 +547,46 @@ fn test_gen() { } assert::(); + #[derive(Serialize, Deserialize)] + pub struct Flatten { + #[serde(flatten)] + t: T, + } + #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - struct FlattenDenyUnknown { + pub struct FlattenDenyUnknown { #[serde(flatten)] t: T, } #[derive(Serialize, Deserialize)] - struct StaticStrStruct<'a> { + pub struct SkipDeserializing { + #[serde(skip_deserializing)] + flat: T, + } + + #[derive(Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + pub struct SkipDeserializingDenyUnknown { + #[serde(skip_deserializing)] + flat: T, + } + + #[derive(Serialize, Deserialize)] + pub struct StaticStrStruct<'a> { a: &'a str, b: &'static str, } #[derive(Serialize, Deserialize)] - struct StaticStrTupleStruct<'a>(&'a str, &'static str); + pub struct StaticStrTupleStruct<'a>(&'a str, &'static str); #[derive(Serialize, Deserialize)] - struct StaticStrNewtypeStruct(&'static str); + pub struct StaticStrNewtypeStruct(&'static str); #[derive(Serialize, Deserialize)] - enum StaticStrEnum<'a> { + pub enum StaticStrEnum<'a> { Struct { a: &'a str, b: &'static str }, Tuple(&'a str, &'static str), Newtype(&'static str), @@ -639,6 +660,7 @@ fn test_gen() { use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] + #[allow(dead_code)] struct Restricted { pub(super) a: usize, pub(in super::inner) b: usize, @@ -648,7 +670,7 @@ fn test_gen() { #[derive(Deserialize)] #[serde(tag = "t", content = "c")] - enum AdjacentlyTaggedVoid {} + pub enum AdjacentlyTaggedVoid {} #[derive(Serialize, Deserialize)] enum SkippedVariant { @@ -661,14 +683,13 @@ fn test_gen() { assert::>(); #[derive(Deserialize)] - struct ImplicitlyBorrowedOption<'a> { - #[allow(dead_code)] + pub struct ImplicitlyBorrowedOption<'a> { option: std::option::Option<&'a str>, } #[derive(Serialize, Deserialize)] #[serde(untagged)] - enum UntaggedNewtypeVariantWith { + pub enum UntaggedNewtypeVariantWith { Newtype( #[serde(serialize_with = "ser_x")] #[serde(deserialize_with = "de_x")] @@ -678,7 +699,7 @@ fn test_gen() { #[derive(Serialize, Deserialize)] #[serde(transparent)] - struct TransparentWith { + pub struct TransparentWith { #[serde(serialize_with = "ser_x")] #[serde(deserialize_with = "de_x")] x: X, @@ -686,41 +707,60 @@ fn test_gen() { #[derive(Deserialize)] #[serde(untagged)] - enum UntaggedWithBorrow<'a> { - Single(#[serde(borrow)] RelObject<'a>), - Many(#[serde(borrow)] Vec>), + pub enum UntaggedWithBorrow<'a> { + Single( + #[serde(borrow)] + #[allow(dead_code)] + RelObject<'a>, + ), + Many( + #[serde(borrow)] + #[allow(dead_code)] + Vec>, + ), } #[derive(Deserialize)] - struct RelObject<'a> { - #[allow(dead_code)] + pub struct RelObject<'a> { ty: &'a str, - #[allow(dead_code)] id: String, } #[derive(Serialize, Deserialize)] - struct FlattenSkipSerializing { + pub struct FlattenSkipSerializing { #[serde(flatten, skip_serializing)] #[allow(dead_code)] flat: T, } #[derive(Serialize, Deserialize)] - struct FlattenSkipSerializingIf { + pub struct FlattenSkipSerializingIf { #[serde(flatten, skip_serializing_if = "StdOption::is_none")] flat: StdOption, } #[derive(Serialize, Deserialize)] - struct FlattenSkipDeserializing { + pub struct FlattenSkipDeserializing { #[serde(flatten, skip_deserializing)] flat: T, } + #[derive(Serialize, Deserialize)] + #[serde(untagged)] + pub enum Inner { + Builder { + s: T, + #[serde(flatten)] + o: T, + }, + Default { + s: T, + }, + } + // https://github.com/serde-rs/serde/issues/1804 #[derive(Serialize, Deserialize)] - enum Message { + pub enum Message { #[serde(skip)] #[allow(dead_code)] String(String), @@ -729,7 +769,8 @@ fn test_gen() { } #[derive(Serialize)] - #[repr(packed)] + #[repr(C, packed)] + #[allow(dead_code)] struct Packed { x: u8, y: u16, @@ -738,8 +779,7 @@ fn test_gen() { macro_rules! deriving { ($field:ty) => { #[derive(Deserialize)] - struct MacroRules<'a> { - #[allow(dead_code)] + pub struct MacroRules<'a> { field: $field, } }; @@ -754,21 +794,20 @@ fn test_gen() { } #[derive(Deserialize)] - struct BorrowLifetimeInsideMacro<'a> { + pub struct BorrowLifetimeInsideMacro<'a> { #[serde(borrow = "'a")] - #[allow(dead_code)] - f: mac!(Cow<'a, str>), + pub f: mac!(Cow<'a, str>), } #[derive(Serialize)] - struct Struct { + pub struct Struct { #[serde(serialize_with = "vec_first_element")] - vec: Vec, + pub vec: Vec, } #[derive(Deserialize)] #[serde(bound(deserialize = "[&'de str; N]: Copy"))] - struct GenericUnitStruct; + pub struct GenericUnitStruct; } ////////////////////////////////////////////////////////////////////////// @@ -855,7 +894,7 @@ where #[derive(Debug, PartialEq, Deserialize)] #[serde(tag = "tag")] -enum InternallyTagged { +pub enum InternallyTagged { #[serde(deserialize_with = "deserialize_generic")] Unit, @@ -876,14 +915,14 @@ where ////////////////////////////////////////////////////////////////////////// -#[repr(packed)] +#[repr(C, packed)] pub struct RemotePacked { pub a: u16, pub b: u32, } #[derive(Serialize)] -#[repr(packed)] +#[repr(C, packed)] #[serde(remote = "RemotePacked")] pub struct RemotePackedDef { a: u16, @@ -894,14 +933,14 @@ impl Drop for RemotePackedDef { fn drop(&mut self) {} } -#[repr(packed)] +#[repr(C, packed)] pub struct RemotePackedNonCopy { pub a: u16, pub b: String, } #[derive(Deserialize)] -#[repr(packed)] +#[repr(C, packed)] #[serde(remote = "RemotePackedNonCopy")] pub struct RemotePackedNonCopyDef { a: u16, diff --git a/test_suite/tests/test_macros.rs b/test_suite/tests/test_macros.rs index 2b7d189fd..6b2fc4c5d 100644 --- a/test_suite/tests/test_macros.rs +++ b/test_suite/tests/test_macros.rs @@ -6,13 +6,8 @@ clippy::too_many_lines )] -mod bytes; - use serde_derive::{Deserialize, Serialize}; -use serde_test::{ - assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_tokens, Token, -}; -use std::collections::BTreeMap; +use serde_test::{assert_de_tokens, assert_ser_tokens, assert_tokens, Token}; use std::marker::PhantomData; // That tests that the derived Serialize implementation doesn't trigger @@ -433,54 +428,6 @@ fn test_generic_newtype_struct() { ); } -#[test] -fn test_untagged_newtype_struct() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(untagged)] - enum E { - Newtype(GenericNewTypeStruct), - Null, - } - - assert_tokens( - &E::Newtype(GenericNewTypeStruct(5u32)), - &[ - Token::NewtypeStruct { - name: "GenericNewTypeStruct", - }, - Token::U32(5), - ], - ); -} - -#[test] -fn test_adjacently_tagged_newtype_struct() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "t", content = "c")] - enum E { - Newtype(GenericNewTypeStruct), - Null, - } - - assert_de_tokens( - &E::Newtype(GenericNewTypeStruct(5u32)), - &[ - Token::Struct { name: "E", len: 2 }, - Token::Str("c"), - Token::NewtypeStruct { - name: "GenericNewTypeStruct", - }, - Token::U32(5), - Token::Str("t"), - Token::UnitVariant { - name: "E", - variant: "Newtype", - }, - Token::StructEnd, - ], - ); -} - #[test] fn test_generic_tuple_struct() { assert_tokens( @@ -606,1419 +553,247 @@ fn test_enum_state_field() { } #[test] -fn test_untagged_enum() { +fn test_internally_tagged_struct() { #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(untagged)] - enum Untagged { - A { a: u8 }, - B { b: u8 }, - C, - D(u8), - E(String), - F(u8, u8), + #[serde(tag = "type")] + pub struct Struct { + a: u8, } assert_tokens( - &Untagged::A { a: 1 }, + &Struct { a: 1 }, &[ Token::Struct { - name: "Untagged", - len: 1, + name: "Struct", + len: 2, }, + Token::Str("type"), + Token::Str("Struct"), Token::Str("a"), Token::U8(1), Token::StructEnd, ], ); - assert_tokens( - &Untagged::B { b: 2 }, + assert_de_tokens( + &Struct { a: 1 }, &[ Token::Struct { - name: "Untagged", + name: "Struct", len: 1, }, - Token::Str("b"), - Token::U8(2), + Token::Str("a"), + Token::U8(1), Token::StructEnd, ], ); +} - // Serializes to unit, deserializes from either depending on format's - // preference. - assert_tokens(&Untagged::C, &[Token::Unit]); - assert_de_tokens(&Untagged::C, &[Token::None]); - - assert_tokens(&Untagged::D(4), &[Token::U8(4)]); - assert_tokens(&Untagged::E("e".to_owned()), &[Token::Str("e")]); +#[test] +fn test_internally_tagged_braced_struct_with_zero_fields() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "type")] + struct S {} assert_tokens( - &Untagged::F(1, 2), - &[ - Token::Tuple { len: 2 }, - Token::U8(1), - Token::U8(2), - Token::TupleEnd, - ], - ); - - assert_de_tokens_error::( - &[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd], - "data did not match any variant of untagged enum Untagged", - ); - - assert_de_tokens_error::( + &S {}, &[ - Token::Tuple { len: 3 }, - Token::U8(1), - Token::U8(2), - Token::U8(3), - Token::TupleEnd, + Token::Struct { name: "S", len: 1 }, + Token::Str("type"), + Token::Str("S"), + Token::StructEnd, ], - "data did not match any variant of untagged enum Untagged", ); } #[test] -fn test_internally_tagged_enum() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Newtype(BTreeMap); - +fn test_internally_tagged_struct_with_flattened_field() { #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Struct { - f: u8, + #[serde(tag = "tag_struct")] + pub struct Struct { + #[serde(flatten)] + pub flat: Enum, } #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - enum InternallyTagged { - A { a: u8 }, - B, - C(BTreeMap), - D(Newtype), - E(Struct), + #[serde(tag = "tag_enum", content = "content")] + pub enum Enum { + A(u64), } assert_tokens( - &InternallyTagged::A { a: 1 }, + &Struct { flat: Enum::A(0) }, &[ - Token::Struct { - name: "InternallyTagged", - len: 2, + Token::Map { len: None }, + Token::Str("tag_struct"), + Token::Str("Struct"), + Token::Str("tag_enum"), + Token::UnitVariant { + name: "Enum", + variant: "A", }, - Token::Str("type"), - Token::Str("A"), - Token::Str("a"), - Token::U8(1), - Token::StructEnd, + Token::Str("content"), + Token::U64(0), + Token::MapEnd, ], ); assert_de_tokens( - &InternallyTagged::A { a: 1 }, + &Struct { flat: Enum::A(0) }, &[ - Token::Seq { len: Some(2) }, + Token::Map { len: None }, + Token::Str("tag_enum"), Token::Str("A"), - Token::U8(1), - Token::SeqEnd, + Token::Str("content"), + Token::U64(0), + Token::MapEnd, ], ); +} + +#[test] +fn test_rename_all() { + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(rename_all = "snake_case")] + enum E { + #[serde(rename_all = "camelCase")] + Serialize { + serialize: bool, + serialize_seq: bool, + }, + #[serde(rename_all = "kebab-case")] + SerializeSeq { + serialize: bool, + serialize_seq: bool, + }, + #[serde(rename_all = "SCREAMING_SNAKE_CASE")] + SerializeMap { + serialize: bool, + serialize_seq: bool, + }, + } + + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(rename_all = "PascalCase")] + struct S { + serialize: bool, + serialize_seq: bool, + } + + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(rename_all = "SCREAMING-KEBAB-CASE")] + struct ScreamingKebab { + serialize: bool, + serialize_seq: bool, + } assert_tokens( - &InternallyTagged::B, + &E::Serialize { + serialize: true, + serialize_seq: true, + }, &[ - Token::Struct { - name: "InternallyTagged", - len: 1, + Token::StructVariant { + name: "E", + variant: "serialize", + len: 2, }, - Token::Str("type"), - Token::Str("B"), - Token::StructEnd, + Token::Str("serialize"), + Token::Bool(true), + Token::Str("serializeSeq"), + Token::Bool(true), + Token::StructVariantEnd, ], ); - assert_de_tokens( - &InternallyTagged::B, - &[Token::Seq { len: Some(1) }, Token::Str("B"), Token::SeqEnd], - ); - assert_tokens( - &InternallyTagged::C(BTreeMap::new()), + &E::SerializeSeq { + serialize: true, + serialize_seq: true, + }, &[ - Token::Map { len: Some(1) }, - Token::Str("type"), - Token::Str("C"), - Token::MapEnd, + Token::StructVariant { + name: "E", + variant: "serialize_seq", + len: 2, + }, + Token::Str("serialize"), + Token::Bool(true), + Token::Str("serialize-seq"), + Token::Bool(true), + Token::StructVariantEnd, ], ); - assert_de_tokens_error::( + assert_tokens( + &E::SerializeMap { + serialize: true, + serialize_seq: true, + }, &[ - Token::Seq { len: Some(2) }, - Token::Str("C"), - Token::Map { len: Some(0) }, - Token::MapEnd, - Token::SeqEnd, + Token::StructVariant { + name: "E", + variant: "serialize_map", + len: 2, + }, + Token::Str("SERIALIZE"), + Token::Bool(true), + Token::Str("SERIALIZE_SEQ"), + Token::Bool(true), + Token::StructVariantEnd, ], - "invalid type: sequence, expected a map", ); assert_tokens( - &InternallyTagged::D(Newtype(BTreeMap::new())), + &S { + serialize: true, + serialize_seq: true, + }, &[ - Token::Map { len: Some(1) }, - Token::Str("type"), - Token::Str("D"), - Token::MapEnd, + Token::Struct { name: "S", len: 2 }, + Token::Str("Serialize"), + Token::Bool(true), + Token::Str("SerializeSeq"), + Token::Bool(true), + Token::StructEnd, ], ); assert_tokens( - &InternallyTagged::E(Struct { f: 6 }), + &ScreamingKebab { + serialize: true, + serialize_seq: true, + }, &[ Token::Struct { - name: "Struct", + name: "ScreamingKebab", len: 2, }, - Token::Str("type"), - Token::Str("E"), - Token::Str("f"), - Token::U8(6), + Token::Str("SERIALIZE"), + Token::Bool(true), + Token::Str("SERIALIZE-SEQ"), + Token::Bool(true), Token::StructEnd, ], ); - - assert_de_tokens( - &InternallyTagged::E(Struct { f: 6 }), - &[ - Token::Seq { len: Some(2) }, - Token::Str("E"), - Token::U8(6), - Token::SeqEnd, - ], - ); - - assert_de_tokens_error::( - &[Token::Map { len: Some(0) }, Token::MapEnd], - "missing field `type`", - ); - - assert_de_tokens_error::( - &[ - Token::Map { len: Some(1) }, - Token::Str("type"), - Token::Str("Z"), - Token::MapEnd, - ], - "unknown variant `Z`, expected one of `A`, `B`, `C`, `D`, `E`", - ); } #[test] -fn test_internally_tagged_bytes() { - #[derive(Debug, PartialEq, Deserialize)] - #[serde(tag = "type")] - enum InternallyTagged { - String { - string: String, +fn test_rename_all_fields() { + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(rename_all_fields = "kebab-case")] + enum E { + V1, + V2(bool), + V3 { + a_field: bool, + another_field: bool, + #[serde(rename = "last-field")] + yet_another_field: bool, }, - Bytes { - #[serde(with = "bytes")] - bytes: Vec, - }, - } - - assert_de_tokens( - &InternallyTagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "String", - len: 2, - }, - Token::Str("type"), - Token::Str("String"), - Token::Str("string"), - Token::Str("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "String", - len: 2, - }, - Token::Str("type"), - Token::Str("String"), - Token::Str("string"), - Token::String("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "String", - len: 2, - }, - Token::Str("type"), - Token::Str("String"), - Token::Str("string"), - Token::Bytes(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "String", - len: 2, - }, - Token::Str("type"), - Token::Str("String"), - Token::Str("string"), - Token::ByteBuf(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Bytes", - len: 2, - }, - Token::Str("type"), - Token::Str("Bytes"), - Token::Str("bytes"), - Token::Str("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Bytes", - len: 2, - }, - Token::Str("type"), - Token::Str("Bytes"), - Token::Str("bytes"), - Token::String("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Bytes", - len: 2, - }, - Token::Str("type"), - Token::Str("Bytes"), - Token::Str("bytes"), - Token::Bytes(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Bytes", - len: 2, - }, - Token::Str("type"), - Token::Str("Bytes"), - Token::Str("bytes"), - Token::ByteBuf(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Bytes", - len: 2, - }, - Token::Str("type"), - Token::Str("Bytes"), - Token::Str("bytes"), - Token::Seq { len: Some(1) }, - Token::U8(0), - Token::SeqEnd, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_struct_variant_containing_unit_variant() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - pub enum Level { - Info, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "action")] - pub enum Message { - Log { level: Level }, - } - - assert_de_tokens( - &Message::Log { level: Level::Info }, - &[ - Token::Struct { - name: "Message", - len: 2, - }, - Token::Str("action"), - Token::Str("Log"), - Token::Str("level"), - Token::BorrowedStr("Info"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Message::Log { level: Level::Info }, - &[ - Token::Map { len: Some(2) }, - Token::Str("action"), - Token::Str("Log"), - Token::Str("level"), - Token::BorrowedStr("Info"), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &Message::Log { level: Level::Info }, - &[ - Token::Seq { len: Some(2) }, - Token::Str("Log"), - Token::BorrowedStr("Info"), - Token::SeqEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_borrow() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - pub enum Input<'a> { - Package { name: &'a str }, - } - - assert_tokens( - &Input::Package { name: "borrowed" }, - &[ - Token::Struct { - name: "Input", - len: 2, - }, - Token::BorrowedStr("type"), - Token::BorrowedStr("Package"), - Token::BorrowedStr("name"), - Token::BorrowedStr("borrowed"), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_adjacently_tagged_enum() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "t", content = "c")] - enum AdjacentlyTagged { - Unit, - Newtype(T), - Tuple(u8, u8), - Struct { f: u8 }, - } - - // unit with no content - assert_ser_tokens( - &AdjacentlyTagged::Unit::, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 1, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::StructEnd, - ], - ); - - // unit with no content - assert_de_tokens( - &AdjacentlyTagged::Unit::, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::StructEnd, - ], - ); - - // unit with tag first - assert_de_tokens( - &AdjacentlyTagged::Unit::, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::Str("c"), - Token::Unit, - Token::StructEnd, - ], - ); - - // unit with content first - assert_de_tokens( - &AdjacentlyTagged::Unit::, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("c"), - Token::Unit, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::StructEnd, - ], - ); - - // unit with excess content (f, g, h) - assert_de_tokens( - &AdjacentlyTagged::Unit::, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("f"), - Token::Unit, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::Str("g"), - Token::Unit, - Token::Str("c"), - Token::Unit, - Token::Str("h"), - Token::Unit, - Token::StructEnd, - ], - ); - - // newtype with tag first - assert_tokens( - &AdjacentlyTagged::Newtype::(1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Newtype", - }, - Token::Str("c"), - Token::U8(1), - Token::StructEnd, - ], - ); - - // newtype with content first - assert_de_tokens( - &AdjacentlyTagged::Newtype::(1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("c"), - Token::U8(1), - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Newtype", - }, - Token::StructEnd, - ], - ); - - // optional newtype with no content field - assert_de_tokens( - &AdjacentlyTagged::Newtype::>(None), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 1, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Newtype", - }, - Token::StructEnd, - ], - ); - - // tuple with tag first - assert_tokens( - &AdjacentlyTagged::Tuple::(1, 1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Tuple", - }, - Token::Str("c"), - Token::Tuple { len: 2 }, - Token::U8(1), - Token::U8(1), - Token::TupleEnd, - Token::StructEnd, - ], - ); - - // tuple with content first - assert_de_tokens( - &AdjacentlyTagged::Tuple::(1, 1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("c"), - Token::Tuple { len: 2 }, - Token::U8(1), - Token::U8(1), - Token::TupleEnd, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Tuple", - }, - Token::StructEnd, - ], - ); - - // struct with tag first - assert_tokens( - &AdjacentlyTagged::Struct:: { f: 1 }, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Struct", - }, - Token::Str("c"), - Token::Struct { - name: "Struct", - len: 1, - }, - Token::Str("f"), - Token::U8(1), - Token::StructEnd, - Token::StructEnd, - ], - ); - - // struct with content first - assert_de_tokens( - &AdjacentlyTagged::Struct:: { f: 1 }, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("c"), - Token::Struct { - name: "Struct", - len: 1, - }, - Token::Str("f"), - Token::U8(1), - Token::StructEnd, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Struct", - }, - Token::StructEnd, - ], - ); - - // integer field keys - assert_de_tokens( - &AdjacentlyTagged::Newtype::(1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::U64(1), // content field - Token::U8(1), - Token::U64(0), // tag field - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Newtype", - }, - Token::StructEnd, - ], - ); - - // byte-array field keys - assert_de_tokens( - &AdjacentlyTagged::Newtype::(1), - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Bytes(b"c"), - Token::U8(1), - Token::Bytes(b"t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Newtype", - }, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_adjacently_tagged_enum_deny_unknown_fields() { - #[derive(Debug, PartialEq, Deserialize)] - #[serde(tag = "t", content = "c", deny_unknown_fields)] - enum AdjacentlyTagged { - Unit, - } - - assert_de_tokens( - &AdjacentlyTagged::Unit, - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::Str("c"), - Token::Unit, - Token::StructEnd, - ], - ); - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::Str("c"), - Token::Unit, - Token::Str("h"), - ], - r#"invalid value: string "h", expected "t" or "c""#, - ); - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("h"), - ], - r#"invalid value: string "h", expected "t" or "c""#, - ); - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Str("c"), - Token::Unit, - Token::Str("h"), - ], - r#"invalid value: string "h", expected "t" or "c""#, - ); - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::U64(0), // tag field - Token::UnitVariant { - name: "AdjacentlyTagged", - variant: "Unit", - }, - Token::U64(3), - ], - r#"invalid value: integer `3`, expected "t" or "c""#, - ); - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "AdjacentlyTagged", - len: 2, - }, - Token::Bytes(b"c"), - Token::Unit, - Token::Bytes(b"h"), - ], - r#"invalid value: byte array, expected "t" or "c""#, - ); -} - -#[test] -fn test_enum_in_internally_tagged_enum() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - enum Outer { - Inner(Inner), - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - enum Inner { - Unit, - Newtype(u8), - Tuple(u8, u8), - Struct { f: u8 }, - } - - assert_tokens( - &Outer::Inner(Inner::Unit), - &[ - Token::Map { len: Some(2) }, - Token::Str("type"), - Token::Str("Inner"), - Token::Str("Unit"), - Token::Unit, - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer::Inner(Inner::Newtype(1)), - &[ - Token::Map { len: Some(2) }, - Token::Str("type"), - Token::Str("Inner"), - Token::Str("Newtype"), - Token::U8(1), - Token::MapEnd, - ], - ); - - // Reaches crate::private::de::content::VariantDeserializer::tuple_variant - // Content::Seq case - // via ContentDeserializer::deserialize_enum - assert_tokens( - &Outer::Inner(Inner::Tuple(1, 1)), - &[ - Token::Map { len: Some(2) }, - Token::Str("type"), - Token::Str("Inner"), - Token::Str("Tuple"), - Token::TupleStruct { - name: "Tuple", - len: 2, - }, - Token::U8(1), - Token::U8(1), - Token::TupleStructEnd, - Token::MapEnd, - ], - ); - - // Reaches crate::private::de::content::VariantDeserializer::struct_variant - // Content::Map case - // via ContentDeserializer::deserialize_enum - assert_tokens( - &Outer::Inner(Inner::Struct { f: 1 }), - &[ - Token::Map { len: Some(2) }, - Token::Str("type"), - Token::Str("Inner"), - Token::Str("Struct"), - Token::Struct { - name: "Struct", - len: 1, - }, - Token::Str("f"), - Token::U8(1), - Token::StructEnd, - Token::MapEnd, - ], - ); - - // Reaches crate::private::de::content::VariantDeserializer::struct_variant - // Content::Seq case - // via ContentDeserializer::deserialize_enum - assert_de_tokens( - &Outer::Inner(Inner::Struct { f: 1 }), - &[ - Token::Map { len: Some(2) }, - Token::Str("type"), - Token::Str("Inner"), - Token::Str("Struct"), - Token::Seq { len: Some(1) }, - Token::U8(1), // f - Token::SeqEnd, - Token::MapEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_struct() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - pub struct Struct { - a: u8, - } - - assert_tokens( - &Struct { a: 1 }, - &[ - Token::Struct { - name: "Struct", - len: 2, - }, - Token::Str("type"), - Token::Str("Struct"), - Token::Str("a"), - Token::U8(1), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Struct { a: 1 }, - &[ - Token::Struct { - name: "Struct", - len: 1, - }, - Token::Str("a"), - Token::U8(1), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_braced_struct_with_zero_fields() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - struct S {} - - assert_tokens( - &S {}, - &[ - Token::Struct { name: "S", len: 1 }, - Token::Str("type"), - Token::Str("S"), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_struct_with_flattened_field() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "tag_struct")] - pub struct Struct { - #[serde(flatten)] - pub flat: Enum, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "tag_enum", content = "content")] - pub enum Enum { - A(u64), - } - - assert_tokens( - &Struct { flat: Enum::A(0) }, - &[ - Token::Map { len: None }, - Token::Str("tag_struct"), - Token::Str("Struct"), - Token::Str("tag_enum"), - Token::UnitVariant { - name: "Enum", - variant: "A", - }, - Token::Str("content"), - Token::U64(0), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &Struct { flat: Enum::A(0) }, - &[ - Token::Map { len: None }, - Token::Str("tag_enum"), - Token::Str("A"), - Token::Str("content"), - Token::U64(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_untagged_enum_with_flattened_integer_key() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(untagged)] - pub enum Untagged { - Variant { - #[serde(flatten)] - map: BTreeMap, - }, - } - - assert_tokens( - &Untagged::Variant { - map: { - let mut map = BTreeMap::new(); - map.insert(100, "BTreeMap".to_owned()); - map - }, - }, - &[ - Token::Map { len: None }, - Token::U64(100), - Token::Str("BTreeMap"), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_enum_in_untagged_enum() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(untagged)] - enum Outer { - Inner(Inner), - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - enum Inner { - Unit, - Newtype(u8), - Tuple(u8, u8), - Struct { f: u8 }, - } - - assert_tokens( - &Outer::Inner(Inner::Unit), - &[Token::UnitVariant { - name: "Inner", - variant: "Unit", - }], - ); - - assert_tokens( - &Outer::Inner(Inner::Newtype(1)), - &[ - Token::NewtypeVariant { - name: "Inner", - variant: "Newtype", - }, - Token::U8(1), - ], - ); - - assert_tokens( - &Outer::Inner(Inner::Tuple(1, 1)), - &[ - Token::TupleVariant { - name: "Inner", - variant: "Tuple", - len: 2, - }, - Token::U8(1), - Token::U8(1), - Token::TupleVariantEnd, - ], - ); - - assert_tokens( - &Outer::Inner(Inner::Struct { f: 1 }), - &[ - Token::StructVariant { - name: "Inner", - variant: "Struct", - len: 1, - }, - Token::Str("f"), - Token::U8(1), - Token::StructVariantEnd, - ], - ); -} - -#[test] -fn test_untagged_bytes() { - #[derive(Debug, PartialEq, Deserialize)] - #[serde(untagged)] - enum Untagged { - String { - string: String, - }, - Bytes { - #[serde(with = "bytes")] - bytes: Vec, - }, - } - - assert_de_tokens( - &Untagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("string"), - Token::Str("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("string"), - Token::String("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("string"), - Token::Bytes(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::String { - string: "\0".to_owned(), - }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("string"), - Token::ByteBuf(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("bytes"), - Token::Str("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("bytes"), - Token::String("\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("bytes"), - Token::Bytes(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("bytes"), - Token::ByteBuf(b"\0"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Untagged::Bytes { bytes: vec![0] }, - &[ - Token::Struct { - name: "Untagged", - len: 1, - }, - Token::Str("bytes"), - Token::Seq { len: Some(1) }, - Token::U8(0), - Token::SeqEnd, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_rename_all() { - #[derive(Serialize, Deserialize, Debug, PartialEq)] - #[serde(rename_all = "snake_case")] - enum E { - #[serde(rename_all = "camelCase")] - Serialize { - serialize: bool, - serialize_seq: bool, - }, - #[serde(rename_all = "kebab-case")] - SerializeSeq { - serialize: bool, - serialize_seq: bool, - }, - #[serde(rename_all = "SCREAMING_SNAKE_CASE")] - SerializeMap { - serialize: bool, - serialize_seq: bool, - }, - } - - #[derive(Serialize, Deserialize, Debug, PartialEq)] - #[serde(rename_all = "PascalCase")] - struct S { - serialize: bool, - serialize_seq: bool, - } - - #[derive(Serialize, Deserialize, Debug, PartialEq)] - #[serde(rename_all = "SCREAMING-KEBAB-CASE")] - struct ScreamingKebab { - serialize: bool, - serialize_seq: bool, - } - - assert_tokens( - &E::Serialize { - serialize: true, - serialize_seq: true, - }, - &[ - Token::StructVariant { - name: "E", - variant: "serialize", - len: 2, - }, - Token::Str("serialize"), - Token::Bool(true), - Token::Str("serializeSeq"), - Token::Bool(true), - Token::StructVariantEnd, - ], - ); - - assert_tokens( - &E::SerializeSeq { - serialize: true, - serialize_seq: true, - }, - &[ - Token::StructVariant { - name: "E", - variant: "serialize_seq", - len: 2, - }, - Token::Str("serialize"), - Token::Bool(true), - Token::Str("serialize-seq"), - Token::Bool(true), - Token::StructVariantEnd, - ], - ); - - assert_tokens( - &E::SerializeMap { - serialize: true, - serialize_seq: true, - }, - &[ - Token::StructVariant { - name: "E", - variant: "serialize_map", - len: 2, - }, - Token::Str("SERIALIZE"), - Token::Bool(true), - Token::Str("SERIALIZE_SEQ"), - Token::Bool(true), - Token::StructVariantEnd, - ], - ); - - assert_tokens( - &S { - serialize: true, - serialize_seq: true, - }, - &[ - Token::Struct { name: "S", len: 2 }, - Token::Str("Serialize"), - Token::Bool(true), - Token::Str("SerializeSeq"), - Token::Bool(true), - Token::StructEnd, - ], - ); - - assert_tokens( - &ScreamingKebab { - serialize: true, - serialize_seq: true, - }, - &[ - Token::Struct { - name: "ScreamingKebab", - len: 2, - }, - Token::Str("SERIALIZE"), - Token::Bool(true), - Token::Str("SERIALIZE-SEQ"), - Token::Bool(true), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_rename_all_fields() { - #[derive(Serialize, Deserialize, Debug, PartialEq)] - #[serde(rename_all_fields = "kebab-case")] - enum E { - V1, - V2(bool), - V3 { - a_field: bool, - another_field: bool, - #[serde(rename = "last-field")] - yet_another_field: bool, - }, - #[serde(rename_all = "snake_case")] - V4 { - a_field: bool, + #[serde(rename_all = "snake_case")] + V4 { + a_field: bool, }, } @@ -2059,90 +834,32 @@ fn test_rename_all_fields() { ); } -#[test] -fn test_untagged_newtype_variant_containing_unit_struct_not_map() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Unit; - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(untagged)] - enum Message { - Unit(Unit), - Map(BTreeMap), - } - - assert_tokens( - &Message::Map(BTreeMap::new()), - &[Token::Map { len: Some(0) }, Token::MapEnd], - ); -} - -#[test] -fn test_internally_tagged_newtype_variant_containing_unit_struct() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Info; - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "topic")] - enum Message { - Info(Info), - } - - assert_tokens( - &Message::Info(Info), - &[ - Token::Map { len: Some(1) }, - Token::Str("topic"), - Token::Str("Info"), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &Message::Info(Info), - &[ - Token::Struct { - name: "Message", - len: 1, - }, - Token::Str("topic"), - Token::Str("Info"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Message::Info(Info), - &[ - Token::Seq { len: Some(1) }, - Token::Str("Info"), - Token::SeqEnd, - ], - ); -} - #[test] fn test_packed_struct_can_derive_serialize() { #[derive(Copy, Clone, Serialize)] #[repr(packed, C)] + #[allow(dead_code)] struct PackedC { t: f32, } #[derive(Copy, Clone, Serialize)] #[repr(C, packed)] + #[allow(dead_code)] struct CPacked { t: f32, } #[derive(Copy, Clone, Serialize)] #[repr(C, packed(2))] + #[allow(dead_code)] struct CPacked2 { t: f32, } #[derive(Copy, Clone, Serialize)] #[repr(packed(2), C)] + #[allow(dead_code)] struct Packed2C { t: f32, } diff --git a/test_suite/tests/test_remote.rs b/test_suite/tests/test_remote.rs index c1f152eb8..d550af427 100644 --- a/test_suite/tests/test_remote.rs +++ b/test_suite/tests/test_remote.rs @@ -7,14 +7,17 @@ mod remote { pub struct PrimitivePriv(u8); + #[allow(dead_code)] pub struct PrimitivePub(pub u8); pub struct NewtypePriv(Unit); + #[allow(dead_code)] pub struct NewtypePub(pub Unit); pub struct TuplePriv(u8, Unit); + #[allow(dead_code)] pub struct TuplePub(pub u8, pub Unit); pub struct StructPriv { @@ -22,6 +25,7 @@ mod remote { b: Unit, } + #[allow(dead_code)] pub struct StructPub { pub a: u8, pub b: Unit, @@ -86,12 +90,14 @@ mod remote { } } + #[allow(dead_code)] pub enum EnumGeneric { Variant(T), } } #[derive(Serialize, Deserialize)] +#[allow(dead_code)] struct Test { #[serde(with = "UnitDef")] unit: remote::Unit, @@ -132,6 +138,7 @@ struct Test { #[derive(Serialize, Deserialize)] #[serde(remote = "remote::Unit")] +#[allow(dead_code)] struct UnitDef; #[derive(Serialize, Deserialize)] @@ -140,6 +147,7 @@ struct PrimitivePrivDef(#[serde(getter = "remote::PrimitivePriv::get")] u8); #[derive(Serialize, Deserialize)] #[serde(remote = "remote::PrimitivePub")] +#[allow(dead_code)] struct PrimitivePubDef(u8); #[derive(Serialize, Deserialize)] @@ -148,6 +156,7 @@ struct NewtypePrivDef(#[serde(getter = "remote::NewtypePriv::get", with = "UnitD #[derive(Serialize, Deserialize)] #[serde(remote = "remote::NewtypePub")] +#[allow(dead_code)] struct NewtypePubDef(#[serde(with = "UnitDef")] remote::Unit); #[derive(Serialize, Deserialize)] @@ -159,6 +168,7 @@ struct TuplePrivDef( #[derive(Serialize, Deserialize)] #[serde(remote = "remote::TuplePub")] +#[allow(dead_code)] struct TuplePubDef(u8, #[serde(with = "UnitDef")] remote::Unit); #[derive(Serialize, Deserialize)] @@ -174,6 +184,7 @@ struct StructPrivDef { #[derive(Serialize, Deserialize)] #[serde(remote = "remote::StructPub")] +#[allow(dead_code)] struct StructPubDef { a: u8, @@ -190,17 +201,20 @@ struct StructGenericWithGetterDef { #[derive(Serialize, Deserialize)] #[serde(remote = "remote::StructGeneric")] +#[allow(dead_code)] struct StructConcrete { value: u8, } #[derive(Serialize, Deserialize)] #[serde(remote = "remote::EnumGeneric")] +#[allow(dead_code)] enum EnumConcrete { Variant(u8), } #[derive(Debug)] +#[allow(dead_code)] enum ErrorKind { NotFound, PermissionDenied, @@ -211,6 +225,7 @@ enum ErrorKind { #[derive(Serialize, Deserialize)] #[serde(remote = "ErrorKind")] #[non_exhaustive] +#[allow(dead_code)] enum ErrorKindDef { NotFound, PermissionDenied, diff --git a/test_suite/tests/test_self.rs b/test_suite/tests/test_self.rs index 516bdeaec..2813c4854 100644 --- a/test_suite/tests/test_self.rs +++ b/test_suite/tests/test_self.rs @@ -41,7 +41,7 @@ fn test_self() { } #[derive(Deserialize, Serialize)] - struct Tuple( + pub struct Tuple( Box, Box<::Assoc>, [(); Self::ASSOC], @@ -60,7 +60,7 @@ fn test_self() { } #[derive(Deserialize, Serialize)] - enum Enum { + pub enum Enum { Struct { _f1: Box, _f2: Box<::Assoc>, diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index 81fc9ea74..1c8063184 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -1,14 +1,13 @@ #![allow(clippy::derive_partial_eq_without_eq, clippy::unreadable_literal)] #![cfg_attr(feature = "unstable", feature(never_type))] -use fnv::FnvHasher; use serde_derive::Serialize; use serde_test::{assert_ser_tokens, assert_ser_tokens_error, Configure, Token}; use std::cell::RefCell; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::ffi::CString; use std::net; -use std::num::Wrapping; +use std::num::{Saturating, Wrapping}; use std::ops::Bound; use std::path::{Path, PathBuf}; use std::rc::{Rc, Weak as RcWeak}; @@ -220,7 +219,7 @@ fn test_hashset() { &[Token::Seq { len: Some(1) }, Token::I32(1), Token::SeqEnd], ); assert_ser_tokens( - &hashset![FnvHasher @ 1], + &hashset![foldhash::fast::FixedState; 1], &[Token::Seq { len: Some(1) }, Token::I32(1), Token::SeqEnd], ); } @@ -300,7 +299,7 @@ fn test_hashmap() { ], ); assert_ser_tokens( - &hashmap![FnvHasher @ 1 => 2], + &hashmap![foldhash::fast::FixedState; 1 => 2], &[ Token::Map { len: Some(1) }, Token::I32(1), @@ -500,6 +499,38 @@ fn test_range_inclusive() { ); } +#[test] +fn test_range_from() { + assert_ser_tokens( + &(1u32..), + &[ + Token::Struct { + name: "RangeFrom", + len: 1, + }, + Token::Str("start"), + Token::U32(1), + Token::StructEnd, + ], + ); +} + +#[test] +fn test_range_to() { + assert_ser_tokens( + &(..2u32), + &[ + Token::Struct { + name: "RangeTo", + len: 1, + }, + Token::Str("end"), + Token::U32(2), + Token::StructEnd, + ], + ); +} + #[test] fn test_bound() { assert_ser_tokens( @@ -592,6 +623,11 @@ fn test_wrapping() { assert_ser_tokens(&Wrapping(1usize), &[Token::U64(1)]); } +#[test] +fn test_saturating() { + assert_ser_tokens(&Saturating(1usize), &[Token::U64(1)]); +} + #[test] fn test_rc_dst() { assert_ser_tokens(&Rc::::from("s"), &[Token::Str("s")]); diff --git a/test_suite/tests/test_serde_path.rs b/test_suite/tests/test_serde_path.rs index a87e49d91..1a54e12e3 100644 --- a/test_suite/tests/test_serde_path.rs +++ b/test_suite/tests/test_serde_path.rs @@ -1,5 +1,6 @@ #![allow( clippy::extra_unused_type_parameters, + clippy::needless_lifetimes, clippy::type_repetition_in_bounds )] @@ -27,19 +28,21 @@ mod fake_serde { { } + #[allow(dead_code)] pub trait Serialize { fn serialize(&self, serializer: S) -> Result; } + #[allow(dead_code)] pub trait Deserialize<'a>: Sized { fn deserialize>(deserializer: D) -> Result; } } -trait AssertNotSerdeSerialize {} +pub trait AssertNotSerdeSerialize {} impl AssertNotSerdeSerialize for T {} -trait AssertNotSerdeDeserialize<'a> {} +pub trait AssertNotSerdeDeserialize<'a> {} impl<'a, T: serde::Deserialize<'a>> AssertNotSerdeDeserialize<'a> for T {} diff --git a/test_suite/tests/test_value.rs b/test_suite/tests/test_value.rs index 15d25401a..3a1922c0f 100644 --- a/test_suite/tests/test_value.rs +++ b/test_suite/tests/test_value.rs @@ -63,7 +63,7 @@ fn test_map_access_to_enum() { type Value = Potential; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a map") + formatter.write_str("a map") } fn visit_map(self, map: A) -> Result diff --git a/test_suite/tests/ui/conflict/alias-enum.rs b/test_suite/tests/ui/conflict/alias-enum.rs new file mode 100644 index 000000000..5ca958b6d --- /dev/null +++ b/test_suite/tests/ui/conflict/alias-enum.rs @@ -0,0 +1,75 @@ +#![allow(non_camel_case_types)] + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +enum E { + S1 { + #[serde(alias = "a", alias = "b", alias = "c")] + a: (), + + // Warning on "c" and "b" + #[serde(alias = "c")] + b: (), + + #[serde(skip_deserializing)] + c: (), + }, + + S2 { + #[serde(alias = "b", alias = "c")] + a: (), + + // Warning on "c" + #[serde(rename = "c")] + b: (), + }, + + #[serde(rename_all = "UPPERCASE")] + S3 { + #[serde(alias = "B", alias = "c")] + a: (), + + // Warning on "b" because this collides with the "B" above after + // applying rename rules + b: (), + }, +} + +#[derive(Deserialize)] +enum E1 { + #[serde(alias = "a", alias = "b", alias = "c")] + a, + + // Warning on "c" and "b" + #[serde(alias = "c")] + b, + + #[serde(skip_deserializing)] + c, +} + +#[derive(Deserialize)] +enum E2 { + #[serde(alias = "b", alias = "c")] + a, + + // Warning on "c" + #[serde(rename = "c")] + b, +} + +#[derive(Deserialize)] +#[serde(rename_all = "UPPERCASE")] +enum E3 { + #[serde(alias = "B", alias = "c")] + a, + + // Warning on "b" because this collides with the "B" above after applying + // rename rules + b, +} + +fn main() { + __FAIL__; +} diff --git a/test_suite/tests/ui/conflict/alias-enum.stderr b/test_suite/tests/ui/conflict/alias-enum.stderr new file mode 100644 index 000000000..c84041051 --- /dev/null +++ b/test_suite/tests/ui/conflict/alias-enum.stderr @@ -0,0 +1,79 @@ +error[E0425]: cannot find value `__FAIL__` in this scope + --> tests/ui/conflict/alias-enum.rs:74:5 + | +74 | __FAIL__; + | ^^^^^^^^ not found in this scope + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:13:9 + | +8 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +13 | b: (), + | ^ no value can reach this + | + = note: `#[warn(unreachable_patterns)]` on by default + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:12:25 + | +8 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +12 | #[serde(alias = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:24:26 + | +20 | #[serde(alias = "b", alias = "c")] + | --- matches all the relevant values +... +24 | #[serde(rename = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:35:9 + | +30 | #[serde(alias = "B", alias = "c")] + | --- matches all the relevant values +... +35 | b: (), + | ^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:46:5 + | +41 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +46 | b, + | ^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:45:21 + | +41 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +45 | #[serde(alias = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:58:22 + | +54 | #[serde(alias = "b", alias = "c")] + | --- matches all the relevant values +... +58 | #[serde(rename = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias-enum.rs:70:5 + | +65 | #[serde(alias = "B", alias = "c")] + | --- matches all the relevant values +... +70 | b, + | ^ no value can reach this diff --git a/test_suite/tests/ui/conflict/alias.rs b/test_suite/tests/ui/conflict/alias.rs new file mode 100644 index 000000000..e18254759 --- /dev/null +++ b/test_suite/tests/ui/conflict/alias.rs @@ -0,0 +1,39 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +struct S1 { + #[serde(alias = "a", alias = "b", alias = "c")] + a: (), + + // Warning on "c" and "b" + #[serde(alias = "c")] + b: (), + + #[serde(skip_deserializing)] + c: (), +} + +#[derive(Deserialize)] +struct S2 { + #[serde(alias = "b", alias = "c")] + a: (), + + // Warning on "c" + #[serde(rename = "c")] + b: (), +} + +#[derive(Deserialize)] +#[serde(rename_all = "UPPERCASE")] +struct S3 { + #[serde(alias = "B", alias = "c")] + a: (), + + // Warning on "b" because this collides with the "B" above after applying + // rename rules + b: (), +} + +fn main() { + __FAIL__; +} diff --git a/test_suite/tests/ui/conflict/alias.stderr b/test_suite/tests/ui/conflict/alias.stderr new file mode 100644 index 000000000..4c1a96d1c --- /dev/null +++ b/test_suite/tests/ui/conflict/alias.stderr @@ -0,0 +1,43 @@ +error[E0425]: cannot find value `__FAIL__` in this scope + --> tests/ui/conflict/alias.rs:38:5 + | +38 | __FAIL__; + | ^^^^^^^^ not found in this scope + +warning: unreachable pattern + --> tests/ui/conflict/alias.rs:10:5 + | +5 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +10 | b: (), + | ^ no value can reach this + | + = note: `#[warn(unreachable_patterns)]` on by default + +warning: unreachable pattern + --> tests/ui/conflict/alias.rs:9:21 + | +5 | #[serde(alias = "a", alias = "b", alias = "c")] + | --- matches all the relevant values +... +9 | #[serde(alias = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias.rs:22:22 + | +18 | #[serde(alias = "b", alias = "c")] + | --- matches all the relevant values +... +22 | #[serde(rename = "c")] + | ^^^ no value can reach this + +warning: unreachable pattern + --> tests/ui/conflict/alias.rs:34:5 + | +29 | #[serde(alias = "B", alias = "c")] + | --- matches all the relevant values +... +34 | b: (), + | ^ no value can reach this diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs b/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs new file mode 100644 index 000000000..2e0fc8675 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs @@ -0,0 +1,20 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(tag = "tag", content = "content")] +enum Enum { + // Newtype variants do not use the provided path, so it is forbidden here + // Newtype(#[serde(default = "main")] u8), + Tuple(u8, #[serde(default = "main")] i8), + Struct { + #[serde(default = "main")] + f1: u8, + f2: u8, + #[serde(default = "main")] + f3: i8, + }, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.stderr new file mode 100644 index 000000000..c21b4769b --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.stderr @@ -0,0 +1,35 @@ +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs:10:33 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +10 | Tuple(u8, #[serde(default = "main")] i8), + | ^^^^^^ expected `i8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs:12:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +... +12 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_adjacently_tagged.rs:15:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +15 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs b/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs new file mode 100644 index 000000000..7c92b15f1 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs @@ -0,0 +1,19 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +enum Enum { + // Newtype variants do not use the provided path, so it is forbidden here + // Newtype(#[serde(default = "main")] u8), + Tuple(u8, #[serde(default = "main")] i8), + Struct { + #[serde(default = "main")] + f1: u8, + f2: u8, + #[serde(default = "main")] + f3: i8, + }, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.stderr new file mode 100644 index 000000000..09da24386 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_externally_tagged.stderr @@ -0,0 +1,35 @@ +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs:9:33 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +9 | Tuple(u8, #[serde(default = "main")] i8), + | ^^^^^^ expected `i8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs:11:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +... +11 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_externally_tagged.rs:14:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +14 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.rs b/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.rs new file mode 100644 index 000000000..5e1a4e848 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.rs @@ -0,0 +1,20 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(tag = "tag")] +enum Enum { + // Newtype variants do not use the provided path, so it is forbidden here + // Newtype(#[serde(default = "main")] u8), + // Tuple variants are not supported in internally tagged enums + Struct { + #[serde(default = "main")] + f1: u8, + f2: u8, + #[serde(default = "main")] + f3: i8, + }, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.stderr new file mode 100644 index 000000000..74fead2da --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_internally_tagged.stderr @@ -0,0 +1,23 @@ +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_internally_tagged.rs:12:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +... +12 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_internally_tagged.rs:15:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +15 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.rs b/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.rs new file mode 100644 index 000000000..1c5e910d7 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.rs @@ -0,0 +1,20 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(untagged)] +enum Enum { + // Newtype variants do not use the provided path, so it is forbidden here + // Newtype(#[serde(default = "main")] u8), + Tuple(u8, #[serde(default = "main")] i8), + Struct { + #[serde(default = "main")] + f1: u8, + f2: u8, + #[serde(default = "main")] + f3: i8, + }, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.stderr new file mode 100644 index 000000000..9e1167d9d --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_enum_untagged.stderr @@ -0,0 +1,35 @@ +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_untagged.rs:10:33 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +10 | Tuple(u8, #[serde(default = "main")] i8), + | ^^^^^^ expected `i8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_untagged.rs:12:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +... +12 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_enum_untagged.rs:15:27 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +15 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_newtype.rs b/test_suite/tests/ui/default-attribute/incorrect_type_newtype.rs new file mode 100644 index 000000000..84b60942d --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_newtype.rs @@ -0,0 +1,9 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default = "main")] +struct Newtype(#[serde(default = "main")] u8); + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_newtype.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_newtype.stderr new file mode 100644 index 000000000..b4e066ae2 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_newtype.stderr @@ -0,0 +1,37 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_newtype.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ + | | + | expected `Newtype`, found `()` + | expected due to this + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_newtype.rs:7:34 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +6 | #[serde(default = "main")] +7 | struct Newtype(#[serde(default = "main")] u8); + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_newtype.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Newtype`, found `()` +7 | struct Newtype(#[serde(default = "main")] u8); + | ------- expected due to this + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_newtype.rs:7:34 + | +5 | #[derive(Deserialize)] + | ----------- expected due to the type of this binding +6 | #[serde(default = "main")] +7 | struct Newtype(#[serde(default = "main")] u8); + | ^^^^^^ expected `u8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_struct.rs b/test_suite/tests/ui/default-attribute/incorrect_type_struct.rs new file mode 100644 index 000000000..3c5370b8e --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_struct.rs @@ -0,0 +1,15 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default = "main")] +struct Struct { + #[serde(default = "main")] + f1: u8, + f2: u8, + #[serde(default = "main")] + f3: i8, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_struct.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_struct.stderr new file mode 100644 index 000000000..13431b034 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_struct.stderr @@ -0,0 +1,58 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_struct.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ + | | + | expected `Struct`, found `()` + | expected due to this + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_struct.rs:8:23 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `u8` + | `match` arms have incompatible types +... +8 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_struct.rs:11:23 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +... +11 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_struct.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Struct`, found `()` +7 | struct Struct { + | ------ expected due to this + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_struct.rs:8:23 + | +5 | #[derive(Deserialize)] + | ----------- expected due to the type of this binding +... +8 | #[serde(default = "main")] + | ^^^^^^ expected `u8`, found `()` + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_struct.rs:11:23 + | +5 | #[derive(Deserialize)] + | ----------- expected due to the type of this binding +... +11 | #[serde(default = "main")] + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_tuple.rs b/test_suite/tests/ui/default-attribute/incorrect_type_tuple.rs new file mode 100644 index 000000000..f40e11651 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_tuple.rs @@ -0,0 +1,9 @@ +// Tests that type error points to the path in attribute + +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default = "main")] +struct Tuple(u8, #[serde(default = "main")] i8); + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/incorrect_type_tuple.stderr b/test_suite/tests/ui/default-attribute/incorrect_type_tuple.stderr new file mode 100644 index 000000000..131158e28 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_tuple.stderr @@ -0,0 +1,37 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_tuple.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ + | | + | expected `Tuple`, found `()` + | expected due to this + +error[E0308]: `match` arms have incompatible types + --> tests/ui/default-attribute/incorrect_type_tuple.rs:7:36 + | +5 | #[derive(Deserialize)] + | ----------- + | | + | this is found to be of type `i8` + | `match` arms have incompatible types +6 | #[serde(default = "main")] +7 | struct Tuple(u8, #[serde(default = "main")] i8); + | ^^^^^^ expected `i8`, found `()` + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_tuple.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Tuple`, found `()` +7 | struct Tuple(u8, #[serde(default = "main")] i8); + | ----- expected due to this + +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_tuple.rs:7:36 + | +5 | #[derive(Deserialize)] + | ----------- expected due to the type of this binding +6 | #[serde(default = "main")] +7 | struct Tuple(u8, #[serde(default = "main")] i8); + | ^^^^^^ expected `i8`, found `()` diff --git a/test_suite/tests/ui/default-attribute/union.rs b/test_suite/tests/ui/default-attribute/union.rs new file mode 100644 index 000000000..296512ff0 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/union.rs @@ -0,0 +1,9 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default)] +union Union { + f: u8, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/union.stderr b/test_suite/tests/ui/default-attribute/union.stderr new file mode 100644 index 000000000..d0268ce34 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/union.stderr @@ -0,0 +1,14 @@ +error: #[serde(default)] can only be used on structs + --> tests/ui/default-attribute/union.rs:4:9 + | +4 | #[serde(default)] + | ^^^^^^^ + +error: Serde does not support derive for unions + --> tests/ui/default-attribute/union.rs:4:1 + | +4 | / #[serde(default)] +5 | | union Union { +6 | | f: u8, +7 | | } + | |_^ diff --git a/test_suite/tests/ui/default-attribute/union_path.rs b/test_suite/tests/ui/default-attribute/union_path.rs new file mode 100644 index 000000000..e57f4d1a0 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/union_path.rs @@ -0,0 +1,9 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default = "default_u")] +union Union { + f: u8, +} + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/union_path.stderr b/test_suite/tests/ui/default-attribute/union_path.stderr new file mode 100644 index 000000000..08e89ee18 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/union_path.stderr @@ -0,0 +1,14 @@ +error: #[serde(default = "...")] can only be used on structs + --> tests/ui/default-attribute/union_path.rs:4:9 + | +4 | #[serde(default = "default_u")] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: Serde does not support derive for unions + --> tests/ui/default-attribute/union_path.rs:4:1 + | +4 | / #[serde(default = "default_u")] +5 | | union Union { +6 | | f: u8, +7 | | } + | |_^ diff --git a/test_suite/tests/ui/default-attribute/unit.rs b/test_suite/tests/ui/default-attribute/unit.rs new file mode 100644 index 000000000..c0500545f --- /dev/null +++ b/test_suite/tests/ui/default-attribute/unit.rs @@ -0,0 +1,7 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default)] +struct Unit; + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/unit.stderr b/test_suite/tests/ui/default-attribute/unit.stderr new file mode 100644 index 000000000..c99c3bb17 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/unit.stderr @@ -0,0 +1,7 @@ +error: #[serde(default)] can only be used on structs that have fields + --> tests/ui/default-attribute/unit.rs:3:10 + | +3 | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test_suite/tests/ui/default-attribute/unit_path.rs b/test_suite/tests/ui/default-attribute/unit_path.rs new file mode 100644 index 000000000..25705fad5 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/unit_path.rs @@ -0,0 +1,7 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +#[serde(default = "default_u")] +struct Unit; + +fn main() {} diff --git a/test_suite/tests/ui/default-attribute/unit_path.stderr b/test_suite/tests/ui/default-attribute/unit_path.stderr new file mode 100644 index 000000000..430136120 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/unit_path.stderr @@ -0,0 +1,5 @@ +error: #[serde(default = "...")] can only be used on structs that have fields + --> tests/ui/default-attribute/unit_path.rs:4:9 + | +4 | #[serde(default = "default_u")] + | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/test_suite/tests/ui/on_unimplemented.rs b/test_suite/tests/ui/on_unimplemented.rs new file mode 100644 index 000000000..fab56751b --- /dev/null +++ b/test_suite/tests/ui/on_unimplemented.rs @@ -0,0 +1,23 @@ +use serde::de::Deserialize; +use serde::ser::Serialize; + +fn to_string(_: &T) -> String +where + T: Serialize, +{ + unimplemented!() +} + +fn from_str<'de, T>(_: &'de str) -> T +where + T: Deserialize<'de>, +{ + unimplemented!() +} + +struct MyStruct; + +fn main() { + to_string(&MyStruct); + let _: MyStruct = from_str(""); +} diff --git a/test_suite/tests/ui/on_unimplemented.stderr b/test_suite/tests/ui/on_unimplemented.stderr new file mode 100644 index 000000000..1b0318190 --- /dev/null +++ b/test_suite/tests/ui/on_unimplemented.stderr @@ -0,0 +1,55 @@ +error[E0277]: the trait bound `MyStruct: Serialize` is not satisfied + --> tests/ui/on_unimplemented.rs:21:15 + | +21 | to_string(&MyStruct); + | --------- ^^^^^^^^^ the trait `Serialize` is not implemented for `MyStruct` + | | + | required by a bound introduced by this call + | + = note: for local types consider adding `#[derive(serde::Serialize)]` to your `MyStruct` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + and $N others +note: required by a bound in `to_string` + --> tests/ui/on_unimplemented.rs:6:8 + | +4 | fn to_string(_: &T) -> String + | --------- required by a bound in this function +5 | where +6 | T: Serialize, + | ^^^^^^^^^ required by this bound in `to_string` + +error[E0277]: the trait bound `MyStruct: Deserialize<'_>` is not satisfied + --> tests/ui/on_unimplemented.rs:22:23 + | +22 | let _: MyStruct = from_str(""); + | ^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `MyStruct` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `MyStruct` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `from_str` + --> tests/ui/on_unimplemented.rs:13:8 + | +11 | fn from_str<'de, T>(_: &'de str) -> T + | -------- required by a bound in this function +12 | where +13 | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `from_str` diff --git a/test_suite/tests/ui/remote/unknown_field.stderr b/test_suite/tests/ui/remote/unknown_field.stderr index 152ffb60c..5c851ce1e 100644 --- a/test_suite/tests/ui/remote/unknown_field.stderr +++ b/test_suite/tests/ui/remote/unknown_field.stderr @@ -2,10 +2,18 @@ error[E0609]: no field `b` on type `&remote::S` --> tests/ui/remote/unknown_field.rs:12:5 | 12 | b: u8, - | ^ help: a field with a similar name exists: `a` + | ^ unknown field + | +help: a field with a similar name exists + | +12 - b: u8, +12 + a: u8, + | error[E0560]: struct `remote::S` has no field named `b` --> tests/ui/remote/unknown_field.rs:12:5 | 12 | b: u8, - | ^ help: a field with a similar name exists: `a` + | ^ `remote::S` does not have this field + | + = note: all struct fields are already assigned diff --git a/test_suite/tests/ui/remote/wrong_de.stderr b/test_suite/tests/ui/remote/wrong_de.stderr index 314f2b395..be7b78783 100644 --- a/test_suite/tests/ui/remote/wrong_de.stderr +++ b/test_suite/tests/ui/remote/wrong_de.stderr @@ -12,7 +12,3 @@ note: tuple struct defined here 4 | pub struct S(pub u16); | ^ = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you can convert a `u8` to a `u16` - | -7 | #[derive(Deserialize.into())] - | +++++++ diff --git a/test_suite/tests/ui/with/incorrect_type.rs b/test_suite/tests/ui/with/incorrect_type.rs new file mode 100644 index 000000000..c1502bb45 --- /dev/null +++ b/test_suite/tests/ui/with/incorrect_type.rs @@ -0,0 +1,23 @@ +use serde_derive::{Deserialize, Serialize}; + +mod w { + use serde::{Deserializer, Serializer}; + + pub fn deserialize<'de, D: Deserializer<'de>>(_: D) -> Result<(), D::Error> { + unimplemented!() + } + pub fn serialize(_: S) -> Result { + unimplemented!() + } +} + +#[derive(Serialize, Deserialize)] +struct W(#[serde(with = "w")] u8, u8); + +#[derive(Serialize, Deserialize)] +struct S(#[serde(serialize_with = "w::serialize")] u8, u8); + +#[derive(Serialize, Deserialize)] +struct D(#[serde(deserialize_with = "w::deserialize")] u8, u8); + +fn main() {} diff --git a/test_suite/tests/ui/with/incorrect_type.stderr b/test_suite/tests/ui/with/incorrect_type.stderr new file mode 100644 index 000000000..fd08ebada --- /dev/null +++ b/test_suite/tests/ui/with/incorrect_type.stderr @@ -0,0 +1,101 @@ +error[E0277]: the trait bound `&u8: Serializer` is not satisfied + --> tests/ui/with/incorrect_type.rs:14:10 + | +14 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^ the trait `Serializer` is not implemented for `&u8` +15 | struct W(#[serde(with = "w")] u8, u8); + | --- required by a bound introduced by this call + | + = help: the following other types implement trait `Serializer`: + &mut Formatter<'a> + FlatMapSerializer<'a, M> + _::_serde::__private::ser::content::ContentSerializer +note: required by a bound in `w::serialize` + --> tests/ui/with/incorrect_type.rs:9:28 + | +9 | pub fn serialize(_: S) -> Result { + | ^^^^^^^^^^ required by this bound in `serialize` + +error[E0061]: this function takes 1 argument but 2 arguments were supplied + --> tests/ui/with/incorrect_type.rs:15:25 + | +14 | #[derive(Serialize, Deserialize)] + | --------- unexpected argument #2 of type `__S` +15 | struct W(#[serde(with = "w")] u8, u8); + | ^^^ + | +note: function defined here + --> tests/ui/with/incorrect_type.rs:9:12 + | +9 | pub fn serialize(_: S) -> Result { + | ^^^^^^^^^ + +error[E0277]: the trait bound `&u8: Serializer` is not satisfied + --> tests/ui/with/incorrect_type.rs:15:25 + | +15 | struct W(#[serde(with = "w")] u8, u8); + | ^^^ the trait `Serializer` is not implemented for `&u8` + | + = help: the following other types implement trait `Serializer`: + &mut Formatter<'a> + FlatMapSerializer<'a, M> + _::_serde::__private::ser::content::ContentSerializer + +error[E0308]: `?` operator has incompatible types + --> tests/ui/with/incorrect_type.rs:15:25 + | +15 | struct W(#[serde(with = "w")] u8, u8); + | ^^^ expected `u8`, found `()` + | + = note: `?` operator cannot convert from `()` to `u8` + +error[E0277]: the trait bound `&u8: Serializer` is not satisfied + --> tests/ui/with/incorrect_type.rs:17:10 + | +17 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^ the trait `Serializer` is not implemented for `&u8` +18 | struct S(#[serde(serialize_with = "w::serialize")] u8, u8); + | -------------- required by a bound introduced by this call + | + = help: the following other types implement trait `Serializer`: + &mut Formatter<'a> + FlatMapSerializer<'a, M> + _::_serde::__private::ser::content::ContentSerializer +note: required by a bound in `w::serialize` + --> tests/ui/with/incorrect_type.rs:9:28 + | +9 | pub fn serialize(_: S) -> Result { + | ^^^^^^^^^^ required by this bound in `serialize` + +error[E0061]: this function takes 1 argument but 2 arguments were supplied + --> tests/ui/with/incorrect_type.rs:18:35 + | +17 | #[derive(Serialize, Deserialize)] + | --------- unexpected argument #2 of type `__S` +18 | struct S(#[serde(serialize_with = "w::serialize")] u8, u8); + | ^^^^^^^^^^^^^^ + | +note: function defined here + --> tests/ui/with/incorrect_type.rs:9:12 + | +9 | pub fn serialize(_: S) -> Result { + | ^^^^^^^^^ + +error[E0277]: the trait bound `&u8: Serializer` is not satisfied + --> tests/ui/with/incorrect_type.rs:18:35 + | +18 | struct S(#[serde(serialize_with = "w::serialize")] u8, u8); + | ^^^^^^^^^^^^^^ the trait `Serializer` is not implemented for `&u8` + | + = help: the following other types implement trait `Serializer`: + &mut Formatter<'a> + FlatMapSerializer<'a, M> + _::_serde::__private::ser::content::ContentSerializer + +error[E0308]: `?` operator has incompatible types + --> tests/ui/with/incorrect_type.rs:21:37 + | +21 | struct D(#[serde(deserialize_with = "w::deserialize")] u8, u8); + | ^^^^^^^^^^^^^^^^ expected `u8`, found `()` + | + = note: `?` operator cannot convert from `()` to `u8`