diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..75070770 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd49909b..8c79633d 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,9 +52,10 @@ 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)' || ''}} + name: Rust nightly${{matrix.os == 'windows' && ' (windows)' || ''}} runs-on: ${{matrix.os}}-latest strategy: fail-fast: false @@ -63,7 +70,7 @@ jobs: - run: cd serde && cargo build --no-default-features --features alloc - run: cd serde && cargo build --no-default-features --features rc,alloc - run: cd serde && cargo build --no-default-features --features unstable - - run: cd serde && cargo test --features derive,rc,unstable + - run: cd serde_core && cargo test --features rc,unstable - run: cd test_suite/no_std && cargo build if: matrix.os != 'windows' - run: cd serde_derive && cargo check --tests @@ -77,37 +84,34 @@ jobs: strategy: fail-fast: false matrix: - rust: [1.31.0, 1.34.0] + rust: [1.56.0, 1.60.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 - 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 --no-default-features --features alloc - 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 - alloc: - name: Rust 1.36.0 - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.36.0 - - run: cd serde && cargo build --no-default-features --features alloc - minimal: name: Minimal versions runs-on: ubuntu-latest @@ -129,6 +133,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-docs-rs - run: cargo docs-rs -p serde + - run: cargo docs-rs -p serde_core - run: cargo docs-rs -p serde_derive - run: cargo docs-rs -p serde_derive_internals @@ -141,6 +146,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy - run: cd serde && cargo clippy --features rc,unstable -- -Dclippy::all -Dclippy::pedantic + - run: cd serde_core && cargo clippy --features rc,unstable -- -Dclippy::all -Dclippy::pedantic - run: cd serde_derive && cargo clippy -- -Dclippy::all -Dclippy::pedantic - run: cd serde_derive_internals && cargo clippy -- -Dclippy::all -Dclippy::pedantic - run: cd test_suite && cargo clippy --tests --features unstable -- -Dclippy::all -Dclippy::pedantic @@ -153,8 +159,10 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri + with: + toolchain: nightly-2025-05-16 # https://github.com/rust-lang/miri/issues/4323 - run: cargo miri setup - - run: cd serde && cargo miri test --features derive,rc,unstable + - run: cd serde_core && cargo miri test --features rc,unstable env: MIRIFLAGS: -Zmiri-strict-provenance - run: cd test_suite && cargo miri test --features unstable @@ -168,5 +176,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 165eb22d..e9e21997 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -target/ -**/*.rs.bk -*.sw[po] -Cargo.lock +/target/ +/Cargo.lock diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20d7f4c0..f63646b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,11 +27,11 @@ pull request with your changes. If anything does not pass, typically it will be easier to iterate and fix it locally than waiting for the CI servers to run tests for you. -##### In the [`serde`] directory +##### In the [`serde_core`] directory ```sh # Test all the example code in Serde documentation -cargo test --features derive +cargo test ``` ##### In the [`test_suite`] directory @@ -43,7 +43,7 @@ cargo +nightly test --features unstable Note that this test suite currently only supports running on a nightly compiler. -[`serde`]: https://github.com/serde-rs/serde/tree/master/serde +[`serde_core`]: https://github.com/serde-rs/serde/tree/master/serde_core [`test_suite`]: https://github.com/serde-rs/serde/tree/master/test_suite ## Conduct diff --git a/Cargo.toml b/Cargo.toml index ac94fb08..a139b97d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,19 @@ [workspace] members = [ "serde", + "serde_core", "serde_derive", "serde_derive_internals", "test_suite", ] +resolver = "2" [patch.crates-io] serde = { path = "serde" } +serde_core = { path = "serde_core" } +serde_derive = { path = "serde_derive" } [workspace.dependencies] proc-macro2 = { version = "1.0.74", default-features = false } quote = { version = "1.0.35", default-features = false } -syn = { version = "2.0.46", default-features = false } +syn = { version = "2.0.81", default-features = false } diff --git a/README.OpenSource b/README.OpenSource index 21579cdc..743966f7 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "serde", "License": "Apache-2.0", "License File": "LICENSE-APACHE", - "Version Number": "v1.0.197", + "Version Number": "v1.0.228", "Owner": "xuelei3@huawei.com", "Upstream URL": "https://github.com/serde-rs/serde", "Description": "A generic serialization/deserialization framework." diff --git a/README.md b/README.md index 31292944..bf83d766 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Serde   [![Build Status]][actions] [![Latest Version]][crates.io] [![serde msrv]][Rust 1.31] [![serde_derive msrv]][Rust 1.56] +# Serde   [![Build Status]][actions] [![Latest Version]][crates.io] [![serde msrv]][Rust 1.56] [![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 @@ -6,8 +6,8 @@ [crates.io]: https://crates.io/crates/serde [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.** @@ -15,7 +15,7 @@ You may be looking for: -- [An overview of Serde](https://serde.rs/) +- [An overview of Serde](https://serde.rs) - [Data formats supported by Serde](https://serde.rs/#data-formats) - [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/derive.html) - [Examples](https://serde.rs/examples.html) @@ -27,7 +27,7 @@ You may be looking for:
Click to show Cargo.toml. -Run this code in the playground. +Run this code in the playground. ```toml diff --git a/crates-io.md b/crates-io.md index 18710035..e6e7d9fb 100644 --- a/crates-io.md +++ b/crates-io.md @@ -6,7 +6,7 @@ You may be looking for: -- [An overview of Serde](https://serde.rs/) +- [An overview of Serde](https://serde.rs) - [Data formats supported by Serde](https://serde.rs/#data-formats) - [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/derive.html) - [Examples](https://serde.rs/examples.html) @@ -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/BUILD.gn b/serde/BUILD.gn index 14905b55..e6ebe39d 100644 --- a/serde/BUILD.gn +++ b/serde/BUILD.gn @@ -19,12 +19,16 @@ ohos_cargo_crate("lib") { crate_root = "src/lib.rs" sources = [ "src/lib.rs" ] - - deps = [ - "../serde_derive:lib(${host_toolchain})" + rustflags = [ + "--cfg", + "no_diagnostic_namespace" ] - edition = "2018" - cargo_pkg_version = "1.0.195" + deps = [ + "../serde_derive:lib(${host_toolchain})", + "../serde_core:lib" + ] + edition = "2021" + cargo_pkg_version = "1.0.228" cargo_pkg_authors = "Erick Tryzelaar , David Tolnay " cargo_pkg_name = "serde" cargo_pkg_description = "A generic serialization/deserialization framework" diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 73732672..0bffa22d 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -1,43 +1,36 @@ [package] name = "serde" -version = "1.0.197" +version = "1.0.228" authors = ["Erick Tryzelaar ", "David Tolnay "] build = "build.rs" categories = ["encoding", "no-std", "no-std::no-alloc"] description = "A generic serialization/deserialization framework" documentation = "https://docs.rs/serde" -edition = "2018" +edition = "2021" homepage = "https://serde.rs" keywords = ["serde", "serialization", "no_std"] license = "MIT OR Apache-2.0" readme = "crates-io.md" repository = "https://github.com/serde-rs/serde" -rust-version = "1.31" +rust-version = "1.56" [dependencies] +serde_core = { version = "=1.0.228", path = "../serde_core", default-features = false, features = ["result"] } 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", "rc", "unstable"] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] - -# 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 -# separately with serde's "derive" feature disabled. Every serde_derive release -# is compatible with exactly one serde release because the generated code -# involves nonpublic APIs which are not bound by semver. -[target.'cfg(any())'.dependencies] -serde_derive = { version = "=1.0.197", path = "../serde_derive" } +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "--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", +] ### FEATURES ################################################################# @@ -50,20 +43,20 @@ derive = ["serde_derive"] # Provide impls for common standard library types like Vec and HashMap. # Requires a dependency on the Rust standard library. -std = [] +std = ["serde_core/std"] # Provide impls for types that require unstable functionality. For tracking and # discussion of unstable functionality please refer to this issue: # # https://github.com/serde-rs/serde/issues/812 -unstable = [] +unstable = ["serde_core/unstable"] # Provide impls for types in the Rust core allocation and collections library # including String, Box, Vec, and Cow. This is a subset of std but may # be enabled without depending on all of std. -alloc = [] +alloc = ["serde_core/alloc"] # Opt into impls for Rc and Arc. Serializing and deserializing these types # does not preserve identity and may result in multiple copies of the same data. # Be sure that this is what you want before enabling this feature. -rc = [] +rc = ["serde_core/rc"] diff --git a/serde/build.rs b/serde/build.rs index fe5486a7..fa0fd5a0 100644 --- a/serde/build.rs +++ b/serde/build.rs @@ -1,6 +1,17 @@ use std::env; +use std::fs; +use std::path::PathBuf; use std::process::Command; -use std::str::{self, FromStr}; +use std::str; + +const PRIVATE: &str = "\ +#[doc(hidden)] +pub mod __private$$ { + #[doc(hidden)] + pub use crate::private::*; +} +use serde_core::__private$$ as serde_core_private; +"; // 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 @@ -8,89 +19,51 @@ use std::str::{self, FromStr}; fn main() { println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rustc-cfg=if_docsrs_then_no_serde_core"); + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let patch_version = env::var("CARGO_PKG_VERSION_PATCH").unwrap(); + let module = PRIVATE.replace("$$", &patch_version); + fs::write(out_dir.join("private.rs"), module).unwrap(); + let minor = match rustc_minor_version() { Some(minor) => minor, None => return, }; - let target = env::var("TARGET").unwrap(); - let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten"; - - // TryFrom, Atomic types, non-zero signed integers, and SystemTime::checked_add - // stabilized in Rust 1.34: - // https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#tryfrom-and-tryinto - // https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html#library-stabilizations - if minor < 34 { - println!("cargo:rustc-cfg=no_core_try_from"); - println!("cargo:rustc-cfg=no_num_nonzero_signed"); - println!("cargo:rustc-cfg=no_systemtime_checked_add"); - println!("cargo:rustc-cfg=no_relaxed_trait_bounds"); + if minor >= 77 { + println!("cargo:rustc-check-cfg=cfg(feature, values(\"result\"))"); + println!("cargo:rustc-check-cfg=cfg(if_docsrs_then_no_serde_core)"); + 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_diagnostic_namespace)"); + 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_target_has_atomic)"); } - // 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"); - } - - // Current minimum supported version of serde_derive crate is Rust 1.56. - if minor < 56 { + // Current minimum supported version of serde_derive crate is Rust 1.61. + if minor < 61 { println!("cargo:rustc-cfg=no_serde_derive"); } - // Support for #[cfg(target_has_atomic = "...")] stabilized in Rust 1.60. - if minor < 60 { - println!("cargo:rustc-cfg=no_target_has_atomic"); - // Allowlist of archs that support std::sync::atomic module. This is - // based on rustc's compiler/rustc_target/src/spec/*.rs. - let has_atomic64 = target.starts_with("x86_64") - || target.starts_with("i686") - || target.starts_with("aarch64") - || target.starts_with("powerpc64") - || target.starts_with("sparc64") - || target.starts_with("mips64el") - || target.starts_with("riscv64"); - let has_atomic32 = has_atomic64 || emscripten; - if minor < 34 || !has_atomic64 { - println!("cargo:rustc-cfg=no_std_atomic64"); - } - if minor < 34 || !has_atomic32 { - println!("cargo:rustc-cfg=no_std_atomic"); - } - } - - // Support for core::ffi::CStr and alloc::ffi::CString stabilized in Rust 1.64. - // https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#c-compatible-ffi-types-in-core-and-alloc - if minor < 64 { - println!("cargo:rustc-cfg=no_core_cstr"); + // 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"); } } fn rustc_minor_version() -> Option { - let rustc = match env::var_os("RUSTC") { - Some(rustc) => rustc, - None => return None, - }; - - let output = match Command::new(rustc).arg("--version").output() { - Ok(output) => output, - Err(_) => return None, - }; - - let version = match str::from_utf8(&output.stdout) { - Ok(version) => version, - Err(_) => return None, - }; - + 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/core b/serde/src/core new file mode 100644 index 00000000..8c91f5d8 --- /dev/null +++ b/serde/src/core @@ -0,0 +1 @@ +../../serde_core/src \ No newline at end of file diff --git a/serde/src/extend.rs b/serde/src/extend.rs new file mode 100644 index 00000000..d1c400c7 --- /dev/null +++ b/serde/src/extend.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2026 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod __private228 { +#[doc(hidden)] +pub use crate::private::*; +} +use serde_core::__private228 as serde_core_private; \ No newline at end of file diff --git a/serde/src/integer128.rs b/serde/src/integer128.rs index 2f94a644..c9ff9d64 100644 --- a/serde/src/integer128.rs +++ b/serde/src/integer128.rs @@ -1,6 +1,11 @@ -// No longer used. Old versions of serde used this macro for supporting targets -// that did not yet have 128-bit integer support. #[macro_export] +#[deprecated = " +This macro has no effect on any version of Serde released in the past 2 years. +It was used long ago in crates that needed to support Rustc older than 1.26.0, +or Emscripten targets older than 1.40.0, which did not yet have 128-bit integer +support. These days Serde requires a Rust compiler newer than that so 128-bit +integers are always supported. +"] #[doc(hidden)] macro_rules! serde_if_integer128 { ($($tt:tt)*) => { diff --git a/serde/src/lib.rs b/serde/src/lib.rs index 5cf44c1c..c4fc7cc1 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -9,7 +9,7 @@ //! these two groups interact with each other, allowing any supported data //! structure to be serialized and deserialized using any supported data format. //! -//! See the Serde website for additional documentation and +//! See the Serde website for additional documentation and //! usage examples. //! //! ## Design @@ -95,17 +95,23 @@ //////////////////////////////////////////////////////////////////////////////// // Serde types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/serde/1.0.197")] +#![doc(html_root_url = "https://docs.rs/serde/1.0.228")] // 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(doc_cfg, feature(doc_cfg))] +#![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))] -#![allow(unknown_lints, bare_trait_objects, deprecated)] +#![cfg_attr(feature = "unstable", feature(never_type))] +#![allow( + unknown_lints, + bare_trait_objects, + deprecated, + mismatched_lifetime_syntaxes +)] // Ignored clippy and clippy_pedantic lints #![allow( // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 @@ -118,6 +124,7 @@ // 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, @@ -142,6 +149,8 @@ clippy::too_many_lines, // preference clippy::doc_markdown, + clippy::elidable_lifetime_names, + clippy::needless_lifetimes, clippy::unseparated_literal_suffix, // false positive clippy::needless_doctest_main, @@ -159,163 +168,103 @@ #[cfg(feature = "alloc")] extern crate alloc; -/// A facade around all the types we need from the `std`, `core`, and `alloc` -/// crates. This avoids elaborate import wrangling having to happen in every -/// module. -mod lib { - mod core { - #[cfg(not(feature = "std"))] - pub use core::*; - #[cfg(feature = "std")] - pub use std::*; - } - - 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; - pub use self::core::cmp::Reverse; - 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; - pub use self::core::result; - pub use self::core::time::Duration; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub use alloc::borrow::{Cow, ToOwned}; - #[cfg(feature = "std")] - pub use std::borrow::{Cow, ToOwned}; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub use alloc::string::{String, ToString}; - #[cfg(feature = "std")] - pub use std::string::{String, ToString}; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub use alloc::vec::Vec; - #[cfg(feature = "std")] - pub use std::vec::Vec; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub use alloc::boxed::Box; - #[cfg(feature = "std")] - pub use std::boxed::Box; - - #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))] - pub use alloc::rc::{Rc, Weak as RcWeak}; - #[cfg(all(feature = "rc", feature = "std"))] - pub use std::rc::{Rc, Weak as RcWeak}; - - #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))] - pub use alloc::sync::{Arc, Weak as ArcWeak}; - #[cfg(all(feature = "rc", feature = "std"))] - pub use std::sync::{Arc, Weak as ArcWeak}; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; - #[cfg(feature = "std")] - pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; - - #[cfg(all(not(no_core_cstr), not(feature = "std")))] - pub use self::core::ffi::CStr; - #[cfg(feature = "std")] - pub use std::ffi::CStr; - - #[cfg(all(not(no_core_cstr), feature = "alloc", not(feature = "std")))] - pub use alloc::ffi::CString; - #[cfg(feature = "std")] - pub use std::ffi::CString; - - #[cfg(feature = "std")] - pub use std::{error, net}; - - #[cfg(feature = "std")] - pub use std::collections::{HashMap, HashSet}; - #[cfg(feature = "std")] - pub use std::ffi::{OsStr, OsString}; - #[cfg(feature = "std")] - pub use std::hash::{BuildHasher, Hash}; - #[cfg(feature = "std")] - pub use std::io::Write; - #[cfg(feature = "std")] - pub use std::path::{Path, PathBuf}; - #[cfg(feature = "std")] - pub use std::sync::{Mutex, RwLock}; - #[cfg(feature = "std")] - pub use std::time::{SystemTime, UNIX_EPOCH}; - - #[cfg(all(feature = "std", no_target_has_atomic, not(no_std_atomic)))] - pub use std::sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8, - AtomicUsize, Ordering, - }; - #[cfg(all(feature = "std", no_target_has_atomic, not(no_std_atomic64)))] - pub use std::sync::atomic::{AtomicI64, AtomicU64}; - - #[cfg(all(feature = "std", not(no_target_has_atomic)))] - pub use std::sync::atomic::Ordering; - #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "8"))] - pub use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU8}; - #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "16"))] - pub use std::sync::atomic::{AtomicI16, AtomicU16}; - #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "32"))] - pub use std::sync::atomic::{AtomicI32, AtomicU32}; - #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "64"))] - 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}; -} - -// None of this crate's error handling needs the `From::from` error conversion -// performed implicitly by the `?` operator or the standard library's `try!` -// macro. This simplified macro gives a 5.5% improvement in compile time -// compared to standard `try!`, and 9% improvement compared to `?`. -macro_rules! tri { - ($expr:expr) => { - match $expr { - Ok(val) => val, - Err(err) => return Err(err), - } - }; -} - -//////////////////////////////////////////////////////////////////////////////// - +// Rustdoc has a lot of shortcomings related to cross-crate re-exports that make +// the rendered documentation of serde_core traits in serde more challenging to +// understand than the equivalent documentation of the same items in serde_core. +// https://github.com/rust-lang/rust/labels/A-cross-crate-reexports +// So, just for the purpose of docs.rs documentation, we inline the contents of +// serde_core into serde. This sidesteps all the cross-crate rustdoc bugs. +#[cfg(docsrs)] #[macro_use] +#[path = "core/crate_root.rs"] +mod crate_root; + +#[cfg(docsrs)] +#[macro_use] +#[path = "core/macros.rs"] mod macros; -#[macro_use] +#[cfg(not(docsrs))] +macro_rules! crate_root { + () => { + /// A facade around all the types we need from the `std`, `core`, and `alloc` + /// crates. This avoids elaborate import wrangling having to happen in every + /// module. + mod lib { + mod core { + #[cfg(not(feature = "std"))] + pub use core::*; + #[cfg(feature = "std")] + pub use std::*; + } + + pub use self::core::{f32, f64}; + pub use self::core::{ptr, str}; + + #[cfg(any(feature = "std", feature = "alloc"))] + pub use self::core::slice; + + pub use self::core::clone; + 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::option; + pub use self::core::result; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::borrow::{Cow, ToOwned}; + #[cfg(feature = "std")] + pub use std::borrow::{Cow, ToOwned}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::string::{String, ToString}; + #[cfg(feature = "std")] + pub use std::string::{String, ToString}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::vec::Vec; + #[cfg(feature = "std")] + pub use std::vec::Vec; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::boxed::Box; + #[cfg(feature = "std")] + pub use std::boxed::Box; + } + + // None of this crate's error handling needs the `From::from` error conversion + // performed implicitly by the `?` operator or the standard library's `try!` + // macro. This simplified macro gives a 5.5% improvement in compile time + // compared to standard `try!`, and 9% improvement compared to `?`. + #[cfg(not(no_serde_derive))] + macro_rules! tri { + ($expr:expr) => { + match $expr { + Ok(val) => val, + Err(err) => return Err(err), + } + }; + } + + //////////////////////////////////////////////////////////////////////////////// + + pub use serde_core::{ + de, forward_to_deserialize_any, ser, Deserialize, Deserializer, Serialize, Serializer, + }; + + // Used by generated code and doc tests. Not public API. + #[doc(hidden)] + mod private; + + include!("extend.rs"); + }; +} + +crate_root!(); + mod integer128; -pub mod de; -pub mod ser; - -#[doc(inline)] -pub use crate::de::{Deserialize, Deserializer}; -#[doc(inline)] -pub use crate::ser::{Serialize, Serializer}; - -// Used by generated code and doc tests. Not public API. -#[doc(hidden)] -#[path = "private/mod.rs"] -pub mod __private; - -#[path = "de/seed.rs"] -mod seed; - -#[cfg(not(any(feature = "std", feature = "unstable")))] -mod std_error; - // Re-export #[derive(Serialize, Deserialize)]. // // The reason re-exporting is not enabled by default is that disabling it would @@ -326,10 +275,11 @@ extern crate serde_derive; /// Derive macro available if serde is built with `features = ["derive"]`. #[cfg(feature = "serde_derive")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "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")))] -mod actually_private { - pub struct T; +#[macro_export] +#[doc(hidden)] +macro_rules! __require_serde_not_serde_core { + () => {}; } diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 883e6909..6f657f50 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -11,12 +11,13 @@ use crate::de::{MapAccess, Unexpected}; #[cfg(any(feature = "std", feature = "alloc"))] pub use self::content::{ - Content, ContentDeserializer, ContentRefDeserializer, EnumDeserializer, - InternallyTaggedUnitVisitor, TagContentOtherField, TagContentOtherFieldVisitor, - TagOrContentField, TagOrContentFieldVisitor, TaggedContentVisitor, UntaggedUnitVisitor, + content_as_str, Content, ContentDeserializer, ContentRefDeserializer, ContentVisitor, + EnumDeserializer, InternallyTaggedUnitVisitor, TagContentOtherField, + TagContentOtherFieldVisitor, TagOrContentField, TagOrContentFieldVisitor, TaggedContentVisitor, + UntaggedUnitVisitor, }; -pub use crate::seed::InPlaceSeed; +pub use crate::serde_core_private::InPlaceSeed; /// If the missing field is of type `Option` then treat is as `None`, /// otherwise it is an error. @@ -27,6 +28,7 @@ where { struct MissingFieldDeserializer(&'static str, PhantomData); + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> Deserializer<'de> for MissingFieldDeserializer where E: Error, @@ -47,7 +49,7 @@ where visitor.visit_none() } - forward_to_deserialize_any! { + serde_core::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any @@ -66,6 +68,7 @@ where { struct CowStrVisitor; + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a> Visitor<'a> for CowStrVisitor { type Value = Cow<'a, str>; @@ -139,6 +142,7 @@ where { struct CowBytesVisitor; + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a> Visitor<'a> for CowBytesVisitor { type Value = Cow<'a, [u8]>; @@ -208,121 +212,104 @@ mod content { use crate::lib::*; - use crate::actually_private; - use crate::de::value::{MapDeserializer, SeqDeserializer}; use crate::de::{ - self, size_hint, Deserialize, DeserializeSeed, Deserializer, EnumAccess, Expected, - IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor, + self, Deserialize, DeserializeSeed, Deserializer, EnumAccess, Expected, IgnoredAny, + MapAccess, SeqAccess, Unexpected, Visitor, }; + use crate::serde_core_private::size_hint; + pub use crate::serde_core_private::Content; - /// Used from generated code to buffer the contents of the Deserializer when - /// deserializing untagged enums and internally tagged enums. - /// - /// Not public API. Use serde-value instead. - #[derive(Debug, Clone)] - pub enum Content<'de> { - Bool(bool), - - U8(u8), - U16(u16), - U32(u32), - U64(u64), - - I8(i8), - I16(i16), - I32(i32), - I64(i64), - - F32(f32), - F64(f64), - - Char(char), - String(String), - Str(&'de str), - ByteBuf(Vec), - Bytes(&'de [u8]), - - None, - Some(Box>), - - Unit, - Newtype(Box>), - Seq(Vec>), - Map(Vec<(Content<'de>, Content<'de>)>), - } - - impl<'de> Content<'de> { - pub fn as_str(&self) -> Option<&str> { - match *self { - Content::Str(x) => Some(x), - Content::String(ref x) => Some(x), - Content::Bytes(x) => str::from_utf8(x).ok(), - Content::ByteBuf(ref x) => str::from_utf8(x).ok(), - _ => None, - } - } - - #[cold] - fn unexpected(&self) -> Unexpected { - match *self { - Content::Bool(b) => Unexpected::Bool(b), - Content::U8(n) => Unexpected::Unsigned(n as u64), - Content::U16(n) => Unexpected::Unsigned(n as u64), - Content::U32(n) => Unexpected::Unsigned(n as u64), - Content::U64(n) => Unexpected::Unsigned(n), - Content::I8(n) => Unexpected::Signed(n as i64), - Content::I16(n) => Unexpected::Signed(n as i64), - Content::I32(n) => Unexpected::Signed(n as i64), - Content::I64(n) => Unexpected::Signed(n), - Content::F32(f) => Unexpected::Float(f as f64), - Content::F64(f) => Unexpected::Float(f), - Content::Char(c) => Unexpected::Char(c), - Content::String(ref s) => Unexpected::Str(s), - Content::Str(s) => Unexpected::Str(s), - Content::ByteBuf(ref b) => Unexpected::Bytes(b), - Content::Bytes(b) => Unexpected::Bytes(b), - Content::None | Content::Some(_) => Unexpected::Option, - Content::Unit => Unexpected::Unit, - Content::Newtype(_) => Unexpected::NewtypeStruct, - Content::Seq(_) => Unexpected::Seq, - Content::Map(_) => Unexpected::Map, - } + pub fn content_as_str<'a, 'de>(content: &'a Content<'de>) -> Option<&'a str> { + match *content { + Content::Str(x) => Some(x), + Content::String(ref x) => Some(x), + Content::Bytes(x) => str::from_utf8(x).ok(), + Content::ByteBuf(ref x) => str::from_utf8(x).ok(), + _ => None, } } - impl<'de> Deserialize<'de> for Content<'de> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - // Untagged and internally tagged enums are only supported in - // self-describing formats. - let visitor = ContentVisitor { value: PhantomData }; - deserializer.__deserialize_content(actually_private::T, visitor) + fn content_clone<'de>(content: &Content<'de>) -> Content<'de> { + match content { + Content::Bool(b) => Content::Bool(*b), + Content::U8(n) => Content::U8(*n), + Content::U16(n) => Content::U16(*n), + Content::U32(n) => Content::U32(*n), + Content::U64(n) => Content::U64(*n), + Content::I8(n) => Content::I8(*n), + Content::I16(n) => Content::I16(*n), + Content::I32(n) => Content::I32(*n), + Content::I64(n) => Content::I64(*n), + Content::F32(f) => Content::F32(*f), + Content::F64(f) => Content::F64(*f), + Content::Char(c) => Content::Char(*c), + Content::String(s) => Content::String(s.clone()), + Content::Str(s) => Content::Str(*s), + Content::ByteBuf(b) => Content::ByteBuf(b.clone()), + Content::Bytes(b) => Content::Bytes(b), + Content::None => Content::None, + Content::Some(content) => Content::Some(Box::new(content_clone(content))), + Content::Unit => Content::Unit, + Content::Newtype(content) => Content::Newtype(Box::new(content_clone(content))), + Content::Seq(seq) => Content::Seq(seq.iter().map(content_clone).collect()), + Content::Map(map) => Content::Map( + map.iter() + .map(|(k, v)| (content_clone(k), content_clone(v))) + .collect(), + ), } } - impl<'de, E> de::IntoDeserializer<'de, E> for Content<'de> - where - E: de::Error, - { - type Deserializer = ContentDeserializer<'de, E>; - - fn into_deserializer(self) -> Self::Deserializer { - ContentDeserializer::new(self) + #[cold] + fn content_unexpected<'a, 'de>(content: &'a Content<'de>) -> Unexpected<'a> { + match *content { + Content::Bool(b) => Unexpected::Bool(b), + Content::U8(n) => Unexpected::Unsigned(n as u64), + Content::U16(n) => Unexpected::Unsigned(n as u64), + Content::U32(n) => Unexpected::Unsigned(n as u64), + Content::U64(n) => Unexpected::Unsigned(n), + Content::I8(n) => Unexpected::Signed(n as i64), + Content::I16(n) => Unexpected::Signed(n as i64), + Content::I32(n) => Unexpected::Signed(n as i64), + Content::I64(n) => Unexpected::Signed(n), + Content::F32(f) => Unexpected::Float(f as f64), + Content::F64(f) => Unexpected::Float(f), + Content::Char(c) => Unexpected::Char(c), + Content::String(ref s) => Unexpected::Str(s), + Content::Str(s) => Unexpected::Str(s), + Content::ByteBuf(ref b) => Unexpected::Bytes(b), + Content::Bytes(b) => Unexpected::Bytes(b), + Content::None | Content::Some(_) => Unexpected::Option, + Content::Unit => Unexpected::Unit, + Content::Newtype(_) => Unexpected::NewtypeStruct, + Content::Seq(_) => Unexpected::Seq, + Content::Map(_) => Unexpected::Map, } } - struct ContentVisitor<'de> { + pub struct ContentVisitor<'de> { value: PhantomData>, } impl<'de> ContentVisitor<'de> { - fn new() -> Self { + pub fn new() -> Self { ContentVisitor { value: PhantomData } } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de> DeserializeSeed<'de> for ContentVisitor<'de> { + type Value = Content<'de>; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.__deserialize_content_v1(self) + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> Visitor<'de> for ContentVisitor<'de> { type Value = Content<'de>; @@ -474,14 +461,16 @@ mod content { where D: Deserializer<'de>, { - Deserialize::deserialize(deserializer).map(|v| Content::Some(Box::new(v))) + let v = tri!(ContentVisitor::new().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!(ContentVisitor::new().deserialize(deserializer)); + Ok(Content::Newtype(Box::new(v))) } fn visit_seq(self, mut visitor: V) -> Result @@ -490,7 +479,7 @@ mod content { { let mut vec = Vec::::with_capacity(size_hint::cautious::(visitor.size_hint())); - while let Some(e) = tri!(visitor.next_element()) { + while let Some(e) = tri!(visitor.next_element_seed(ContentVisitor::new())) { vec.push(e); } Ok(Content::Seq(vec)) @@ -504,7 +493,9 @@ mod content { Vec::<(Content, Content)>::with_capacity( size_hint::cautious::<(Content, Content)>(visitor.size_hint()), ); - while let Some(kv) = tri!(visitor.next_entry()) { + while let Some(kv) = + tri!(visitor.next_entry_seed(ContentVisitor::new(), ContentVisitor::new())) + { vec.push(kv); } Ok(Content::Map(vec)) @@ -528,6 +519,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>, @@ -542,6 +535,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> DeserializeSeed<'de> for TagOrContentVisitor<'de> { type Value = TagOrContent<'de>; @@ -555,6 +549,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> Visitor<'de> for TagOrContentVisitor<'de> { type Value = TagOrContent<'de>; @@ -814,6 +809,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, @@ -833,6 +831,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, T> Visitor<'de> for TaggedContentVisitor where T: Deserialize<'de>, @@ -854,7 +853,7 @@ mod content { } }; let rest = de::value::SeqAccessDeserializer::new(seq); - Ok((tag, tri!(Content::deserialize(rest)))) + Ok((tag, tri!(ContentVisitor::new().deserialize(rest)))) } fn visit_map(self, mut map: M) -> Result @@ -875,7 +874,7 @@ mod content { tag = Some(tri!(map.next_value())); } TagOrContent::Content(k) => { - let v = tri!(map.next_value()); + let v = tri!(map.next_value_seed(ContentVisitor::new())); vec.push((k, v)); } } @@ -897,10 +896,13 @@ 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, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> DeserializeSeed<'de> for TagOrContentFieldVisitor { type Value = TagOrContentField; @@ -912,6 +914,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> Visitor<'de> for TagOrContentFieldVisitor { type Value = TagOrContentField; @@ -972,10 +975,13 @@ 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, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> DeserializeSeed<'de> for TagContentOtherFieldVisitor { type Value = TagContentOtherField; @@ -987,6 +993,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de> Visitor<'de> for TagContentOtherFieldVisitor { type Value = TagContentOtherField; @@ -1041,8 +1048,8 @@ mod content { E: de::Error, { #[cold] - fn invalid_type(self, exp: &Expected) -> E { - de::Error::invalid_type(self.content.unexpected(), exp) + fn invalid_type(self, exp: &dyn Expected) -> E { + de::Error::invalid_type(content_unexpected(&self.content), exp) } fn deserialize_integer(self, visitor: V) -> Result @@ -1087,8 +1094,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); let value = tri!(visitor.visit_seq(&mut seq_visitor)); tri!(seq_visitor.end()); Ok(value) @@ -1102,10 +1108,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); let value = tri!(visitor.visit_map(&mut map_visitor)); tri!(map_visitor.end()); Ok(value) @@ -1113,6 +1116,7 @@ mod content { /// Used when deserializing an internally tagged enum because the content /// will be used exactly once. + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> Deserializer<'de> for ContentDeserializer<'de, E> where E: de::Error, @@ -1446,7 +1450,7 @@ mod content { s @ Content::String(_) | s @ Content::Str(_) => (s, None), other => { return Err(de::Error::invalid_type( - other.unexpected(), + content_unexpected(&other), &"string or map", )); } @@ -1478,11 +1482,7 @@ mod content { visitor.visit_unit() } - fn __deserialize_content( - self, - _: actually_private::T, - visitor: V, - ) -> Result, Self::Error> + fn __deserialize_content_v1(self, visitor: V) -> Result where V: Visitor<'de, Value = Content<'de>>, { @@ -1501,6 +1501,369 @@ mod content { } } + struct SeqDeserializer<'de, E> { + iter: > as IntoIterator>::IntoIter, + count: usize, + marker: PhantomData, + } + + impl<'de, E> SeqDeserializer<'de, E> { + fn new(content: Vec>) -> Self { + SeqDeserializer { + iter: content.into_iter(), + count: 0, + marker: PhantomData, + } + } + } + + impl<'de, E> SeqDeserializer<'de, E> + where + E: de::Error, + { + fn end(self) -> Result<(), E> { + let remaining = self.iter.count(); + if remaining == 0 { + Ok(()) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length( + self.count + remaining, + &ExpectedInSeq(self.count), + )) + } + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> Deserializer<'de> for SeqDeserializer<'de, E> + where + E: de::Error, + { + type Error = E; + + fn deserialize_any(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let v = tri!(visitor.visit_seq(&mut self)); + tri!(self.end()); + Ok(v) + } + + serde_core::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 + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> SeqAccess<'de> for SeqDeserializer<'de, E> + where + E: de::Error, + { + type Error = E; + + fn next_element_seed(&mut self, seed: V) -> Result, Self::Error> + where + V: DeserializeSeed<'de>, + { + match self.iter.next() { + Some(value) => { + self.count += 1; + seed.deserialize(ContentDeserializer::new(value)).map(Some) + } + None => Ok(None), + } + } + + fn size_hint(&self) -> Option { + size_hint::from_bounds(&self.iter) + } + } + + struct ExpectedInSeq(usize); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl Expected for ExpectedInSeq { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if self.0 == 1 { + formatter.write_str("1 element in sequence") + } else { + write!(formatter, "{} elements in sequence", self.0) + } + } + } + + struct MapDeserializer<'de, E> { + iter: , Content<'de>)> as IntoIterator>::IntoIter, + value: Option>, + count: usize, + error: PhantomData, + } + + impl<'de, E> MapDeserializer<'de, E> { + fn new(content: Vec<(Content<'de>, Content<'de>)>) -> Self { + MapDeserializer { + iter: content.into_iter(), + value: None, + count: 0, + error: PhantomData, + } + } + } + + impl<'de, E> MapDeserializer<'de, E> + where + E: de::Error, + { + fn end(self) -> Result<(), E> { + let remaining = self.iter.count(); + if remaining == 0 { + Ok(()) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length( + self.count + remaining, + &ExpectedInMap(self.count), + )) + } + } + } + + impl<'de, E> MapDeserializer<'de, E> { + fn next_pair(&mut self) -> Option<(Content<'de>, Content<'de>)> { + match self.iter.next() { + Some((k, v)) => { + self.count += 1; + Some((k, v)) + } + None => None, + } + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> Deserializer<'de> for MapDeserializer<'de, E> + where + E: de::Error, + { + type Error = E; + + fn deserialize_any(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value = tri!(visitor.visit_map(&mut self)); + tri!(self.end()); + Ok(value) + } + + fn deserialize_seq(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value = tri!(visitor.visit_seq(&mut self)); + tri!(self.end()); + Ok(value) + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + let _ = len; + self.deserialize_seq(visitor) + } + + serde_core::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 tuple_struct map + struct enum identifier ignored_any + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> MapAccess<'de> for MapDeserializer<'de, E> + where + E: de::Error, + { + type Error = E; + + fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeSeed<'de>, + { + match self.next_pair() { + Some((key, value)) => { + self.value = Some(value); + seed.deserialize(ContentDeserializer::new(key)).map(Some) + } + None => Ok(None), + } + } + + fn next_value_seed(&mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + let value = self.value.take(); + // Panic because this indicates a bug in the program rather than an + // expected failure. + let value = value.expect("MapAccess::next_value called before next_key"); + seed.deserialize(ContentDeserializer::new(value)) + } + + fn next_entry_seed( + &mut self, + kseed: TK, + vseed: TV, + ) -> Result, Self::Error> + where + TK: DeserializeSeed<'de>, + TV: DeserializeSeed<'de>, + { + match self.next_pair() { + Some((key, value)) => { + let key = tri!(kseed.deserialize(ContentDeserializer::new(key))); + let value = tri!(vseed.deserialize(ContentDeserializer::new(value))); + Ok(Some((key, value))) + } + None => Ok(None), + } + } + + fn size_hint(&self) -> Option { + size_hint::from_bounds(&self.iter) + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> SeqAccess<'de> for MapDeserializer<'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.next_pair() { + Some((k, v)) => { + let de = PairDeserializer(k, v, PhantomData); + seed.deserialize(de).map(Some) + } + None => Ok(None), + } + } + + fn size_hint(&self) -> Option { + size_hint::from_bounds(&self.iter) + } + } + + struct PairDeserializer<'de, E>(Content<'de>, Content<'de>, PhantomData); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> Deserializer<'de> for PairDeserializer<'de, E> + where + E: de::Error, + { + type Error = E; + + serde_core::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 tuple_struct map + struct enum identifier ignored_any + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let mut pair_visitor = PairVisitor(Some(self.0), Some(self.1), PhantomData); + let pair = tri!(visitor.visit_seq(&mut pair_visitor)); + if pair_visitor.1.is_none() { + Ok(pair) + } else { + let remaining = pair_visitor.size_hint().unwrap(); + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length(2, &ExpectedInSeq(2 - remaining))) + } + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + if len == 2 { + self.deserialize_seq(visitor) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length(2, &ExpectedInSeq(len))) + } + } + } + + struct PairVisitor<'de, E>(Option>, Option>, PhantomData); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'de, E> SeqAccess<'de> for PairVisitor<'de, E> + where + E: de::Error, + { + type Error = E; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeSeed<'de>, + { + if let Some(k) = self.0.take() { + seed.deserialize(ContentDeserializer::new(k)).map(Some) + } else if let Some(v) = self.1.take() { + seed.deserialize(ContentDeserializer::new(v)).map(Some) + } else { + Ok(None) + } + } + + fn size_hint(&self) -> Option { + if self.0.is_some() { + Some(2) + } else if self.1.is_some() { + Some(1) + } else { + Some(0) + } + } + } + + struct ExpectedInMap(usize); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl Expected for ExpectedInMap { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if self.0 == 1 { + formatter.write_str("1 element in map") + } else { + write!(formatter, "{} elements in map", self.0) + } + } + } + pub struct EnumDeserializer<'de, E> where E: de::Error, @@ -1523,6 +1886,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> de::EnumAccess<'de> for EnumDeserializer<'de, E> where E: de::Error, @@ -1551,6 +1915,7 @@ mod content { err: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> de::VariantAccess<'de> for VariantDeserializer<'de, E> where E: de::Error, @@ -1583,10 +1948,10 @@ mod content { { match self.value { Some(Content::Seq(v)) => { - de::Deserializer::deserialize_any(SeqDeserializer::new(v.into_iter()), visitor) + de::Deserializer::deserialize_any(SeqDeserializer::new(v), visitor) } Some(other) => Err(de::Error::invalid_type( - other.unexpected(), + content_unexpected(&other), &"tuple variant", )), None => Err(de::Error::invalid_type( @@ -1606,13 +1971,13 @@ mod content { { match self.value { Some(Content::Map(v)) => { - de::Deserializer::deserialize_any(MapDeserializer::new(v.into_iter()), visitor) + de::Deserializer::deserialize_any(MapDeserializer::new(v), visitor) } Some(Content::Seq(v)) => { - de::Deserializer::deserialize_any(SeqDeserializer::new(v.into_iter()), visitor) + de::Deserializer::deserialize_any(SeqDeserializer::new(v), visitor) } Some(other) => Err(de::Error::invalid_type( - other.unexpected(), + content_unexpected(&other), &"struct variant", )), None => Err(de::Error::invalid_type( @@ -1634,8 +1999,8 @@ mod content { E: de::Error, { #[cold] - fn invalid_type(self, exp: &Expected) -> E { - de::Error::invalid_type(self.content.unexpected(), exp) + fn invalid_type(self, exp: &dyn Expected) -> E { + de::Error::invalid_type(content_unexpected(self.content), exp) } fn deserialize_integer(self, visitor: V) -> Result @@ -1683,8 +2048,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 = SeqRefDeserializer::new(content); let value = tri!(visitor.visit_seq(&mut seq_visitor)); tri!(seq_visitor.end()); Ok(value) @@ -1698,13 +2062,7 @@ mod content { V: Visitor<'de>, E: de::Error, { - let map = content.iter().map(|(k, v)| { - ( - ContentRefDeserializer::new(k), - ContentRefDeserializer::new(v), - ) - }); - let mut map_visitor = MapDeserializer::new(map); + let mut map_visitor = MapRefDeserializer::new(content); let value = tri!(visitor.visit_map(&mut map_visitor)); tri!(map_visitor.end()); Ok(value) @@ -1712,6 +2070,7 @@ mod content { /// Used when deserializing an untagged enum because the content may need /// to be used more than once. + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a, E> Deserializer<'de> for ContentRefDeserializer<'a, 'de, E> where E: de::Error, @@ -1887,10 +2246,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 +2288,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), } } @@ -2016,7 +2395,7 @@ mod content { ref s @ Content::String(_) | ref s @ Content::Str(_) => (s, None), ref other => { return Err(de::Error::invalid_type( - other.unexpected(), + content_unexpected(other), &"string or map", )); } @@ -2051,16 +2430,12 @@ mod content { visitor.visit_unit() } - fn __deserialize_content( - self, - _: actually_private::T, - visitor: V, - ) -> Result, Self::Error> + fn __deserialize_content_v1(self, visitor: V) -> Result where V: Visitor<'de, Value = Content<'de>>, { let _ = visitor; - Ok(self.content.clone()) + Ok(content_clone(self.content)) } } @@ -2074,14 +2449,358 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de: 'a, E> Copy for ContentRefDeserializer<'a, 'de, E> {} + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de: 'a, E> Clone for ContentRefDeserializer<'a, 'de, E> { fn clone(&self) -> Self { *self } } + struct SeqRefDeserializer<'a, 'de, E> { + iter: <&'a [Content<'de>] as IntoIterator>::IntoIter, + count: usize, + marker: PhantomData, + } + + impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E> { + fn new(content: &'a [Content<'de>]) -> Self { + SeqRefDeserializer { + iter: content.iter(), + count: 0, + marker: PhantomData, + } + } + } + + impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + fn end(self) -> Result<(), E> { + let remaining = self.iter.count(); + if remaining == 0 { + Ok(()) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length( + self.count + remaining, + &ExpectedInSeq(self.count), + )) + } + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> Deserializer<'de> for SeqRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + type Error = E; + + fn deserialize_any(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let v = tri!(visitor.visit_seq(&mut self)); + tri!(self.end()); + Ok(v) + } + + serde_core::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 + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> SeqAccess<'de> for SeqRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + type Error = E; + + fn next_element_seed(&mut self, seed: V) -> Result, Self::Error> + where + V: DeserializeSeed<'de>, + { + match self.iter.next() { + Some(value) => { + self.count += 1; + 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, E> { + iter: <&'a [(Content<'de>, Content<'de>)] as IntoIterator>::IntoIter, + value: Option<&'a Content<'de>>, + count: usize, + error: PhantomData, + } + + impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E> { + fn new(content: &'a [(Content<'de>, Content<'de>)]) -> Self { + MapRefDeserializer { + iter: content.iter(), + value: None, + count: 0, + error: PhantomData, + } + } + } + + impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + fn end(self) -> Result<(), E> { + let remaining = self.iter.count(); + if remaining == 0 { + Ok(()) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length( + self.count + remaining, + &ExpectedInMap(self.count), + )) + } + } + } + + impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E> { + fn next_pair(&mut self) -> Option<(&'a Content<'de>, &'a Content<'de>)> { + match self.iter.next() { + Some((k, v)) => { + self.count += 1; + Some((k, v)) + } + None => None, + } + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> Deserializer<'de> for MapRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + type Error = E; + + fn deserialize_any(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value = tri!(visitor.visit_map(&mut self)); + tri!(self.end()); + Ok(value) + } + + fn deserialize_seq(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value = tri!(visitor.visit_seq(&mut self)); + tri!(self.end()); + Ok(value) + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + let _ = len; + self.deserialize_seq(visitor) + } + + serde_core::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 tuple_struct map + struct enum identifier ignored_any + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> 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: DeserializeSeed<'de>, + { + match self.next_pair() { + 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: DeserializeSeed<'de>, + { + let value = self.value.take(); + // Panic because this indicates a bug in the program rather than an + // expected failure. + let value = value.expect("MapAccess::next_value called before next_key"); + seed.deserialize(ContentRefDeserializer::new(value)) + } + + fn next_entry_seed( + &mut self, + kseed: TK, + vseed: TV, + ) -> Result, Self::Error> + where + TK: DeserializeSeed<'de>, + TV: DeserializeSeed<'de>, + { + match self.next_pair() { + Some((key, value)) => { + let key = tri!(kseed.deserialize(ContentRefDeserializer::new(key))); + let value = tri!(vseed.deserialize(ContentRefDeserializer::new(value))); + Ok(Some((key, value))) + } + None => Ok(None), + } + } + + fn size_hint(&self) -> Option { + size_hint::from_bounds(&self.iter) + } + } + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> SeqAccess<'de> for MapRefDeserializer<'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.next_pair() { + Some((k, v)) => { + let de = PairRefDeserializer(k, v, PhantomData); + seed.deserialize(de).map(Some) + } + None => Ok(None), + } + } + + fn size_hint(&self) -> Option { + size_hint::from_bounds(&self.iter) + } + } + + struct PairRefDeserializer<'a, 'de, E>(&'a Content<'de>, &'a Content<'de>, PhantomData); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> Deserializer<'de> for PairRefDeserializer<'a, 'de, E> + where + E: de::Error, + { + type Error = E; + + serde_core::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 tuple_struct map + struct enum identifier ignored_any + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let mut pair_visitor = PairRefVisitor(Some(self.0), Some(self.1), PhantomData); + let pair = tri!(visitor.visit_seq(&mut pair_visitor)); + if pair_visitor.1.is_none() { + Ok(pair) + } else { + let remaining = pair_visitor.size_hint().unwrap(); + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length(2, &ExpectedInSeq(2 - remaining))) + } + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + if len == 2 { + self.deserialize_seq(visitor) + } else { + // First argument is the number of elements in the data, second + // argument is the number of elements expected by the Deserialize. + Err(de::Error::invalid_length(2, &ExpectedInSeq(len))) + } + } + } + + struct PairRefVisitor<'a, 'de, E>( + Option<&'a Content<'de>>, + Option<&'a Content<'de>>, + PhantomData, + ); + + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] + impl<'a, 'de, E> SeqAccess<'de> for PairRefVisitor<'a, 'de, E> + where + E: de::Error, + { + type Error = E; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeSeed<'de>, + { + if let Some(k) = self.0.take() { + seed.deserialize(ContentRefDeserializer::new(k)).map(Some) + } else if let Some(v) = self.1.take() { + seed.deserialize(ContentRefDeserializer::new(v)).map(Some) + } else { + Ok(None) + } + } + + fn size_hint(&self) -> Option { + if self.0.is_some() { + Some(2) + } else if self.1.is_some() { + Some(1) + } else { + Some(0) + } + } + } + struct EnumRefDeserializer<'a, 'de: 'a, E> where E: de::Error, @@ -2091,6 +2810,7 @@ mod content { err: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a, E> de::EnumAccess<'de> for EnumRefDeserializer<'a, 'de, E> where E: de::Error, @@ -2119,6 +2839,7 @@ mod content { err: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a, E> de::VariantAccess<'de> for VariantRefDeserializer<'a, 'de, E> where E: de::Error, @@ -2128,6 +2849,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 +2862,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,11 +2880,15 @@ 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(), + content_unexpected(other), &"tuple variant", )), None => Err(de::Error::invalid_type( @@ -2173,14 +2907,15 @@ 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(), + content_unexpected(other), &"struct variant", )), None => Err(de::Error::invalid_type( @@ -2191,158 +2926,7 @@ 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 - } - } - + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> de::IntoDeserializer<'de, E> for ContentDeserializer<'de, E> where E: de::Error, @@ -2354,6 +2938,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a, E> de::IntoDeserializer<'de, E> for ContentRefDeserializer<'a, 'de, E> where E: de::Error, @@ -2383,6 +2968,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a> Visitor<'de> for InternallyTaggedUnitVisitor<'a> { type Value = (); @@ -2428,6 +3014,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a> Visitor<'de> for UntaggedUnitVisitor<'a> { type Value = (); @@ -2475,6 +3062,7 @@ pub trait IdentifierDeserializer<'de, E: Error> { pub struct Borrowed<'de, T: 'de + ?Sized>(pub &'de T); +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> IdentifierDeserializer<'de, E> for u64 where E: Error, @@ -2491,6 +3079,7 @@ pub struct StrDeserializer<'a, E> { marker: PhantomData, } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, 'a, E> Deserializer<'de> for StrDeserializer<'a, E> where E: Error, @@ -2504,7 +3093,7 @@ where visitor.visit_str(self.value) } - forward_to_deserialize_any! { + serde_core::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 @@ -2516,6 +3105,7 @@ pub struct BorrowedStrDeserializer<'de, E> { marker: PhantomData, } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> Deserializer<'de> for BorrowedStrDeserializer<'de, E> where E: Error, @@ -2529,13 +3119,14 @@ where visitor.visit_borrowed_str(self.value) } - forward_to_deserialize_any! { + serde_core::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 } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, E> IdentifierDeserializer<'a, E> for &'a str where E: Error, @@ -2550,6 +3141,7 @@ where } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> IdentifierDeserializer<'de, E> for Borrowed<'de, str> where E: Error, @@ -2564,6 +3156,7 @@ where } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, E> IdentifierDeserializer<'a, E> for &'a [u8] where E: Error, @@ -2575,6 +3168,7 @@ where } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, E> IdentifierDeserializer<'de, E> for Borrowed<'de, [u8]> where E: Error, @@ -2617,6 +3211,7 @@ macro_rules! forward_to_deserialize_other { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E> where E: Error, @@ -2703,6 +3298,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 +3333,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) @@ -2743,6 +3348,7 @@ struct FlatMapAccess<'a, 'de: 'a, E> { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de, E> MapAccess<'de> for FlatMapAccess<'a, 'de, E> where E: Error, @@ -2787,6 +3393,7 @@ struct FlatStructAccess<'a, 'de: 'a, E> { } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de, E> MapAccess<'de> for FlatStructAccess<'a, 'de, E> where E: Error, @@ -2829,7 +3436,7 @@ fn flat_map_take_entry<'de>( // and if the field is one recognized by the current data structure. let is_recognized = match entry { None => false, - Some((k, _v)) => k.as_str().map_or(false, |name| recognized.contains(&name)), + Some((k, _v)) => content_as_str(k).map_or(false, |name| recognized.contains(&name)), }; if is_recognized { @@ -2850,6 +3457,7 @@ pub struct AdjacentlyTaggedEnumVariantVisitor { fields_enum: PhantomData, } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, F> Visitor<'de> for AdjacentlyTaggedEnumVariantVisitor where F: Deserialize<'de>, @@ -2870,6 +3478,7 @@ where } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'de, F> DeserializeSeed<'de> for AdjacentlyTaggedEnumVariantSeed where F: Deserialize<'de>, diff --git a/serde/src/private/mod.rs b/serde/src/private/mod.rs index 177f8501..20bfa4eb 100644 --- a/serde/src/private/mod.rs +++ b/serde/src/private/mod.rs @@ -3,11 +3,8 @@ pub mod de; #[cfg(not(no_serde_derive))] pub mod ser; -// FIXME: #[cfg(doctest)] once https://github.com/rust-lang/rust/issues/67295 is fixed. -pub mod doc; - pub use crate::lib::clone::Clone; -pub use crate::lib::convert::{From, Into}; +pub use crate::lib::convert::{From, Into, TryFrom}; pub use crate::lib::default::Default; pub use crate::lib::fmt::{self, Formatter}; pub use crate::lib::marker::PhantomData; @@ -15,34 +12,7 @@ pub use crate::lib::option::Option::{self, None, Some}; pub use crate::lib::ptr; pub use crate::lib::result::Result::{self, Err, Ok}; -pub use self::string::from_utf8_lossy; +pub use crate::serde_core_private::string::from_utf8_lossy; #[cfg(any(feature = "alloc", feature = "std"))] pub use crate::lib::{ToString, Vec}; - -#[cfg(not(no_core_try_from))] -pub use crate::lib::convert::TryFrom; - -mod string { - use crate::lib::*; - - #[cfg(any(feature = "std", feature = "alloc"))] - pub fn from_utf8_lossy(bytes: &[u8]) -> Cow { - String::from_utf8_lossy(bytes) - } - - // The generated code calls this like: - // - // let value = &_serde::__private::from_utf8_lossy(bytes); - // Err(_serde::de::Error::unknown_variant(value, VARIANTS)) - // - // so it is okay for the return type to be different from the std case as long - // as the above works. - #[cfg(not(any(feature = "std", feature = "alloc")))] - pub fn from_utf8_lossy(bytes: &[u8]) -> &str { - // Three unicode replacement characters if it fails. They look like a - // white-on-black question mark. The user will recognize it as invalid - // UTF-8. - str::from_utf8(bytes).unwrap_or("\u{fffd}\u{fffd}\u{fffd}") - } -} diff --git a/serde/src/private/ser.rs b/serde/src/private/ser.rs index 50bcb251..411e2b41 100644 --- a/serde/src/private/ser.rs +++ b/serde/src/private/ser.rs @@ -51,14 +51,14 @@ enum Unsupported { String, ByteArray, Optional, - #[cfg(any(feature = "std", feature = "alloc"))] - UnitStruct, Sequence, Tuple, TupleStruct, + #[cfg(not(any(feature = "std", feature = "alloc")))] Enum, } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Display for Unsupported { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -69,11 +69,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"), } } @@ -91,6 +90,7 @@ where } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Serializer for TaggedSerializer where S: Serializer, @@ -174,9 +174,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 +205,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 +224,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 +327,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)) } @@ -357,6 +357,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeTupleVariant for SerializeTupleVariantAsMapValue where M: ser::SerializeMap, @@ -364,9 +365,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); @@ -397,6 +398,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeStructVariant for SerializeStructVariantAsMapValue where M: ser::SerializeMap, @@ -404,13 +406,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)); @@ -468,6 +466,7 @@ mod content { ), } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Serialize for Content { fn serialize(&self, serializer: S) -> Result where @@ -560,6 +559,7 @@ mod content { } } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Serializer for ContentSerializer where E: ser::Error, @@ -635,9 +635,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 +659,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 +669,7 @@ mod content { )) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, @@ -681,7 +677,7 @@ mod content { value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { Ok(Content::NewtypeVariant( name, @@ -775,6 +771,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeSeq for SerializeSeq where E: ser::Error, @@ -782,9 +779,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); @@ -801,6 +798,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeTuple for SerializeTuple where E: ser::Error, @@ -808,9 +806,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); @@ -828,6 +826,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeTupleStruct for SerializeTupleStruct where E: ser::Error, @@ -835,9 +834,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); @@ -857,6 +856,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeTupleVariant for SerializeTupleVariant where E: ser::Error, @@ -864,9 +864,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); @@ -889,6 +889,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeMap for SerializeMap where E: ser::Error, @@ -896,18 +897,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 +923,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())); @@ -940,6 +941,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeStruct for SerializeStruct where E: ser::Error, @@ -947,9 +949,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)); @@ -969,6 +971,7 @@ mod content { error: PhantomData, } + #[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::SerializeStructVariant for SerializeStructVariant where E: ser::Error, @@ -976,9 +979,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)); @@ -1013,6 +1016,7 @@ where } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, M> Serializer for FlatMapSerializer<'a, M> where M: SerializeMap + 'a, @@ -1088,9 +1092,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 +1104,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 +1135,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 { @@ -1195,6 +1198,7 @@ where pub struct FlatMapSerializeMap<'a, M: 'a>(&'a mut M); #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, M> ser::SerializeMap for FlatMapSerializeMap<'a, M> where M: SerializeMap + 'a, @@ -1202,28 +1206,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) } @@ -1237,6 +1237,7 @@ where pub struct FlatMapSerializeStruct<'a, M: 'a>(&'a mut M); #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, M> ser::SerializeStruct for FlatMapSerializeStruct<'a, M> where M: SerializeMap + 'a, @@ -1244,13 +1245,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) } @@ -1282,6 +1279,7 @@ where } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, M> ser::SerializeTupleVariant for FlatMapSerializeTupleVariantAsMapValue<'a, M> where M: SerializeMap + 'a, @@ -1289,9 +1287,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); @@ -1328,6 +1326,7 @@ where } #[cfg(any(feature = "std", feature = "alloc"))] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, M> ser::SerializeStructVariant for FlatMapSerializeStructVariantAsMapValue<'a, M> where M: SerializeMap + 'a, @@ -1335,13 +1334,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)); @@ -1362,6 +1357,7 @@ pub struct AdjacentlyTaggedEnumVariant { pub variant_name: &'static str, } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Serialize for AdjacentlyTaggedEnumVariant { fn serialize(&self, serializer: S) -> Result where @@ -1375,6 +1371,7 @@ impl Serialize for AdjacentlyTaggedEnumVariant { // that is not recognized. pub struct CannotSerializeVariant(pub T); +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Display for CannotSerializeVariant where T: Debug, diff --git a/serde_core/BUILD.gn b/serde_core/BUILD.gn new file mode 100644 index 00000000..a207db50 --- /dev/null +++ b/serde_core/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright (c) 2026 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +ohos_cargo_crate("lib") { + crate_name = "serde_core" + crate_type = "rlib" + crate_root = "src/lib.rs" + + sources = [ "src/lib.rs" ] + + rustflags = [ + "--cfg", + "no_diagnostic_namespace" + ] + + edition = "2021" + cargo_pkg_version = "1.0.228" + cargo_pkg_authors = "Erick Tryzelaar , David Tolnay " + cargo_pkg_name = "serde" + cargo_pkg_description = "A generic serialization/deserialization framework" + features = [ + "default", + "std", + ] +} diff --git a/serde_core/Cargo.toml b/serde_core/Cargo.toml new file mode 100644 index 00000000..6705c998 --- /dev/null +++ b/serde_core/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "serde_core" +version = "1.0.228" +authors = ["Erick Tryzelaar ", "David Tolnay "] +build = "build.rs" +categories = ["encoding", "no-std", "no-std::no-alloc"] +description = "Serde traits only, with no support for derive -- use the `serde` crate instead" +documentation = "https://docs.rs/serde_core" +edition = "2021" +homepage = "https://serde.rs" +keywords = ["serde", "serialization", "no_std"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/serde-rs/serde" +rust-version = "1.56" + +[dev-dependencies] +serde = { version = "1", path = "../serde" } +serde_derive = { version = "1", path = "../serde_derive" } + +[package.metadata.playground] +features = ["rc", "result"] + +[package.metadata.docs.rs] +features = ["rc", "result", "unstable"] +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "--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 +# separately with serde's "derive" feature disabled. Every serde_derive release +# 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.228", path = "../serde_derive" } + + +### FEATURES ################################################################# + +[features] +default = ["std", "result"] + +# Provide impls for common standard library types like Vec and HashMap. +# Requires a dependency on the Rust standard library. +std = [] + +# Provide impls for types that require unstable functionality. For tracking and +# discussion of unstable functionality please refer to this issue: +# +# https://github.com/serde-rs/serde/issues/812 +unstable = [] + +# Provide impls for types in the Rust core allocation and collections library +# including String, Box, Vec, and Cow. This is a subset of std but may +# be enabled without depending on all of std. +alloc = [] + +# Opt into impls for Rc and Arc. Serializing and deserializing these types +# does not preserve identity and may result in multiple copies of the same data. +# Be sure that this is what you want before enabling this feature. +rc = [] + +# Provide impls for Result. Convenient in some contexts but can lead to +# confusion if ? or unwrap are used incautiously. +result = [] diff --git a/serde_core/LICENSE-APACHE b/serde_core/LICENSE-APACHE new file mode 100644 index 00000000..965b606f --- /dev/null +++ b/serde_core/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/serde_core/LICENSE-MIT b/serde_core/LICENSE-MIT new file mode 100644 index 00000000..76219eb7 --- /dev/null +++ b/serde_core/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/serde_core/README.md b/serde_core/README.md new file mode 100644 index 00000000..94a765e2 --- /dev/null +++ b/serde_core/README.md @@ -0,0 +1,28 @@ +The `serde_core` crate contains Serde's trait definitions with **no support for +#\[derive()\]**. + +In crates that derive an implementation of `Serialize` or `Deserialize`, you +must depend on the [`serde`] crate, not `serde_core`. + +[`serde`]: https://crates.io/crates/serde + +In crates that handwrite implementations of Serde traits, or only use them as +trait bounds, depending on `serde_core` is permitted. But `serde` re-exports all +of these traits and can be used for this use case too. If in doubt, disregard +`serde_core` and always use `serde`. + +Crates that depend on `serde_core` instead of `serde` are able to compile in +parallel with `serde_derive` even when `serde`'s "derive" feature is turned on, +as shown in the following build timings. + +
+ +| When `serde_json` depends on `serde` | +|---| +| | + +
+ +| When `serde_json` depends on `serde_core` | +|---| +| | diff --git a/serde_core/build.rs b/serde_core/build.rs new file mode 100644 index 00000000..9a33ab30 --- /dev/null +++ b/serde_core/build.rs @@ -0,0 +1,113 @@ +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use std::str; + +const PRIVATE: &str = "\ +#[doc(hidden)] +pub mod __private$$ { + #[doc(hidden)] + pub use crate::private::*; +} +"; + +// 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 +// these cfgs other than by executing our build script. +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let patch_version = env::var("CARGO_PKG_VERSION_PATCH").unwrap(); + let module = PRIVATE.replace("$$", &patch_version); + fs::write(out_dir.join("private.rs"), module).unwrap(); + + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + if minor >= 77 { + println!("cargo:rustc-check-cfg=cfg(if_docsrs_then_no_serde_core)"); + 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_diagnostic_namespace)"); + 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_target_has_atomic)"); + } + + let target = env::var("TARGET").unwrap(); + let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten"; + + // Support for #[cfg(target_has_atomic = "...")] stabilized in Rust 1.60. + if minor < 60 { + println!("cargo:rustc-cfg=no_target_has_atomic"); + // Allowlist of archs that support std::sync::atomic module. This is + // based on rustc's compiler/rustc_target/src/spec/*.rs. + let has_atomic64 = target.starts_with("x86_64") + || target.starts_with("i686") + || target.starts_with("aarch64") + || target.starts_with("powerpc64") + || target.starts_with("sparc64") + || target.starts_with("mips64el") + || target.starts_with("riscv64"); + let has_atomic32 = has_atomic64 || emscripten; + if minor < 34 || !has_atomic64 { + println!("cargo:rustc-cfg=no_std_atomic64"); + } + if minor < 34 || !has_atomic32 { + println!("cargo:rustc-cfg=no_std_atomic"); + } + } + + // 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"); + } + + // 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"); + } + + // 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"); + } + + // 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; + } + pieces.next()?.parse().ok() +} diff --git a/serde_core/src/crate_root.rs b/serde_core/src/crate_root.rs new file mode 100644 index 00000000..7ddd6eec --- /dev/null +++ b/serde_core/src/crate_root.rs @@ -0,0 +1,171 @@ +macro_rules! crate_root { + () => { + /// A facade around all the types we need from the `std`, `core`, and `alloc` + /// crates. This avoids elaborate import wrangling having to happen in every + /// module. + mod lib { + mod core { + #[cfg(not(feature = "std"))] + pub use core::*; + #[cfg(feature = "std")] + pub use std::*; + } + + pub use self::core::{f32, f64}; + pub use self::core::{iter, num, str}; + + #[cfg(any(feature = "std", feature = "alloc"))] + pub use self::core::{cmp, mem}; + + pub use self::core::cell::{Cell, RefCell}; + pub use self::core::cmp::Reverse; + pub use self::core::fmt::{self, Debug, Display, Write as FmtWrite}; + pub use self::core::marker::PhantomData; + pub use self::core::num::Wrapping; + pub use self::core::ops::{Bound, Range, RangeFrom, RangeInclusive, RangeTo}; + pub use self::core::result; + pub use self::core::time::Duration; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::borrow::{Cow, ToOwned}; + #[cfg(feature = "std")] + pub use std::borrow::{Cow, ToOwned}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::string::{String, ToString}; + #[cfg(feature = "std")] + pub use std::string::{String, ToString}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::vec::Vec; + #[cfg(feature = "std")] + pub use std::vec::Vec; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::boxed::Box; + #[cfg(feature = "std")] + pub use std::boxed::Box; + + #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))] + pub use alloc::rc::{Rc, Weak as RcWeak}; + #[cfg(all(feature = "rc", feature = "std"))] + pub use std::rc::{Rc, Weak as RcWeak}; + + #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))] + pub use alloc::sync::{Arc, Weak as ArcWeak}; + #[cfg(all(feature = "rc", feature = "std"))] + pub use std::sync::{Arc, Weak as ArcWeak}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; + #[cfg(feature = "std")] + pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; + + #[cfg(all(not(no_core_cstr), not(feature = "std")))] + pub use self::core::ffi::CStr; + #[cfg(feature = "std")] + pub use std::ffi::CStr; + + #[cfg(all(not(no_core_cstr), feature = "alloc", not(feature = "std")))] + pub use alloc::ffi::CString; + #[cfg(feature = "std")] + pub use std::ffi::CString; + + #[cfg(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; + + #[cfg(feature = "std")] + pub use std::collections::{HashMap, HashSet}; + #[cfg(feature = "std")] + pub use std::ffi::{OsStr, OsString}; + #[cfg(feature = "std")] + pub use std::hash::{BuildHasher, Hash}; + #[cfg(feature = "std")] + pub use std::io::Write; + #[cfg(feature = "std")] + pub use std::path::{Path, PathBuf}; + #[cfg(feature = "std")] + pub use std::sync::{Mutex, RwLock}; + #[cfg(feature = "std")] + pub use std::time::{SystemTime, UNIX_EPOCH}; + + #[cfg(all(feature = "std", no_target_has_atomic, not(no_std_atomic)))] + pub use std::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, + AtomicU8, AtomicUsize, Ordering, + }; + #[cfg(all(feature = "std", no_target_has_atomic, not(no_std_atomic64)))] + pub use std::sync::atomic::{AtomicI64, AtomicU64}; + + #[cfg(all(feature = "std", not(no_target_has_atomic)))] + pub use std::sync::atomic::Ordering; + #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "8"))] + pub use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU8}; + #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "16"))] + pub use std::sync::atomic::{AtomicI16, AtomicU16}; + #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "32"))] + pub use std::sync::atomic::{AtomicI32, AtomicU32}; + #[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "64"))] + 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 + // performed implicitly by the `?` operator or the standard library's `try!` + // macro. This simplified macro gives a 5.5% improvement in compile time + // compared to standard `try!`, and 9% improvement compared to `?`. + macro_rules! tri { + ($expr:expr) => { + match $expr { + Ok(val) => val, + Err(err) => return Err(err), + } + }; + } + + #[cfg_attr(all(docsrs, if_docsrs_then_no_serde_core), path = "core/de/mod.rs")] + pub mod de; + #[cfg_attr(all(docsrs, if_docsrs_then_no_serde_core), path = "core/ser/mod.rs")] + pub mod ser; + + #[cfg_attr(all(docsrs, if_docsrs_then_no_serde_core), path = "core/format.rs")] + mod format; + + #[doc(inline)] + pub use crate::de::{Deserialize, Deserializer}; + #[doc(inline)] + pub use crate::ser::{Serialize, Serializer}; + + // Used by generated code. Not public API. + #[doc(hidden)] + #[cfg_attr( + all(docsrs, if_docsrs_then_no_serde_core), + path = "core/private/mod.rs" + )] + mod private; + + // Used by declarative macro generated code. Not public API. + #[doc(hidden)] + pub mod __private { + #[doc(hidden)] + pub use crate::private::doc; + #[doc(hidden)] + pub use core::result::Result; + } + + include!("extend.rs"); + + #[cfg(all(not(feature = "std"), no_core_error))] + #[cfg_attr(all(docsrs, if_docsrs_then_no_serde_core), path = "core/std_error.rs")] + mod std_error; + }; +} diff --git a/serde/src/de/ignored_any.rs b/serde_core/src/de/ignored_any.rs similarity index 100% rename from serde/src/de/ignored_any.rs rename to serde_core/src/de/ignored_any.rs diff --git a/serde/src/de/impls.rs b/serde_core/src/de/impls.rs similarity index 88% rename from serde/src/de/impls.rs rename to serde_core/src/de/impls.rs index 413c997a..ab1a893c 100644 --- a/serde/src/de/impls.rs +++ b/serde_core/src/de/impls.rs @@ -4,11 +4,10 @@ use crate::de::{ Deserialize, Deserializer, EnumAccess, Error, MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, }; - -use crate::seed::InPlaceSeed; +use crate::private::{self, InPlaceSeed}; #[cfg(any(feature = "std", feature = "alloc"))] -use crate::de::size_hint; +use crate::private::size_hint; //////////////////////////////////////////////////////////////////////////////// @@ -39,7 +38,7 @@ impl<'de> Deserialize<'de> for () { } #[cfg(feature = "unstable")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "unstable")))] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl<'de> Deserialize<'de> for ! { fn deserialize(_deserializer: D) -> Result where @@ -80,10 +79,9 @@ impl<'de> Deserialize<'de> for bool { //////////////////////////////////////////////////////////////////////////////// macro_rules! impl_deserialize_num { - ($primitive:ident, $nonzero:ident $(cfg($($cfg:tt)*))*, $deserialize:ident $($method:ident!($($val:ident : $visit:ident)*);)*) => { + ($primitive:ident, $nonzero:ident, $deserialize:ident $($method:ident!($($val:ident : $visit:ident)*);)*) => { impl_deserialize_num!($primitive, $deserialize $($method!($($val : $visit)*);)*); - $(#[cfg($($cfg)*)])* impl<'de> Deserialize<'de> for num::$nonzero { fn deserialize(deserializer: D) -> Result where @@ -104,6 +102,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)*);)*) => { @@ -154,6 +174,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 { @@ -179,6 +208,15 @@ 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 { @@ -188,12 +226,12 @@ macro_rules! num_as_copysign_self { where E: Error, { - #[cfg(any(no_float_copysign, not(feature = "std")))] + #[cfg(not(feature = "std"))] { Ok(v as Self::Value) } - #[cfg(all(not(no_float_copysign), feature = "std"))] + #[cfg(feature = "std")] { // Preserve sign of NaN. The `as` produces a nondeterministic sign. let sign = if v.is_sign_positive() { 1.0 } else { -1.0 }; @@ -210,13 +248,8 @@ macro_rules! int_to_int { where E: Error, { - if Self::Value::min_value() as i64 <= v as i64 - && v as i64 <= Self::Value::max_value() as i64 - { - Ok(v as Self::Value) - } else { - Err(Error::invalid_value(Unexpected::Signed(v as i64), &self)) - } + Self::Value::try_from(v as i64) + .map_err(|_| Error::invalid_value(Unexpected::Signed(v as i64), &self)) } }; @@ -225,16 +258,29 @@ macro_rules! int_to_int { where E: Error, { - if $primitive::min_value() as i64 <= v as i64 - && v as i64 <= $primitive::max_value() as i64 - { - if let Some(nonzero) = Self::Value::new(v as $primitive) { + if let Ok(v) = $primitive::try_from(v as i64) { + if let Some(nonzero) = Self::Value::new(v) { return Ok(nonzero); } } 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 { @@ -244,11 +290,13 @@ macro_rules! int_to_uint { where E: Error, { - if 0 <= v && v as u64 <= Self::Value::max_value() as u64 { - Ok(v as Self::Value) - } else { - Err(Error::invalid_value(Unexpected::Signed(v as i64), &self)) + if 0 <= v { + #[allow(irrefutable_let_patterns)] + if let Ok(v) = Self::Value::try_from(v as u64) { + return Ok(v as Self::Value); + } } + Err(Error::invalid_value(Unexpected::Signed(v as i64), &self)) } }; @@ -257,14 +305,32 @@ macro_rules! int_to_uint { where E: Error, { - if 0 < v && v as u64 <= $primitive::max_value() as u64 { - if let Some(nonzero) = Self::Value::new(v as $primitive) { - return Ok(nonzero); + if 0 < v { + #[allow(irrefutable_let_patterns)] + if let Ok(v) = $primitive::try_from(v as u64) { + if let Some(nonzero) = Self::Value::new(v) { + return Ok(nonzero); + } } } 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 { @@ -274,11 +340,8 @@ macro_rules! uint_to_self { where E: Error, { - if v as u64 <= Self::Value::max_value() as u64 { - Ok(v as Self::Value) - } else { - Err(Error::invalid_value(Unexpected::Unsigned(v as u64), &self)) - } + Self::Value::try_from(v as u64) + .map_err(|_| Error::invalid_value(Unexpected::Unsigned(v as u64), &self)) } }; @@ -287,25 +350,38 @@ macro_rules! uint_to_self { where E: Error, { - if v as u64 <= $primitive::max_value() as u64 { - if let Some(nonzero) = Self::Value::new(v as $primitive) { + if let Ok(v) = $primitive::try_from(v as u64) { + if let Some(nonzero) = Self::Value::new(v) { return Ok(nonzero); } } 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 let Ok(v) = $primitive::try_from(v as u64) { + Ok(Saturating(v as $primitive)) + } else { + Ok(Saturating($primitive::MAX)) + } + } + }; } impl_deserialize_num! { - i8, NonZeroI8 cfg(not(no_num_nonzero_signed)), deserialize_i8 + i8, NonZeroI8, deserialize_i8 num_self!(i8:visit_i8); int_to_int!(i16:visit_i16 i32:visit_i32 i64:visit_i64); uint_to_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } impl_deserialize_num! { - i16, NonZeroI16 cfg(not(no_num_nonzero_signed)), deserialize_i16 + i16, NonZeroI16, deserialize_i16 num_self!(i16:visit_i16); num_as_self!(i8:visit_i8); int_to_int!(i32:visit_i32 i64:visit_i64); @@ -313,7 +389,7 @@ impl_deserialize_num! { } impl_deserialize_num! { - i32, NonZeroI32 cfg(not(no_num_nonzero_signed)), deserialize_i32 + i32, NonZeroI32, deserialize_i32 num_self!(i32:visit_i32); num_as_self!(i8:visit_i8 i16:visit_i16); int_to_int!(i64:visit_i64); @@ -321,14 +397,14 @@ impl_deserialize_num! { } impl_deserialize_num! { - i64, NonZeroI64 cfg(not(no_num_nonzero_signed)), deserialize_i64 + i64, NonZeroI64, deserialize_i64 num_self!(i64:visit_i64); num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32); uint_to_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } impl_deserialize_num! { - isize, NonZeroIsize cfg(not(no_num_nonzero_signed)), deserialize_i64 + isize, NonZeroIsize, deserialize_i64 num_as_self!(i8:visit_i8 i16:visit_i16); int_to_int!(i32:visit_i32 i64:visit_i64); uint_to_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); @@ -393,9 +469,7 @@ macro_rules! num_128 { where E: Error, { - if v as i128 >= Self::Value::min_value() as i128 - && v as u128 <= Self::Value::max_value() as u128 - { + if v as i128 >= Self::Value::MIN as i128 && v as u128 <= Self::Value::MAX as u128 { Ok(v as Self::Value) } else { Err(Error::invalid_value( @@ -411,9 +485,7 @@ macro_rules! num_128 { where E: Error, { - if v as i128 >= $primitive::min_value() as i128 - && v as u128 <= $primitive::max_value() as u128 - { + if v as i128 >= $primitive::MIN as i128 && v as u128 <= $primitive::MAX as u128 { if let Some(nonzero) = Self::Value::new(v as $primitive) { Ok(nonzero) } else { @@ -427,10 +499,25 @@ 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! { - i128, NonZeroI128 cfg(not(no_num_nonzero_signed)), deserialize_i128 + i128, NonZeroI128, deserialize_i128 num_self!(i128:visit_i128); 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); @@ -597,7 +684,7 @@ impl<'a, 'de> Visitor<'de> for StringInPlaceVisitor<'a> { } #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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 @@ -741,7 +828,7 @@ impl<'de> Visitor<'de> for CStringVisitor { } #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] -#[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl<'de> Deserialize<'de> for CString { fn deserialize(deserializer: D) -> Result where @@ -770,7 +857,7 @@ macro_rules! forwarded_impl { forwarded_impl! { #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] (), Box, CString::into_boxed_c_str } @@ -852,7 +939,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 { @@ -868,7 +958,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>, @@ -982,7 +1075,7 @@ fn nop_reserve(_seq: T, _n: usize) {} seq_impl!( #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BinaryHeap, seq, BinaryHeap::clear, @@ -993,7 +1086,7 @@ seq_impl!( seq_impl!( #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BTreeSet, seq, BTreeSet::clear, @@ -1004,7 +1097,7 @@ seq_impl!( seq_impl!( #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] LinkedList, seq, LinkedList::clear, @@ -1015,7 +1108,7 @@ seq_impl!( seq_impl!( #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] HashSet, seq, HashSet::clear, @@ -1026,7 +1119,7 @@ seq_impl!( seq_impl!( #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] VecDeque, seq, VecDeque::clear, @@ -1038,7 +1131,7 @@ seq_impl!( //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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>, @@ -1289,82 +1382,103 @@ array_impls! { macro_rules! tuple_impls { ($($len:tt => ($($n:tt $name:ident)+))+) => { $( - impl<'de, $($name: Deserialize<'de>),+> Deserialize<'de> for ($($name,)+) { - #[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] - #[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)), - }; - )+ - - Ok(($($name,)+)) - } - } - - deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData }) - } - - #[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,)+)); - - 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(()) - } - } - - deserializer.deserialize_tuple($len, TupleInPlaceVisitor(place)) - } + #[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] + #[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)), + }; + )+ + + Ok(($($name,)+)) + } + } + + deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData }) + } + + #[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,)+)); + + 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(()) + } + } + + 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) @@ -1442,7 +1556,7 @@ macro_rules! map_impl { map_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BTreeMap, map, BTreeMap::new(), @@ -1450,7 +1564,7 @@ map_impl! { map_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(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()), @@ -1458,12 +1572,9 @@ map_impl! { //////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! parse_ip_impl { - ( - $(#[$attr:meta])* - $ty:ty, $expecting:expr, $size:tt - ) => { - $(#[$attr])* + ($ty:ty, $expecting:expr, $size:tt) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -1479,7 +1590,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),*) @@ -1554,7 +1665,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),*) @@ -1591,8 +1702,7 @@ macro_rules! deserialize_enum { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl<'de> Deserialize<'de> for net::IpAddr { fn deserialize(deserializer: D) -> Result where @@ -1611,25 +1721,18 @@ impl<'de> Deserialize<'de> for net::IpAddr { } } -parse_ip_impl! { - #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] - net::Ipv4Addr, "IPv4 address", 4 -} +#[cfg(any(feature = "std", not(no_core_net)))] +parse_ip_impl!(net::Ipv4Addr, "IPv4 address", 4); -parse_ip_impl! { - #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] - net::Ipv6Addr, "IPv6 address", 16 -} +#[cfg(any(feature = "std", not(no_core_net)))] +parse_ip_impl!(net::Ipv6Addr, "IPv6 address", 16); +#[cfg(any(feature = "std", not(no_core_net)))] macro_rules! parse_socket_impl { ( - $(#[$attr:meta])* $ty:ty, $expecting:tt, $new:expr, ) => { - $(#[$attr])* impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where @@ -1645,8 +1748,7 @@ macro_rules! parse_socket_impl { }; } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl<'de> Deserialize<'de> for net::SocketAddr { fn deserialize(deserializer: D) -> Result where @@ -1665,16 +1767,14 @@ impl<'de> Deserialize<'de> for net::SocketAddr { } } +#[cfg(any(feature = "std", not(no_core_net)))] parse_socket_impl! { - #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] net::SocketAddrV4, "IPv4 socket address", |(ip, port)| net::SocketAddrV4::new(ip, port), } +#[cfg(any(feature = "std", not(no_core_net)))] parse_socket_impl! { - #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] net::SocketAddrV6, "IPv6 socket address", |(ip, port)| net::SocketAddrV6::new(ip, port, 0, 0), } @@ -1710,7 +1810,7 @@ impl<'a> Visitor<'a> for PathVisitor { } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(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 @@ -1765,7 +1865,7 @@ impl<'de> Visitor<'de> for PathBufVisitor { } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de> Deserialize<'de> for PathBuf { fn deserialize(deserializer: D) -> Result where @@ -1777,7 +1877,7 @@ impl<'de> Deserialize<'de> for PathBuf { forwarded_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] (), Box, PathBuf::into_boxed_path } @@ -1839,7 +1939,7 @@ impl<'de> Visitor<'de> for OsStringVisitor { } #[cfg(all(feature = "std", any(unix, windows)))] -#[cfg_attr(doc_cfg, doc(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 @@ -1853,33 +1953,33 @@ impl<'de> Deserialize<'de> for OsString { forwarded_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] (T), Box, Box::new } forwarded_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] (T), Box<[T]>, Vec::into_boxed_slice } forwarded_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] (), Box, String::into_boxed_str } forwarded_impl! { #[cfg(all(feature = "std", any(unix, windows)))] - #[cfg_attr(doc_cfg, doc(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"))] -#[cfg_attr(doc_cfg, doc(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] @@ -1899,10 +1999,10 @@ where /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) )] -impl<'de, T: ?Sized> Deserialize<'de> for RcWeak +impl<'de, T> Deserialize<'de> for RcWeak where T: Deserialize<'de>, { @@ -1921,10 +2021,10 @@ where /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) )] -impl<'de, T: ?Sized> Deserialize<'de> for ArcWeak +impl<'de, T> Deserialize<'de> for ArcWeak where T: Deserialize<'de>, { @@ -1945,8 +2045,9 @@ macro_rules! box_forwarded_impl { $t:ident ) => { $(#[$attr])* - impl<'de, T: ?Sized> Deserialize<'de> for $t + impl<'de, T> Deserialize<'de> for $t where + T: ?Sized, Box: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result @@ -1968,7 +2069,7 @@ box_forwarded_impl! { /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] Rc } @@ -1981,7 +2082,7 @@ box_forwarded_impl! { /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] Arc } @@ -2005,13 +2106,13 @@ forwarded_impl! { forwarded_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] (T), Mutex, Mutex::new } forwarded_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] (T), RwLock, RwLock::new } @@ -2072,7 +2173,7 @@ impl<'de> Deserialize<'de> for Duration { b"secs" => Ok(Field::Secs), b"nanos" => Ok(Field::Nanos), _ => { - let value = crate::__private::from_utf8_lossy(value); + let value = private::string::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) } } @@ -2166,7 +2267,7 @@ impl<'de> Deserialize<'de> for Duration { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl<'de> Deserialize<'de> for SystemTime { fn deserialize(deserializer: D) -> Result where @@ -2303,13 +2404,9 @@ impl<'de> Deserialize<'de> for SystemTime { const FIELDS: &[&str] = &["secs_since_epoch", "nanos_since_epoch"]; let duration = tri!(deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor)); - #[cfg(not(no_systemtime_checked_add))] - let ret = UNIX_EPOCH + UNIX_EPOCH .checked_add(duration) - .ok_or_else(|| D::Error::custom("overflow deserializing SystemTime")); - #[cfg(no_systemtime_checked_add)] - let ret = Ok(UNIX_EPOCH + duration); - ret + .ok_or_else(|| D::Error::custom("overflow deserializing SystemTime")) } } @@ -2367,6 +2464,7 @@ mod range { use crate::lib::*; use crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; + use crate::private; pub const FIELDS: &[&str] = &["start", "end"]; @@ -2412,7 +2510,7 @@ mod range { b"start" => Ok(Field::Start), b"end" => Ok(Field::End), _ => { - let value = crate::__private::from_utf8_lossy(value); + let value = private::string::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) } } @@ -2525,6 +2623,7 @@ mod range_from { use crate::lib::*; use crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; + use crate::private; pub const FIELDS: &[&str] = &["start"]; @@ -2567,7 +2666,7 @@ mod range_from { match value { b"start" => Ok(Field::Start), _ => { - let value = crate::__private::from_utf8_lossy(value); + let value = private::string::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) } } @@ -2663,6 +2762,7 @@ mod range_to { use crate::lib::*; use crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; + use crate::private; pub const FIELDS: &[&str] = &["end"]; @@ -2705,7 +2805,7 @@ mod range_to { match value { b"end" => Ok(Field::End), _ => { - let value = crate::__private::from_utf8_lossy(value); + let value = private::string::from_utf8_lossy(value); Err(Error::unknown_field(&*value, FIELDS)) } } @@ -2877,6 +2977,8 @@ where //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "result")] +#[cfg_attr(docsrs, doc(cfg(feature = "result")))] impl<'de, T, E> Deserialize<'de> for Result where T: Deserialize<'de>, @@ -3002,7 +3104,7 @@ macro_rules! atomic_impl { ($($ty:ident $size:expr)*) => { $( #[cfg(any(no_target_has_atomic, target_has_atomic = $size))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", 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 @@ -3034,13 +3136,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 { @@ -3050,7 +3152,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_core/src/de/mod.rs similarity index 95% rename from serde/src/de/mod.rs rename to serde_core/src/de/mod.rs index 1924fe3d..2518ae68 100644 --- a/serde/src/de/mod.rs +++ b/serde_core/src/de/mod.rs @@ -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,14 @@ 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")] @@ -159,6 +156,12 @@ macro_rules! declare_error_trait { /// type appropriate for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html + #[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::Error` is not satisfied", + ) + )] pub trait Error: Sized $(+ $($supertrait)::+)* { /// Raised when there is general error when deserializing a type. /// @@ -207,7 +210,7 @@ macro_rules! declare_error_trait { /// containing an integer, the unexpected type is the integer and the /// expected type is the string. #[cold] - fn invalid_type(unexp: Unexpected, exp: &Expected) -> Self { + fn invalid_type(unexp: Unexpected, exp: &dyn Expected) -> Self { Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) } @@ -225,7 +228,7 @@ macro_rules! declare_error_trait { /// that is not valid UTF-8, the unexpected value is the bytes and the /// expected value is a string. #[cold] - fn invalid_value(unexp: Unexpected, exp: &Expected) -> Self { + fn invalid_value(unexp: Unexpected, exp: &dyn Expected) -> Self { Error::custom(format_args!("invalid value: {}, expected {}", unexp, exp)) } @@ -239,7 +242,7 @@ macro_rules! declare_error_trait { /// expected. For example `exp` might say that a tuple of size 6 was /// expected. #[cold] - fn invalid_length(len: usize, exp: &Expected) -> Self { + fn invalid_length(len: usize, exp: &dyn Expected) -> Self { Error::custom(format_args!("invalid length {}, expected {}", len, exp)) } @@ -472,6 +475,12 @@ impl<'a> fmt::Display for Unexpected<'a> { /// )); /// # } /// ``` +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::Expected` is not satisfied", + ) +)] pub trait Expected { /// Format an explanation of what data was being expected. Same signature as /// the `Display` and `Debug` traits. @@ -487,13 +496,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 dyn Expected + '_ { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { Expected::fmt(self, formatter) } @@ -532,6 +541,16 @@ 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( + // Prevents `serde_core::de::Deserialize` appearing in the error message + // in projects with no direct dependency on serde_core. + message = "the trait bound `{Self}: serde::Deserialize<'de>` is not satisfied", + 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. /// @@ -604,6 +623,12 @@ pub trait Deserialize<'de>: Sized { /// lifetimes]. /// /// [Understanding deserializer lifetimes]: https://serde.rs/lifetimes.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::DeserializeOwned` is not satisfied", + ) +)] pub trait DeserializeOwned: for<'de> Deserialize<'de> {} impl DeserializeOwned for T where T: for<'de> Deserialize<'de> {} @@ -769,6 +794,12 @@ impl DeserializeOwned for T where T: for<'de> Deserialize<'de> {} /// # Ok(()) /// # } /// ``` +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::DeserializeSeed<'de>` is not satisfied", + ) +)] pub trait DeserializeSeed<'de>: Sized { /// The type produced by using this seed. type Value; @@ -905,6 +936,12 @@ where /// a basic JSON `Deserializer`. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::Deserializer<'de>` is not satisfied", + ) +)] pub trait Deserializer<'de>: Sized { /// The error type that can be returned if some error occurs during /// deserialization. @@ -1220,13 +1257,9 @@ pub trait Deserializer<'de>: Sized { // Not public API. #[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))] #[doc(hidden)] - fn __deserialize_content( - self, - _: crate::actually_private::T, - visitor: V, - ) -> Result, Self::Error> + fn __deserialize_content_v1(self, visitor: V) -> Result where - V: Visitor<'de, Value = crate::__private::de::Content<'de>>, + V: Visitor<'de, Value = crate::private::Content<'de>>, { self.deserialize_any(visitor) } @@ -1275,6 +1308,12 @@ pub trait Deserializer<'de>: Sized { /// } /// } /// ``` +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::Visitor<'de>` is not satisfied", + ) +)] pub trait Visitor<'de>: Sized { /// The value produced by this visitor. type Value; @@ -1367,7 +1406,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 +1468,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,7 +1564,7 @@ pub trait Visitor<'de>: Sized { /// `String`. #[inline] #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(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, @@ -1584,7 +1623,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(doc_cfg, doc(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, @@ -1701,6 +1740,12 @@ pub trait Visitor<'de>: Sized { /// implementation of `SeqAccess` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::SeqAccess<'de>` is not satisfied", + ) +)] pub trait SeqAccess<'de> { /// The error type that can be returned if some error occurs during /// deserialization. @@ -1735,9 +1780,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; @@ -1783,6 +1828,12 @@ where /// implementation of `MapAccess` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::MapAccess<'de>` is not satisfied", + ) +)] pub trait MapAccess<'de> { /// The error type that can be returned if some error occurs during /// deserialization. @@ -1888,9 +1939,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; @@ -1975,6 +2026,12 @@ where /// implementation of `EnumAccess` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::EnumAccess<'de>` is not satisfied", + ) +)] pub trait EnumAccess<'de>: Sized { /// The error type that can be returned if some error occurs during /// deserialization. @@ -2022,6 +2079,12 @@ pub trait EnumAccess<'de>: Sized { /// implementation of `VariantAccess` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::de::VariantAccess<'de>` is not satisfied", + ) +)] pub trait VariantAccess<'de>: Sized { /// The error type that can be returned if some error occurs during /// deserialization. Must match the error type of our `EnumAccess`. @@ -2312,13 +2375,17 @@ impl Display for WithDecimalPoint { } } - let mut writer = LookForDecimalPoint { - formatter, - has_decimal_point: false, - }; - tri!(write!(writer, "{}", self.0)); - if !writer.has_decimal_point { - tri!(formatter.write_str(".0")); + 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_core/src/de/value.rs similarity index 91% rename from serde/src/de/value.rs rename to serde_core/src/de/value.rs index 3bc0c71c..3d5475de 100644 --- a/serde/src/de/value.rs +++ b/serde_core/src/de/value.rs @@ -24,7 +24,8 @@ use crate::lib::*; use self::private::{First, Second}; -use crate::de::{self, size_hint, Deserializer, Expected, IntoDeserializer, SeqAccess, Visitor}; +use crate::de::{self, Deserializer, Expected, IntoDeserializer, SeqAccess, Visitor}; +use crate::private::size_hint; use crate::ser; //////////////////////////////////////////////////////////////////////////////// @@ -112,7 +113,7 @@ impl Debug for Error { } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl error::Error for Error { fn description(&self) -> &str { &self.err @@ -175,6 +176,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() @@ -185,14 +197,14 @@ impl Debug for UnitDeserializer { /// A deserializer that cannot be instantiated. #[cfg(feature = "unstable")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "unstable")))] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] pub struct NeverDeserializer { never: !, marker: PhantomData, } #[cfg(feature = "unstable")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "unstable")))] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl<'de, E> IntoDeserializer<'de, E> for ! where E: de::Error, @@ -225,6 +237,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 { @@ -279,6 +303,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 @@ -369,6 +404,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, @@ -458,6 +504,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, @@ -537,6 +594,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, @@ -565,7 +633,7 @@ impl<'de, E> Debug for BorrowedStrDeserializer<'de, E> { /// A deserializer holding a `String`. #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] pub struct StringDeserializer { value: String, marker: PhantomData, @@ -582,7 +650,7 @@ impl Clone for StringDeserializer { } #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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, @@ -640,6 +708,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 @@ -670,7 +750,7 @@ impl Debug for StringDeserializer { /// A deserializer holding a `Cow`. #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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, @@ -687,7 +767,7 @@ impl<'a, E> Clone for CowStrDeserializer<'a, E> { } #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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, @@ -748,6 +828,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 @@ -825,6 +917,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 @@ -873,6 +976,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 @@ -952,6 +1066,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, @@ -1006,7 +1133,7 @@ where //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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>, @@ -1020,7 +1147,7 @@ where } #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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, @@ -1034,7 +1161,7 @@ where } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(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, @@ -1083,6 +1210,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. @@ -1197,6 +1335,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, @@ -1421,7 +1574,7 @@ impl Expected for ExpectedInMap { //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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, @@ -1436,7 +1589,7 @@ where } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(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, @@ -1498,6 +1651,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>, @@ -1551,6 +1715,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_core/src/extend.rs b/serde_core/src/extend.rs new file mode 100644 index 00000000..83ef1836 --- /dev/null +++ b/serde_core/src/extend.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2026 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[doc(hidden)] +pub mod __private228 { +#[doc(hidden)] +pub use crate::private::*; +} \ No newline at end of file diff --git a/serde/src/de/format.rs b/serde_core/src/format.rs similarity index 100% rename from serde/src/de/format.rs rename to serde_core/src/format.rs diff --git a/serde_core/src/lib.rs b/serde_core/src/lib.rs new file mode 100644 index 00000000..9118c456 --- /dev/null +++ b/serde_core/src/lib.rs @@ -0,0 +1,121 @@ +//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data +//! structures efficiently and generically. +//! +//! The `serde_core` crate contains Serde's trait definitions with **no support +//! for #\[derive()\]**. +//! +//! In crates that derive an implementation of `Serialize` or `Deserialize`, you +//! must depend on the [`serde`] crate, not `serde_core`. +//! +//! [`serde`]: https://crates.io/crates/serde +//! +//! In crates that handwrite implementations of Serde traits, or only use them +//! as trait bounds, depending on `serde_core` is permitted. But `serde` +//! re-exports all of these traits and can be used for this use case too. If in +//! doubt, disregard `serde_core` and always use `serde`. +//! +//! Crates that depend on `serde_core` instead of `serde` are able to compile in +//! parallel with `serde_derive` even when `serde`'s "derive" feature is turned on, +//! as shown in the following build timings. +//! +//!
+//! +//! +//! +//! +//!
When serde_json depends on serde
+//! +//!
+//! +//! +//! +//! +//!
When serde_json depends on serde_core
+ +//////////////////////////////////////////////////////////////////////////////// +#![feature(saturating_int_impl)] +// Serde types in rustdoc of other crates get linked to here. +#![doc(html_root_url = "https://docs.rs/serde_core/1.0.228")] +// 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(never_type))] +#![allow(unknown_lints, bare_trait_objects, deprecated)] +// Ignored clippy and clippy_pedantic lints +#![allow( + // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 + clippy::unnested_or_patterns, + // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7768 + clippy::semicolon_if_nothing_returned, + // not available in our oldest supported compiler + clippy::empty_enum, + clippy::type_repetition_in_bounds, // https://github.com/rust-lang/rust-clippy/issues/8772 + // 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::single_match_else, + clippy::type_complexity, + clippy::use_self, + clippy::zero_prefixed_literal, + // correctly used + 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, + clippy::result_unit_err, + clippy::wildcard_imports, + // not practical + clippy::needless_pass_by_value, + clippy::similar_names, + clippy::too_many_lines, + // preference + clippy::doc_markdown, + clippy::elidable_lifetime_names, + clippy::needless_lifetimes, + clippy::unseparated_literal_suffix, + // false positive + clippy::needless_doctest_main, + // noisy + clippy::missing_errors_doc, + clippy::must_use_candidate, +)] +// Restrictions +#![deny(clippy::question_mark_used)] +// Rustc lints. +#![deny(missing_docs, unused_imports)] + +//////////////////////////////////////////////////////////////////////////////// + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[macro_use] +mod crate_root; +#[macro_use] +mod macros; + +crate_root!(); + +#[macro_export] +#[doc(hidden)] +macro_rules! __require_serde_not_serde_core { + () => { + ::core::compile_error!( + "Serde derive requires a dependency on the serde crate, not serde_core" + ); + }; +} diff --git a/serde/src/macros.rs b/serde_core/src/macros.rs similarity index 97% rename from serde/src/macros.rs rename to serde_core/src/macros.rs index a8fd85a3..b9567305 100644 --- a/serde/src/macros.rs +++ b/serde_core/src/macros.rs @@ -1,7 +1,6 @@ // Super explicit first paragraph because this shows up at the top level and // trips up people who are just looking for basic Serialize / Deserialize // documentation. -// /// Helper macro when implementing the `Deserializer` part of a new data format /// for Serde. /// @@ -104,9 +103,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 +122,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_core/src/private/content.rs b/serde_core/src/private/content.rs new file mode 100644 index 00000000..f29a9b52 --- /dev/null +++ b/serde_core/src/private/content.rs @@ -0,0 +1,39 @@ +use crate::lib::*; + +// Used from generated code to buffer the contents of the Deserializer when +// deserializing untagged enums and internally tagged enums. +// +// Not public API. Use serde-value instead. +// +// Obsoleted by format-specific buffer types (https://github.com/serde-rs/serde/pull/2912). +#[doc(hidden)] +pub enum Content<'de> { + Bool(bool), + + U8(u8), + U16(u16), + U32(u32), + U64(u64), + + I8(i8), + I16(i16), + I32(i32), + I64(i64), + + F32(f32), + F64(f64), + + Char(char), + String(String), + Str(&'de str), + ByteBuf(Vec), + Bytes(&'de [u8]), + + None, + Some(Box>), + + Unit, + Newtype(Box>), + Seq(Vec>), + Map(Vec<(Content<'de>, Content<'de>)>), +} diff --git a/serde/src/private/doc.rs b/serde_core/src/private/doc.rs similarity index 92% rename from serde/src/private/doc.rs rename to serde_core/src/private/doc.rs index 1b18fe6b..2cc07f0d 100644 --- a/serde/src/private/doc.rs +++ b/serde_core/src/private/doc.rs @@ -8,6 +8,7 @@ use crate::ser; #[derive(Debug)] pub struct Error; +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl ser::Error for Error { fn custom(_: T) -> Self where @@ -18,12 +19,14 @@ impl ser::Error for Error { } #[cfg(feature = "std")] +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl error::Error for Error { fn description(&self) -> &str { unimplemented!() } } +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl Display for Error { fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { unimplemented!() @@ -56,7 +59,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_core/src/private/mod.rs b/serde_core/src/private/mod.rs new file mode 100644 index 00000000..dd5f93fe --- /dev/null +++ b/serde_core/src/private/mod.rs @@ -0,0 +1,21 @@ +#[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))] +mod content; +mod seed; + +// FIXME: #[cfg(doctest)] once https://github.com/rust-lang/rust/issues/67295 is fixed. +#[doc(hidden)] +pub mod doc; + +#[doc(hidden)] +pub mod size_hint; + +#[doc(hidden)] +pub mod string; + +#[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))] +#[doc(hidden)] +pub use self::content::Content; +#[doc(hidden)] +pub use self::seed::InPlaceSeed; +#[doc(hidden)] +pub use crate::lib::result::Result; diff --git a/serde/src/de/seed.rs b/serde_core/src/private/seed.rs similarity index 88% rename from serde/src/de/seed.rs rename to serde_core/src/private/seed.rs index 52fb89d8..bcf267cb 100644 --- a/serde/src/de/seed.rs +++ b/serde_core/src/private/seed.rs @@ -5,6 +5,7 @@ use crate::de::{Deserialize, DeserializeSeed, Deserializer}; /// Wraps a mutable reference and calls deserialize_in_place on it. pub struct InPlaceSeed<'a, T: 'a>(pub &'a mut T); +#[cfg_attr(not(no_diagnostic_namespace), diagnostic::do_not_recommend)] impl<'a, 'de, T> DeserializeSeed<'de> for InPlaceSeed<'a, T> where T: Deserialize<'de>, diff --git a/serde/src/de/size_hint.rs b/serde_core/src/private/size_hint.rs similarity index 93% rename from serde/src/de/size_hint.rs rename to serde_core/src/private/size_hint.rs index 4a4fe25d..783281b4 100644 --- a/serde/src/de/size_hint.rs +++ b/serde_core/src/private/size_hint.rs @@ -1,3 +1,4 @@ +#[cfg(any(feature = "std", feature = "alloc"))] use crate::lib::*; pub fn from_bounds(iter: &I) -> Option diff --git a/serde_core/src/private/string.rs b/serde_core/src/private/string.rs new file mode 100644 index 00000000..c8121a0d --- /dev/null +++ b/serde_core/src/private/string.rs @@ -0,0 +1,23 @@ +use crate::lib::*; + +#[cfg(any(feature = "std", feature = "alloc"))] +#[doc(hidden)] +pub fn from_utf8_lossy(bytes: &[u8]) -> Cow<'_, str> { + String::from_utf8_lossy(bytes) +} + +// The generated code calls this like: +// +// let value = &_serde::__private::from_utf8_lossy(bytes); +// Err(_serde::de::Error::unknown_variant(value, VARIANTS)) +// +// so it is okay for the return type to be different from the std case as long +// as the above works. +#[cfg(not(any(feature = "std", feature = "alloc")))] +#[doc(hidden)] +pub fn from_utf8_lossy(bytes: &[u8]) -> &str { + // Three unicode replacement characters if it fails. They look like a + // white-on-black question mark. The user will recognize it as invalid + // UTF-8. + str::from_utf8(bytes).unwrap_or("\u{fffd}\u{fffd}\u{fffd}") +} diff --git a/serde/src/ser/fmt.rs b/serde_core/src/ser/fmt.rs similarity index 90% rename from serde/src/ser/fmt.rs rename to serde_core/src/ser/fmt.rs index 0650ab6f..4b1549f0 100644 --- a/serde/src/ser/fmt.rs +++ b/serde_core/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_core/src/ser/impls.rs similarity index 82% rename from serde/src/ser/impls.rs rename to serde_core/src/ser/impls.rs index 8c70634a..a7a175db 100644 --- a/serde/src/ser/impls.rs +++ b/serde_core/src/ser/impls.rs @@ -48,7 +48,7 @@ impl Serialize for str { } #[cfg(any(feature = "std", feature = "alloc"))] -#[cfg_attr(doc_cfg, doc(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 @@ -71,7 +71,7 @@ impl<'a> Serialize for fmt::Arguments<'a> { //////////////////////////////////////////////////////////////////////////////// #[cfg(any(feature = "std", not(no_core_cstr)))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for CStr { #[inline] fn serialize(&self, serializer: S) -> Result @@ -83,7 +83,7 @@ impl Serialize for CStr { } #[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))] -#[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl Serialize for CString { #[inline] fn serialize(&self, serializer: S) -> Result @@ -114,7 +114,10 @@ where //////////////////////////////////////////////////////////////////////////////// -impl Serialize for PhantomData { +impl Serialize for PhantomData +where + T: ?Sized, +{ #[inline] fn serialize(&self, serializer: S) -> Result where @@ -182,11 +185,10 @@ where } } -#[cfg(not(no_relaxed_trait_bounds))] macro_rules! seq_impl { ( $(#[$attr:meta])* - $ty:ident + $ty:ident ) => { $(#[$attr])* impl Serialize for $ty @@ -204,62 +206,39 @@ macro_rules! seq_impl { } } -#[cfg(no_relaxed_trait_bounds)] -macro_rules! seq_impl { - ( - $(#[$attr:meta])* - $ty:ident - ) => { - $(#[$attr])* - impl Serialize for $ty - where - T: Serialize $(+ $tbound1 $(+ $tbound2)*)*, - $($typaram: $bound,)* - { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_seq(self) - } - } - } +seq_impl! { + #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BinaryHeap } seq_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] - BinaryHeap -} - -seq_impl! { - #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] - BTreeSet + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + BTreeSet } seq_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] - HashSet + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + HashSet } seq_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] LinkedList } seq_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] Vec } seq_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] VecDeque } @@ -368,7 +347,7 @@ impl Serialize for () { } #[cfg(feature = "unstable")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "unstable")))] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl Serialize for ! { fn serialize(&self, _serializer: S) -> Result where @@ -383,28 +362,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) @@ -424,7 +421,6 @@ tuple_impls! { //////////////////////////////////////////////////////////////////////////////// -#[cfg(not(no_relaxed_trait_bounds))] macro_rules! map_impl { ( $(#[$attr:meta])* @@ -447,39 +443,15 @@ macro_rules! map_impl { } } -#[cfg(no_relaxed_trait_bounds)] -macro_rules! map_impl { - ( - $(#[$attr:meta])* - $ty:ident - ) => { - $(#[$attr])* - impl Serialize for $ty - where - K: Serialize $(+ $kbound1 $(+ $kbound2)*)*, - V: Serialize, - $($typaram: $bound,)* - { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_map(self) - } - } - } -} - map_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] BTreeMap } map_impl! { #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] HashMap } @@ -504,17 +476,17 @@ macro_rules! deref_impl { } deref_impl! { - <'a, T: ?Sized> Serialize for &'a T where T: Serialize + <'a, T> Serialize for &'a T where T: ?Sized + Serialize } deref_impl! { - <'a, T: ?Sized> Serialize for &'a mut T where T: Serialize + <'a, T> Serialize for &'a mut T where T: ?Sized + Serialize } deref_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] - Serialize for Box where T: Serialize + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + Serialize for Box where T: ?Sized + Serialize } deref_impl! { @@ -527,8 +499,8 @@ deref_impl! { /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] - Serialize for Rc where T: Serialize + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + Serialize for Rc where T: ?Sized + Serialize } deref_impl! { @@ -541,14 +513,14 @@ deref_impl! { /// /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] - Serialize for Arc where T: Serialize + #[cfg_attr(docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))))] + Serialize for Arc where T: ?Sized + Serialize } deref_impl! { #[cfg(any(feature = "std", feature = "alloc"))] - #[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "alloc"))))] - <'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned + #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] + <'a, T> Serialize for Cow<'a, T> where T: ?Sized + Serialize + ToOwned } //////////////////////////////////////////////////////////////////////////////// @@ -558,12 +530,12 @@ deref_impl! { /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) )] -impl Serialize for RcWeak +impl Serialize for RcWeak where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -578,12 +550,12 @@ where /// [`"rc"`]: https://serde.rs/feature-flags.html#-features-rc #[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))) )] -impl Serialize for ArcWeak +impl Serialize for ArcWeak where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -610,16 +582,6 @@ macro_rules! nonzero_integers { } } -nonzero_integers! { - NonZeroU8, - NonZeroU16, - NonZeroU32, - NonZeroU64, - NonZeroU128, - NonZeroUsize, -} - -#[cfg(not(no_num_nonzero_signed))] nonzero_integers! { NonZeroI8, NonZeroI16, @@ -627,6 +589,12 @@ nonzero_integers! { NonZeroI64, NonZeroI128, NonZeroIsize, + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize, } impl Serialize for Cell @@ -641,9 +609,9 @@ where } } -impl Serialize for RefCell +impl Serialize for RefCell where - T: Serialize, + T: ?Sized + Serialize, { fn serialize(&self, serializer: S) -> Result where @@ -657,10 +625,10 @@ where } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(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 @@ -674,10 +642,10 @@ where } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(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 @@ -692,6 +660,8 @@ where //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "result")] +#[cfg_attr(docsrs, doc(cfg(feature = "result")))] impl Serialize for Result where T: Serialize, @@ -728,7 +698,7 @@ impl Serialize for Duration { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for SystemTime { fn serialize(&self, serializer: S) -> Result where @@ -752,28 +722,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_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::IpAddr { fn serialize(&self, serializer: S) -> Result where @@ -797,7 +756,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\ @@ -805,7 +764,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 { @@ -826,7 +785,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; @@ -843,8 +802,7 @@ fn test_format_u8() { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::Ipv4Addr { fn serialize(&self, serializer: S) -> Result where @@ -868,8 +826,7 @@ impl Serialize for net::Ipv4Addr { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::Ipv6Addr { fn serialize(&self, serializer: S) -> Result where @@ -885,8 +842,7 @@ impl Serialize for net::Ipv6Addr { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddr { fn serialize(&self, serializer: S) -> Result where @@ -910,8 +866,7 @@ impl Serialize for net::SocketAddr { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddrV4 { fn serialize(&self, serializer: S) -> Result where @@ -927,8 +882,7 @@ impl Serialize for net::SocketAddrV4 { } } -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg(any(feature = "std", not(no_core_net)))] impl Serialize for net::SocketAddrV6 { fn serialize(&self, serializer: S) -> Result where @@ -950,7 +904,7 @@ impl Serialize for net::SocketAddrV6 { //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for Path { fn serialize(&self, serializer: S) -> Result where @@ -964,7 +918,7 @@ impl Serialize for Path { } #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Serialize for PathBuf { fn serialize(&self, serializer: S) -> Result where @@ -975,7 +929,7 @@ impl Serialize for PathBuf { } #[cfg(all(feature = "std", any(unix, windows)))] -#[cfg_attr(doc_cfg, doc(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 @@ -998,7 +952,7 @@ impl Serialize for OsStr { } #[cfg(all(feature = "std", any(unix, windows)))] -#[cfg_attr(doc_cfg, doc(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 @@ -1023,6 +977,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, @@ -1043,7 +1011,7 @@ macro_rules! atomic_impl { ($($ty:ident $size:expr)*) => { $( #[cfg(any(no_target_has_atomic, target_has_atomic = $size))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", 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_core/src/ser/impossible.rs similarity index 74% rename from serde/src/ser/impossible.rs rename to serde_core/src/ser/impossible.rs index 479be940..fe69ae24 100644 --- a/serde/src/ser/impossible.rs +++ b/serde_core/src/ser/impossible.rs @@ -17,7 +17,7 @@ use crate::ser::{ /// /// ```edition2021 /// # use serde::ser::{Serializer, Impossible}; -/// # use serde::__private::doc::Error; +/// # use serde_core::__private::doc::Error; /// # /// # struct MySerializer; /// # @@ -41,7 +41,7 @@ use crate::ser::{ /// } /// /// /* other Serializer methods */ -/// # serde::__serialize_unimplemented! { +/// # serde_core::__serialize_unimplemented! { /// # bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str bytes none some /// # unit unit_struct unit_variant newtype_struct newtype_variant /// # tuple tuple_struct tuple_variant map struct struct_variant @@ -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_core/src/ser/mod.rs similarity index 91% rename from serde/src/ser/mod.rs rename to serde_core/src/ser/mod.rs index 75c45140..62e59d98 100644 --- a/serde/src/ser/mod.rs +++ b/serde_core/src/ser/mod.rs @@ -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")] @@ -139,6 +139,12 @@ macro_rules! declare_error_trait { /// type appropriate for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html + #[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::Error` is not satisfied", + ) + )] pub trait Error: Sized $(+ $($supertrait)::+)* { /// Used when a [`Serialize`] implementation encounters any error /// while serializing a type. @@ -173,8 +179,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 +221,16 @@ 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( + // Prevents `serde_core::ser::Serialize` appearing in the error message + // in projects with no direct dependency on serde_core. + message = "the trait bound `{Self}: serde::Serialize` is not satisfied", + 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. /// @@ -330,6 +346,12 @@ pub trait Serialize { /// a basic JSON `Serializer`. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::Serializer` is not satisfied", + ) +)] pub trait Serializer: Sized { /// The output type produced by this `Serializer` during successful /// serialization. Most serializers that produce text or binary output @@ -338,7 +360,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. @@ -391,7 +413,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for bool { /// fn serialize(&self, serializer: S) -> Result @@ -413,7 +435,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for i8 { /// fn serialize(&self, serializer: S) -> Result @@ -435,7 +457,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for i16 { /// fn serialize(&self, serializer: S) -> Result @@ -457,7 +479,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for i32 { /// fn serialize(&self, serializer: S) -> Result @@ -475,7 +497,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for i64 { /// fn serialize(&self, serializer: S) -> Result @@ -493,7 +515,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for i128 { /// fn serialize(&self, serializer: S) -> Result @@ -520,7 +542,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for u8 { /// fn serialize(&self, serializer: S) -> Result @@ -542,7 +564,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for u16 { /// fn serialize(&self, serializer: S) -> Result @@ -564,7 +586,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for u32 { /// fn serialize(&self, serializer: S) -> Result @@ -582,7 +604,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for u64 { /// fn serialize(&self, serializer: S) -> Result @@ -600,7 +622,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for u128 { /// fn serialize(&self, serializer: S) -> Result @@ -627,7 +649,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for f32 { /// fn serialize(&self, serializer: S) -> Result @@ -645,7 +667,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for f64 { /// fn serialize(&self, serializer: S) -> Result @@ -666,7 +688,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for char { /// fn serialize(&self, serializer: S) -> Result @@ -684,7 +706,7 @@ pub trait Serializer: Sized { /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for str { /// fn serialize(&self, serializer: S) -> Result @@ -707,7 +729,7 @@ pub trait Serializer: Sized { /// /// ```edition2021 /// # use serde::ser::{Serializer, SerializeSeq}; - /// # use serde::__private::doc::Error; + /// # use serde_core::__private::doc::Error; /// # /// # struct MySerializer; /// # @@ -723,7 +745,7 @@ pub trait Serializer: Sized { /// seq.end() /// } /// # - /// # serde::__serialize_unimplemented! { + /// # serde_core::__serialize_unimplemented! { /// # bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str none some /// # unit unit_struct unit_variant newtype_struct newtype_variant /// # seq tuple tuple_struct tuple_variant map struct struct_variant @@ -762,7 +784,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,17 +817,17 @@ 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. /// /// ```edition2021 /// # use serde::Serializer; /// # - /// # serde::__private_serialize!(); + /// # serde_core::__private_serialize!(); /// # /// impl Serialize for () { /// fn serialize(&self, serializer: S) -> Result @@ -891,13 +913,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 +947,7 @@ pub trait Serializer: Sized { /// } /// } /// ``` - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, @@ -933,7 +955,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 +1192,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 +1230,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 +1368,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 +1403,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. @@ -1485,6 +1509,12 @@ pub trait Serializer: Sized { /// implementation of `SerializeSeq` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeSeq` is not satisfied", + ) +)] pub trait SerializeSeq { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1493,9 +1523,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; @@ -1585,6 +1615,12 @@ pub trait SerializeSeq { /// implementation of `SerializeTuple` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeTuple` is not satisfied", + ) +)] pub trait SerializeTuple { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1593,9 +1629,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; @@ -1630,6 +1666,12 @@ pub trait SerializeTuple { /// implementation of `SerializeTupleStruct` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeTupleStruct` is not satisfied", + ) +)] pub trait SerializeTupleStruct { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1638,9 +1680,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; @@ -1688,6 +1730,12 @@ pub trait SerializeTupleStruct { /// implementation of `SerializeTupleVariant` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeTupleVariant` is not satisfied", + ) +)] pub trait SerializeTupleVariant { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1696,9 +1744,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; @@ -1754,6 +1802,12 @@ pub trait SerializeTupleVariant { /// implementation of `SerializeMap` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeMap` is not satisfied", + ) +)] pub trait SerializeMap { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1767,9 +1821,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 +1831,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 +1849,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) @@ -1848,6 +1898,12 @@ pub trait SerializeMap { /// implementation of `SerializeStruct` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeStruct` is not satisfied", + ) +)] pub trait SerializeStruct { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1856,15 +1912,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; @@ -1914,6 +1968,12 @@ pub trait SerializeStruct { /// implementation of `SerializeStructVariant` for a basic JSON data format. /// /// [example data format]: https://serde.rs/data-format.html +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "the trait bound `{Self}: serde::ser::SerializeStructVariant` is not satisfied", + ) +)] pub trait SerializeStructVariant { /// Must match the `Ok` type of our `Serializer`. type Ok; @@ -1922,15 +1982,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/src/std_error.rs b/serde_core/src/std_error.rs similarity index 95% rename from serde/src/std_error.rs rename to serde_core/src/std_error.rs index f15a4d74..e026ace1 100644 --- a/serde/src/std_error.rs +++ b/serde_core/src/std_error.rs @@ -42,7 +42,7 @@ use crate::lib::{Debug, Display}; /// ``` pub trait Error: Debug + Display { /// The underlying cause of this error, if any. - fn source(&self) -> Option<&(Error + 'static)> { + fn source(&self) -> Option<&(dyn Error + 'static)> { None } } diff --git a/serde_derive/BUILD.gn b/serde_derive/BUILD.gn index 7f650931..3d700105 100644 --- a/serde_derive/BUILD.gn +++ b/serde_derive/BUILD.gn @@ -32,7 +32,7 @@ ohos_cargo_crate("lib") { external_deps += [ "rust_syn:lib" ] } edition = "2018" - cargo_pkg_version = "1.0.195" + cargo_pkg_version = "1.0.228" cargo_pkg_authors = "Erick Tryzelaar , David Tolnay " cargo_pkg_name = "serde" cargo_pkg_description = "A generic serialization/deserialization framework" diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index cd3c4b79..88f8f6a4 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -1,16 +1,18 @@ [package] name = "serde_derive" -version = "1.0.197" +version = "1.0.228" 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 = "2021" +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 = [] @@ -30,4 +32,11 @@ 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", + "--generate-macro-expansion", + "--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 00000000..ebaa409a --- /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 fe8ccfff..2ff6521f 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -227,7 +227,9 @@ pub fn with_bound( 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(_) => {} + syn::TypeParamBound::Lifetime(_) + | syn::TypeParamBound::PreciseCapture(_) + | syn::TypeParamBound::Verbatim(_) => {} _ => {} } } diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index e3b737c6..ca19bb6f 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1,8 +1,10 @@ -use crate::fragment::{Expr, Fragment, Match, Stmts}; +use crate::deprecated::allow_deprecated; +use crate::fragment::{Expr, Fragment, 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}; +use crate::{bound, dummy, pretend, private, this}; +use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use std::collections::BTreeSet; use std::ptr; @@ -10,11 +12,21 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{parse_quote, Ident, Index, Member}; +mod enum_; +mod enum_adjacently; +mod enum_externally; +mod enum_internally; +mod enum_untagged; +mod identifier; +mod struct_; +mod tuple; +mod unit; + pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result { replace_receiver(input); let ctxt = Ctxt::new(); - let cont = match Container::from_ast(&ctxt, input, Derive::Deserialize) { + let cont = match Container::from_ast(&ctxt, input, Derive::Deserialize, &private.ident()) { Some(cont) => cont, None => return Err(ctxt.check().unwrap_err()), }; @@ -23,19 +35,21 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result(__deserializer: __D) -> #serde::__private::Result<#remote #ty_generics, __D::Error> + #vis fn deserialize<__D>(__deserializer: __D) -> _serde::#private::Result<#remote #ty_generics, __D::Error> where - __D: #serde::Deserializer<#delife>, + __D: _serde::Deserializer<#delife>, { #used #body @@ -47,10 +61,11 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result for #ident #ty_generics #where_clause { - fn deserialize<__D>(__deserializer: __D) -> #serde::__private::Result + #allow_deprecated + impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause { + fn deserialize<__D>(__deserializer: __D) -> _serde::#private::Result where - __D: #serde::Deserializer<#delife>, + __D: _serde::Deserializer<#delife>, { #body } @@ -152,6 +167,23 @@ impl Parameters { fn type_name(&self) -> String { self.this_type.segments.last().unwrap().ident.to_string() } + + /// Split the data structure's generics into the pieces to use for its + /// `Deserialize` impl, augmented with an additional `'de` lifetime for use + /// as the `Deserialize` trait's lifetime. + fn generics_with_de_lifetime( + &self, + ) -> ( + DeImplGenerics, + DeTypeGenerics, + syn::TypeGenerics, + Option<&syn::WhereClause>, + ) { + let de_impl_generics = DeImplGenerics(self); + let de_ty_generics = DeTypeGenerics(self); + let (_, ty_generics, where_clause) = self.generics.split_for_impl(); + (de_impl_generics, de_ty_generics, ty_generics, where_clause) + } } // All the generics in the input, plus a bound `T: Deserialize` for each generic @@ -172,7 +204,7 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi attr::Default::Default => bound::with_self_bound( cont, &generics, - &parse_quote!(_serde::__private::Default), + &parse_quote!(_serde::#private::Default), ), attr::Default::None | attr::Default::Path(_) => generics, }; @@ -189,7 +221,7 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi cont, &generics, requires_default, - &parse_quote!(_serde::__private::Default), + &parse_quote!(_serde::#private::Default), ) } } @@ -280,18 +312,18 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { deserialize_try_from(type_try_from) } else if let attr::Identifier::No = cont.attrs.identifier() { match &cont.data { - Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs), + Data::Enum(variants) => enum_::deserialize(params, variants, &cont.attrs), Data::Struct(Style::Struct, fields) => { - deserialize_struct(params, fields, &cont.attrs, StructForm::Struct) + struct_::deserialize(params, fields, &cont.attrs, StructForm::Struct) } Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { - deserialize_tuple(params, fields, &cont.attrs, TupleForm::Tuple) + tuple::deserialize(params, fields, &cont.attrs, TupleForm::Tuple) } - Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs), + Data::Struct(Style::Unit, _) => unit::deserialize(params, &cont.attrs), } } else { match &cont.data { - Data::Enum(variants) => deserialize_custom_identifier(params, variants, &cont.attrs), + Data::Enum(variants) => identifier::deserialize_custom(params, variants, &cont.attrs), Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"), } } @@ -317,10 +349,10 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option { - deserialize_struct_in_place(params, fields, &cont.attrs)? + struct_::deserialize_in_place(params, fields, &cont.attrs)? } Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { - deserialize_tuple_in_place(params, fields, &cont.attrs) + tuple::deserialize_in_place(params, fields, &cont.attrs) } Data::Enum(_) | Data::Struct(Style::Unit, _) => { return None; @@ -331,7 +363,7 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option(__deserializer: __D, __place: &mut Self) -> _serde::__private::Result<(), __D::Error> + fn deserialize_in_place<__D>(__deserializer: __D, __place: &mut Self) -> _serde::#private::Result<(), __D::Error> where __D: _serde::Deserializer<#delife>, { @@ -347,6 +379,7 @@ fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option< None } +/// Generates `Deserialize::deserialize` body for a type with `#[serde(transparent)]` attribute fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment { let fields = match &cont.data { Data::Struct(_, fields) => fields, @@ -370,79 +403,40 @@ fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment { quote!(#member: __transparent) } else { let value = match field.attrs.default() { - attr::Default::Default => quote!(_serde::__private::Default::default()), - attr::Default::Path(path) => quote!(#path()), - attr::Default::None => quote!(_serde::__private::PhantomData), + attr::Default::Default => quote!(_serde::#private::Default::default()), + // 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) } }); quote_block! { - _serde::__private::Result::map( + _serde::#private::Result::map( #path(__deserializer), |__transparent| #this_value { #(#assign),* }) } } +/// Generates `Deserialize::deserialize` body for a type with `#[serde(from)]` attribute fn deserialize_from(type_from: &syn::Type) -> Fragment { quote_block! { - _serde::__private::Result::map( + _serde::#private::Result::map( <#type_from as _serde::Deserialize>::deserialize(__deserializer), - _serde::__private::From::from) + _serde::#private::From::from) } } +/// Generates `Deserialize::deserialize` body for a type with `#[serde(try_from)]` attribute fn deserialize_try_from(type_try_from: &syn::Type) -> Fragment { quote_block! { - _serde::__private::Result::and_then( + _serde::#private::Result::and_then( <#type_try_from as _serde::Deserialize>::deserialize(__deserializer), - |v| _serde::__private::TryFrom::try_from(v).map_err(_serde::de::Error::custom)) - } -} - -fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fragment { - let this_type = ¶ms.this_type; - let this_value = ¶ms.this_value; - let type_name = cattrs.name().deserialize_name(); - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let expecting = format!("unit struct {}", params.type_name()); - let expecting = cattrs.expecting().unwrap_or(&expecting); - - quote_block! { - #[doc(hidden)] - struct __Visitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #[inline] - fn visit_unit<__E>(self) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(#this_value) - } - } - - _serde::Deserializer::deserialize_unit_struct( - __deserializer, - #type_name, - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - }, - ) + |v| _serde::#private::TryFrom::try_from(v).map_err(_serde::de::Error::custom)) } } @@ -450,223 +444,8 @@ enum TupleForm<'a> { Tuple, /// Contains a variant name ExternallyTagged(&'a syn::Ident), - /// Contains a variant name and an intermediate deserializer from which actual - /// deserialization will be performed - Untagged(&'a syn::Ident, TokenStream), -} - -fn deserialize_tuple( - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, - form: TupleForm, -) -> Fragment { - assert!(!cattrs.has_flatten()); - - let field_count = fields - .iter() - .filter(|field| !field.attrs.skip_deserializing()) - .count(); - - let this_type = ¶ms.this_type; - let this_value = ¶ms.this_value; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - // If there are getters (implying private fields), construct the local type - // and use an `Into` conversion to get the remote type. If there are no - // getters then construct the target type directly. - let construct = if params.has_getter { - let local = ¶ms.local; - quote!(#local) - } else { - quote!(#this_value) - }; - - let type_path = match form { - TupleForm::Tuple => construct, - TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident, _) => { - quote!(#construct::#variant_ident) - } - }; - let expecting = match form { - TupleForm::Tuple => format!("tuple struct {}", params.type_name()), - TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident, _) => { - format!("tuple variant {}::{}", params.type_name(), variant_ident) - } - }; - let expecting = cattrs.expecting().unwrap_or(&expecting); - - let nfields = fields.len(); - - let visit_newtype_struct = match form { - TupleForm::Tuple if nfields == 1 => { - Some(deserialize_newtype_struct(&type_path, params, &fields[0])) - } - _ => None, - }; - - let visit_seq = Stmts(deserialize_seq( - &type_path, params, fields, false, cattrs, expecting, - )); - - let visitor_expr = quote! { - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - } - }; - let dispatch = match form { - TupleForm::Tuple if nfields == 1 => { - let type_name = cattrs.name().deserialize_name(); - quote! { - _serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr) - } - } - TupleForm::Tuple => { - let type_name = cattrs.name().deserialize_name(); - quote! { - _serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr) - } - } - TupleForm::ExternallyTagged(_) => quote! { - _serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr) - }, - TupleForm::Untagged(_, deserializer) => quote! { - _serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr) - }, - }; - - let visitor_var = if field_count == 0 { - quote!(_) - } else { - quote!(mut __seq) - }; - - quote_block! { - #[doc(hidden)] - struct __Visitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #visit_newtype_struct - - #[inline] - fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - } - - #dispatch - } -} - -#[cfg(feature = "deserialize_in_place")] -fn deserialize_tuple_in_place( - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, -) -> Fragment { - assert!(!cattrs.has_flatten()); - - let field_count = fields - .iter() - .filter(|field| !field.attrs.skip_deserializing()) - .count(); - - let this_type = ¶ms.this_type; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let expecting = format!("tuple struct {}", params.type_name()); - let expecting = cattrs.expecting().unwrap_or(&expecting); - - let nfields = fields.len(); - - let visit_newtype_struct = if nfields == 1 { - // We do not generate deserialize_in_place if every field has a - // deserialize_with. - assert!(fields[0].attrs.deserialize_with().is_none()); - - Some(quote! { - #[inline] - fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::__private::Result - where - __E: _serde::Deserializer<#delife>, - { - _serde::Deserialize::deserialize_in_place(__e, &mut self.place.0) - } - }) - } else { - None - }; - - let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting)); - - let visitor_expr = quote! { - __Visitor { - place: __place, - lifetime: _serde::__private::PhantomData, - } - }; - - let type_name = cattrs.name().deserialize_name(); - let dispatch = if nfields == 1 { - quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr)) - } else { - quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr)) - }; - - let visitor_var = if field_count == 0 { - quote!(_) - } else { - quote!(mut __seq) - }; - - let in_place_impl_generics = de_impl_generics.in_place(); - let in_place_ty_generics = de_ty_generics.in_place(); - let place_life = place_lifetime(); - - quote_block! { - #[doc(hidden)] - struct __Visitor #in_place_impl_generics #where_clause { - place: &#place_life mut #this_type #ty_generics, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause { - type Value = (); - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #visit_newtype_struct - - #[inline] - fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - } - - #dispatch - } + /// Contains a variant name + Untagged(&'a syn::Ident), } fn deserialize_seq( @@ -710,7 +489,7 @@ fn deserialize_seq( let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); quote!({ #wrapper - _serde::__private::Option::map( + _serde::#private::Option::map( _serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)?, |__wrap| __wrap.value) }) @@ -719,8 +498,8 @@ fn deserialize_seq( let value_if_none = expr_is_missing_seq(None, index_in_seq, field, cattrs, expecting); let assign = quote! { let #var = match #visit { - _serde::__private::Some(__value) => __value, - _serde::__private::None => #value_if_none, + _serde::#private::Some(__value) => __value, + _serde::#private::None => #value_if_none, }; }; index_in_seq += 1; @@ -743,15 +522,19 @@ fn deserialize_seq( let this_type = ¶ms.this_type; let (_, ty_generics, _) = params.generics.split_for_impl(); result = quote! { - _serde::__private::Into::<#this_type #ty_generics>::into(#result) + _serde::#private::Into::<#this_type #ty_generics>::into(#result) }; } let let_default = match cattrs.default() { attr::Default::Default => Some(quote!( - let __default: Self::Value = _serde::__private::Default::default(); + 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 => { @@ -764,7 +547,7 @@ fn deserialize_seq( quote_block! { #let_default #(#let_values)* - _serde::__private::Ok(#result) + _serde::#private::Ok(#result) } } @@ -800,8 +583,8 @@ fn deserialize_seq_in_place( let write = match field.attrs.deserialize_with() { None => { quote! { - if let _serde::__private::None = _serde::de::SeqAccess::next_element_seed(&mut __seq, - _serde::__private::de::InPlaceSeed(&mut self.place.#member))? + if let _serde::#private::None = _serde::de::SeqAccess::next_element_seed(&mut __seq, + _serde::#private::de::InPlaceSeed(&mut self.place.#member))? { #value_if_none; } @@ -812,10 +595,10 @@ fn deserialize_seq_in_place( quote!({ #wrapper match _serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)? { - _serde::__private::Some(__wrap) => { + _serde::#private::Some(__wrap) => { self.place.#member = __wrap.value; } - _serde::__private::None => { + _serde::#private::None => { #value_if_none; } } @@ -831,9 +614,13 @@ fn deserialize_seq_in_place( let (_, ty_generics, _) = params.generics.split_for_impl(); let let_default = match cattrs.default() { attr::Default::Default => Some(quote!( - let __default: #this_type #ty_generics = _serde::__private::Default::default(); + 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 => { @@ -846,51 +633,7 @@ fn deserialize_seq_in_place( quote_block! { #let_default #(#write_values)* - _serde::__private::Ok(()) - } -} - -fn deserialize_newtype_struct( - type_path: &TokenStream, - params: &Parameters, - field: &Field, -) -> TokenStream { - let delife = params.borrowed.de_lifetime(); - let field_ty = field.ty; - - 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)? - } - } - Some(path) => { - quote! { - #path(__e)? - } - } - }; - - let mut result = quote!(#type_path(__field0)); - if params.has_getter { - let this_type = ¶ms.this_type; - let (_, ty_generics, _) = params.generics.split_for_impl(); - result = quote! { - _serde::__private::Into::<#this_type #ty_generics>::into(#result) - }; - } - - quote! { - #[inline] - fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::__private::Result - where - __E: _serde::Deserializer<#delife>, - { - let __field0: #field_ty = #value; - _serde::__private::Ok(#result) - } + _serde::#private::Ok(()) } } @@ -898,1930 +641,15 @@ enum StructForm<'a> { Struct, /// Contains a variant name ExternallyTagged(&'a syn::Ident), - /// Contains a variant name and an intermediate deserializer from which actual - /// deserialization will be performed - InternallyTagged(&'a syn::Ident, TokenStream), - /// Contains a variant name and an intermediate deserializer from which actual - /// deserialization will be performed - Untagged(&'a syn::Ident, TokenStream), + /// Contains a variant name + InternallyTagged(&'a syn::Ident), + /// Contains a variant name + Untagged(&'a syn::Ident), } -fn deserialize_struct( - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, - form: StructForm, -) -> Fragment { - let this_type = ¶ms.this_type; - let this_value = ¶ms.this_value; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - // If there are getters (implying private fields), construct the local type - // and use an `Into` conversion to get the remote type. If there are no - // getters then construct the target type directly. - let construct = if params.has_getter { - let local = ¶ms.local; - quote!(#local) - } else { - quote!(#this_value) - }; - - let type_path = match form { - StructForm::Struct => construct, - StructForm::ExternallyTagged(variant_ident) - | StructForm::InternallyTagged(variant_ident, _) - | StructForm::Untagged(variant_ident, _) => quote!(#construct::#variant_ident), - }; - let expecting = match form { - StructForm::Struct => format!("struct {}", params.type_name()), - StructForm::ExternallyTagged(variant_ident) - | StructForm::InternallyTagged(variant_ident, _) - | StructForm::Untagged(variant_ident, _) => { - format!("struct variant {}::{}", params.type_name(), variant_ident) - } - }; - let expecting = cattrs.expecting().unwrap_or(&expecting); - - let field_names_idents: 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(), - ) - }) - .collect(); - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs); - - // 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, - _ => { - let mut_seq = if field_names_idents.is_empty() { - quote!(_) - } else { - quote!(mut __seq) - }; - - let visit_seq = Stmts(deserialize_seq( - &type_path, params, fields, true, cattrs, expecting, - )); - - Some(quote! { - #[inline] - fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - }) - } - }; - let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs)); - - let visitor_seed = match form { - StructForm::ExternallyTagged(..) if cattrs.has_flatten() => Some(quote! { - impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result - where - __D: _serde::Deserializer<#delife>, - { - _serde::Deserializer::deserialize_map(__deserializer, self) - } - } - }), - _ => None, - }; - - let fields_stmt = if cattrs.has_flatten() { - None - } else { - let field_names = field_names_idents - .iter() - .flat_map(|&(_, _, aliases)| aliases); - - Some(quote! { - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; - }) - }; - - let visitor_expr = quote! { - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - } - }; - let dispatch = match form { - StructForm::Struct if cattrs.has_flatten() => quote! { - _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) - }, - StructForm::Struct => { - let type_name = cattrs.name().deserialize_name(); - quote! { - _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) - } - } - StructForm::ExternallyTagged(_) if cattrs.has_flatten() => quote! { - _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) - }, - StructForm::ExternallyTagged(_) => quote! { - _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) - }, - StructForm::InternallyTagged(_, deserializer) => quote! { - _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) - }, - StructForm::Untagged(_, deserializer) => quote! { - _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) - }, - }; - - quote_block! { - #field_visitor - - #[doc(hidden)] - struct __Visitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #visit_seq - - #[inline] - fn visit_map<__A>(self, mut __map: __A) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<#delife>, - { - #visit_map - } - } - - #visitor_seed - - #fields_stmt - - #dispatch - } -} - -#[cfg(feature = "deserialize_in_place")] -fn deserialize_struct_in_place( - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, -) -> Option { - // for now we do not support in_place deserialization for structs that - // are represented as map. - if cattrs.has_flatten() { - return None; - } - - let this_type = ¶ms.this_type; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let expecting = format!("struct {}", params.type_name()); - let expecting = cattrs.expecting().unwrap_or(&expecting); - - let field_names_idents: Vec<_> = fields - .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) - .collect(); - - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs); - - let mut_seq = if field_names_idents.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 type_name = cattrs.name().deserialize_name(); - - let in_place_impl_generics = de_impl_generics.in_place(); - let in_place_ty_generics = de_ty_generics.in_place(); - let place_life = place_lifetime(); - - Some(quote_block! { - #field_visitor - - #[doc(hidden)] - struct __Visitor #in_place_impl_generics #where_clause { - place: &#place_life mut #this_type #ty_generics, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause { - type Value = (); - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #[inline] - fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - - #[inline] - fn visit_map<__A>(self, mut __map: __A) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<#delife>, - { - #visit_map - } - } - - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; - - _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, __Visitor { - place: __place, - lifetime: _serde::__private::PhantomData, - }) - }) -} - -fn deserialize_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, -) -> Fragment { - // The variants have already been checked (in ast.rs) that all untagged variants appear at the end - match variants.iter().position(|var| var.attrs.untagged()) { - Some(variant_idx) => { - let (tagged, untagged) = variants.split_at(variant_idx); - let tagged_frag = Expr(deserialize_homogeneous_enum(params, tagged, cattrs)); - deserialize_untagged_enum_after(params, untagged, cattrs, Some(tagged_frag)) - } - None => deserialize_homogeneous_enum(params, variants, cattrs), - } -} - -fn deserialize_homogeneous_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, -) -> Fragment { - match cattrs.tag() { - attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs), - attr::TagType::Internal { tag } => { - deserialize_internally_tagged_enum(params, variants, cattrs, tag) - } - attr::TagType::Adjacent { tag, content } => { - deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content) - } - attr::TagType::None => deserialize_untagged_enum(params, variants, cattrs), - } -} - -fn prepare_enum_variant_enum( - variants: &[Variant], - cattrs: &attr::Container, -) -> (TokenStream, Stmts) { - let mut 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(); - - let fallthrough = deserialized_variants - .position(|(_, variant)| variant.attrs.other()) - .map(|other_idx| { - let ignore_variant = variant_names_idents[other_idx].1.clone(); - quote!(_serde::__private::Ok(__Field::#ignore_variant)) - }); - - let variants_stmt = { - let variant_names = variant_names_idents.iter().map(|(name, _, _)| name); - quote! { - #[doc(hidden)] - const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; - } - }; - - let variant_visitor = Stmts(deserialize_generated_identifier( - &variant_names_idents, - cattrs, - true, - None, - fallthrough, - )); - - (variants_stmt, variant_visitor) -} - -fn deserialize_externally_tagged_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, -) -> Fragment { - let this_type = ¶ms.this_type; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let type_name = cattrs.name().deserialize_name(); - 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); - - // Match arms to extract a variant from a string - let variant_arms = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| { - let variant_name = field_i(i); - - let block = Match(deserialize_externally_tagged_variant( - params, variant, cattrs, - )); - - quote! { - (__Field::#variant_name, __variant) => #block - } - }); - - let all_skipped = variants - .iter() - .all(|variant| variant.attrs.skip_deserializing()); - let match_variant = if all_skipped { - // This is an empty enum like `enum Impossible {}` or an enum in which - // all variants have `#[serde(skip_deserializing)]`. - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data); - // _serde::__private::Err(__err) - _serde::__private::Result::map( - _serde::de::EnumAccess::variant::<__Field>(__data), - |(__impossible, _)| match __impossible {}) - } - } else { - quote! { - match _serde::de::EnumAccess::variant(__data)? { - #(#variant_arms)* - } - } - }; - - quote_block! { - #variant_visitor - - #[doc(hidden)] - struct __Visitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - fn visit_enum<__A>(self, __data: __A) -> _serde::__private::Result - where - __A: _serde::de::EnumAccess<#delife>, - { - #match_variant - } - } - - #variants_stmt - - _serde::Deserializer::deserialize_enum( - __deserializer, - #type_name, - VARIANTS, - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - }, - ) - } -} - -fn deserialize_internally_tagged_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, - tag: &str, -) -> Fragment { - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); - - // Match arms to extract a variant from a string - let variant_arms = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| { - let variant_name = field_i(i); - - let block = Match(deserialize_internally_tagged_variant( - params, - variant, - cattrs, - quote!(__deserializer), - )); - - quote! { - __Field::#variant_name => #block - } - }); - - let expecting = format!("internally tagged enum {}", params.type_name()); - let expecting = cattrs.expecting().unwrap_or(&expecting); - - quote_block! { - #variant_visitor - - #variants_stmt - - let (__tag, __content) = _serde::Deserializer::deserialize_any( - __deserializer, - _serde::__private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting))?; - let __deserializer = _serde::__private::de::ContentDeserializer::<__D::Error>::new(__content); - - match __tag { - #(#variant_arms)* - } - } -} - -fn deserialize_adjacently_tagged_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, - tag: &str, - content: &str, -) -> Fragment { - let this_type = ¶ms.this_type; - let this_value = ¶ms.this_value; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); - - let variant_arms: &Vec<_> = &variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| { - let variant_index = field_i(i); - - let block = Match(deserialize_untagged_variant( - params, - variant, - cattrs, - quote!(__deserializer), - )); - - quote! { - __Field::#variant_index => #block - } - }) - .collect(); - - let rust_name = params.type_name(); - let expecting = format!("adjacently tagged enum {}", rust_name); - let expecting = cattrs.expecting().unwrap_or(&expecting); - let type_name = cattrs.name().deserialize_name(); - let deny_unknown_fields = cattrs.deny_unknown_fields(); - - // If unknown fields are allowed, we pick the visitor that can step over - // those. Otherwise we pick the visitor that fails on unknown keys. - let field_visitor_ty = if deny_unknown_fields { - quote! { _serde::__private::de::TagOrContentFieldVisitor } - } else { - quote! { _serde::__private::de::TagContentOtherFieldVisitor } - }; - - let tag_or_content = quote! { - #field_visitor_ty { - tag: #tag, - content: #content, - } - }; - - let variant_seed = quote! { - _serde::__private::de::AdjacentlyTaggedEnumVariantSeed::<__Field> { - enum_name: #rust_name, - variants: VARIANTS, - fields_enum: _serde::__private::PhantomData - } - }; - - let mut missing_content = quote! { - _serde::__private::Err(<__A::Error as _serde::de::Error>::missing_field(#content)) - }; - let mut missing_content_fallthrough = quote!(); - let missing_content_arms = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .filter_map(|(i, variant)| { - let variant_index = field_i(i); - let variant_ident = &variant.ident; - - let arm = match variant.style { - Style::Unit => quote! { - _serde::__private::Ok(#this_value::#variant_ident) - }, - Style::Newtype if variant.attrs.deserialize_with().is_none() => { - let span = variant.original.span(); - let func = quote_spanned!(span=> _serde::__private::de::missing_field); - quote! { - #func(#content).map(#this_value::#variant_ident) - } - } - _ => { - missing_content_fallthrough = quote!(_ => #missing_content); - return None; - } - }; - Some(quote! { - __Field::#variant_index => #arm, - }) - }) - .collect::>(); - if !missing_content_arms.is_empty() { - missing_content = quote! { - match __field { - #(#missing_content_arms)* - #missing_content_fallthrough - } - }; - } - - // Advance the map by one key, returning early in case of error. - let next_key = quote! { - _serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content)? - }; - - let variant_from_map = quote! { - _serde::de::MapAccess::next_value_seed(&mut __map, #variant_seed)? - }; - - // When allowing unknown fields, we want to transparently step through keys - // we don't care about until we find `tag`, `content`, or run out of keys. - let next_relevant_key = if deny_unknown_fields { - next_key - } else { - quote!({ - let mut __rk : _serde::__private::Option<_serde::__private::de::TagOrContentField> = _serde::__private::None; - while let _serde::__private::Some(__k) = #next_key { - match __k { - _serde::__private::de::TagContentOtherField::Other => { - let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; - continue; - }, - _serde::__private::de::TagContentOtherField::Tag => { - __rk = _serde::__private::Some(_serde::__private::de::TagOrContentField::Tag); - break; - } - _serde::__private::de::TagContentOtherField::Content => { - __rk = _serde::__private::Some(_serde::__private::de::TagOrContentField::Content); - break; - } - } - } - - __rk - }) - }; - - // Step through remaining keys, looking for duplicates of previously-seen - // keys. When unknown fields are denied, any key that isn't a duplicate will - // at this point immediately produce an error. - let visit_remaining_keys = quote! { - match #next_relevant_key { - _serde::__private::Some(_serde::__private::de::TagOrContentField::Tag) => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag)) - } - _serde::__private::Some(_serde::__private::de::TagOrContentField::Content) => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content)) - } - _serde::__private::None => _serde::__private::Ok(__ret), - } - }; - - let finish_content_then_tag = if variant_arms.is_empty() { - quote! { - match #variant_from_map {} - } - } else { - quote! { - let __ret = match #variant_from_map { - // Deserialize the buffered content now that we know the variant. - #(#variant_arms)* - }?; - // Visit remaining keys, looking for duplicates. - #visit_remaining_keys - } - }; - - quote_block! { - #variant_visitor - - #variants_stmt - - #[doc(hidden)] - struct __Seed #de_impl_generics #where_clause { - field: __Field, - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Seed #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result - where - __D: _serde::Deserializer<#delife>, - { - match self.field { - #(#variant_arms)* - } - } - } - - #[doc(hidden)] - struct __Visitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - fn visit_map<__A>(self, mut __map: __A) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<#delife>, - { - // Visit the first relevant key. - match #next_relevant_key { - // First key is the tag. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Tag) => { - // Parse the tag. - let __field = #variant_from_map; - // Visit the second key. - match #next_relevant_key { - // Second key is a duplicate of the tag. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Tag) => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag)) - } - // Second key is the content. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Content) => { - let __ret = _serde::de::MapAccess::next_value_seed(&mut __map, - __Seed { - field: __field, - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData, - })?; - // Visit remaining keys, looking for duplicates. - #visit_remaining_keys - } - // There is no second key; might be okay if the we have a unit variant. - _serde::__private::None => #missing_content - } - } - // First key is the content. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Content) => { - // Buffer up the content. - let __content = _serde::de::MapAccess::next_value::<_serde::__private::de::Content>(&mut __map)?; - // Visit the second key. - match #next_relevant_key { - // Second key is the tag. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Tag) => { - let __deserializer = _serde::__private::de::ContentDeserializer::<__A::Error>::new(__content); - #finish_content_then_tag - } - // Second key is a duplicate of the content. - _serde::__private::Some(_serde::__private::de::TagOrContentField::Content) => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content)) - } - // There is no second key. - _serde::__private::None => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::missing_field(#tag)) - } - } - } - // There is no first key. - _serde::__private::None => { - _serde::__private::Err(<__A::Error as _serde::de::Error>::missing_field(#tag)) - } - } - } - - fn visit_seq<__A>(self, mut __seq: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - // Visit the first element - the tag. - match _serde::de::SeqAccess::next_element(&mut __seq)? { - _serde::__private::Some(__field) => { - // Visit the second element - the content. - match _serde::de::SeqAccess::next_element_seed( - &mut __seq, - __Seed { - field: __field, - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData, - }, - )? { - _serde::__private::Some(__ret) => _serde::__private::Ok(__ret), - // There is no second element. - _serde::__private::None => { - _serde::__private::Err(_serde::de::Error::invalid_length(1, &self)) - } - } - } - // There is no first element. - _serde::__private::None => { - _serde::__private::Err(_serde::de::Error::invalid_length(0, &self)) - } - } - } - } - - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[#tag, #content]; - _serde::Deserializer::deserialize_struct( - __deserializer, - #type_name, - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - }, - ) - } -} - -fn deserialize_untagged_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, -) -> Fragment { - let first_attempt = None; - deserialize_untagged_enum_after(params, variants, cattrs, first_attempt) -} - -fn deserialize_untagged_enum_after( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, - first_attempt: Option, -) -> Fragment { - let attempts = variants - .iter() - .filter(|variant| !variant.attrs.skip_deserializing()) - .map(|variant| { - Expr(deserialize_untagged_variant( - params, - variant, - cattrs, - quote!(__deserializer), - )) - }); - // 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 - // largest number of fields. I'm not sure I like that. Maybe it would be - // better to save all the errors and combine them into one message that - // explains why none of the variants matched. - let fallthrough_msg = format!( - "data did not match any variant of untagged enum {}", - params.type_name() - ); - 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); - } - )* - - _serde::__private::Err(_serde::de::Error::custom(#fallthrough_msg)) - } -} - -fn deserialize_externally_tagged_variant( - params: &Parameters, - variant: &Variant, - cattrs: &attr::Container, -) -> Fragment { - if let Some(path) = variant.attrs.deserialize_with() { - let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path); - return quote_block! { - #wrapper - _serde::__private::Result::map( - _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), #unwrap_fn) - }; - } - - let variant_ident = &variant.ident; - - match variant.style { - Style::Unit => { - let this_value = ¶ms.this_value; - quote_block! { - _serde::de::VariantAccess::unit_variant(__variant)?; - _serde::__private::Ok(#this_value::#variant_ident) - } - } - Style::Newtype => deserialize_externally_tagged_newtype_variant( - variant_ident, - params, - &variant.fields[0], - cattrs, - ), - Style::Tuple => deserialize_tuple( - params, - &variant.fields, - cattrs, - TupleForm::ExternallyTagged(variant_ident), - ), - Style::Struct => deserialize_struct( - params, - &variant.fields, - cattrs, - StructForm::ExternallyTagged(variant_ident), - ), - } -} - -// Generates significant part of the visit_seq and visit_map bodies of visitors -// for the variants of internally tagged enum. -fn deserialize_internally_tagged_variant( - params: &Parameters, - variant: &Variant, - cattrs: &attr::Container, - deserializer: TokenStream, -) -> Fragment { - if variant.attrs.deserialize_with().is_some() { - return deserialize_untagged_variant(params, variant, cattrs, deserializer); - } - - let variant_ident = &variant.ident; - - match effective_style(variant) { - Style::Unit => { - let this_value = ¶ms.this_value; - let type_name = params.type_name(); - let variant_name = variant.ident.to_string(); - let default = variant.fields.first().map(|field| { - let default = Expr(expr_is_missing(field, cattrs)); - quote!((#default)) - }); - quote_block! { - _serde::Deserializer::deserialize_any(#deserializer, _serde::__private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name))?; - _serde::__private::Ok(#this_value::#variant_ident #default) - } - } - Style::Newtype => deserialize_untagged_newtype_variant( - variant_ident, - params, - &variant.fields[0], - &deserializer, - ), - Style::Struct => deserialize_struct( - params, - &variant.fields, - cattrs, - StructForm::InternallyTagged(variant_ident, deserializer), - ), - Style::Tuple => unreachable!("checked in serde_derive_internals"), - } -} - -fn deserialize_untagged_variant( - params: &Parameters, - variant: &Variant, - cattrs: &attr::Container, - deserializer: TokenStream, -) -> Fragment { - if let Some(path) = variant.attrs.deserialize_with() { - let unwrap_fn = unwrap_to_variant_closure(params, variant, false); - return quote_block! { - _serde::__private::Result::map(#path(#deserializer), #unwrap_fn) - }; - } - - let variant_ident = &variant.ident; - - match effective_style(variant) { - Style::Unit => { - let this_value = ¶ms.this_value; - let type_name = params.type_name(); - let variant_name = variant.ident.to_string(); - let default = variant.fields.first().map(|field| { - let default = Expr(expr_is_missing(field, cattrs)); - quote!((#default)) - }); - quote_expr! { - match _serde::Deserializer::deserialize_any( - #deserializer, - _serde::__private::de::UntaggedUnitVisitor::new(#type_name, #variant_name) - ) { - _serde::__private::Ok(()) => _serde::__private::Ok(#this_value::#variant_ident #default), - _serde::__private::Err(__err) => _serde::__private::Err(__err), - } - } - } - Style::Newtype => deserialize_untagged_newtype_variant( - variant_ident, - params, - &variant.fields[0], - &deserializer, - ), - Style::Tuple => deserialize_tuple( - params, - &variant.fields, - cattrs, - TupleForm::Untagged(variant_ident, deserializer), - ), - Style::Struct => deserialize_struct( - params, - &variant.fields, - cattrs, - StructForm::Untagged(variant_ident, deserializer), - ), - } -} - -fn deserialize_externally_tagged_newtype_variant( - variant_ident: &syn::Ident, - params: &Parameters, - field: &Field, - cattrs: &attr::Container, -) -> Fragment { - let this_value = ¶ms.this_value; - - if field.attrs.skip_deserializing() { - let default = Expr(expr_is_missing(field, cattrs)); - return quote_block! { - _serde::de::VariantAccess::unit_variant(__variant)?; - _serde::__private::Ok(#this_value::#variant_ident(#default)) - }; - } - - match field.attrs.deserialize_with() { - None => { - let field_ty = field.ty; - let span = field.original.span(); - let func = - quote_spanned!(span=> _serde::de::VariantAccess::newtype_variant::<#field_ty>); - quote_expr! { - _serde::__private::Result::map(#func(__variant), #this_value::#variant_ident) - } - } - Some(path) => { - let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); - quote_block! { - #wrapper - _serde::__private::Result::map( - _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), - |__wrapper| #this_value::#variant_ident(__wrapper.value)) - } - } - } -} - -fn deserialize_untagged_newtype_variant( - variant_ident: &syn::Ident, - params: &Parameters, - field: &Field, - deserializer: &TokenStream, -) -> Fragment { - let this_value = ¶ms.this_value; - let field_ty = field.ty; - match field.attrs.deserialize_with() { - None => { - let span = field.original.span(); - let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize); - quote_expr! { - _serde::__private::Result::map(#func(#deserializer), #this_value::#variant_ident) - } - } - Some(path) => { - quote_block! { - let __value: _serde::__private::Result<#field_ty, _> = #path(#deserializer); - _serde::__private::Result::map(__value, #this_value::#variant_ident) - } - } - } -} - -fn deserialize_generated_identifier( - fields: &[(&str, Ident, &BTreeSet)], - cattrs: &attr::Container, - 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 visitor_impl = Stmts(deserialize_identifier( - &this_value, - fields, - is_variant, - fallthrough, - None, - !is_variant && cattrs.has_flatten(), - None, - )); - - let lifetime = if !is_variant && cattrs.has_flatten() { - Some(quote!(<'de>)) - } else { - None - }; - - quote_block! { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field #lifetime { - #(#field_idents,)* - #ignore_variant - } - - #[doc(hidden)] - struct __FieldVisitor; - - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field #lifetime; - - #visitor_impl - } - - impl<'de> _serde::Deserialize<'de> for __Field #lifetime { - #[inline] - fn deserialize<__D>(__deserializer: __D) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor) - } - } - } -} - -/// Generates enum and its `Deserialize` implementation that represents each -/// non-skipped field of the struct -fn deserialize_field_identifier( - fields: &[(&str, Ident, &BTreeSet)], - cattrs: &attr::Container, -) -> Stmts { - let (ignore_variant, fallthrough) = if cattrs.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)) - } else if cattrs.deny_unknown_fields() { - (None, None) - } else { - let ignore_variant = quote!(__ignore,); - let fallthrough = quote!(_serde::__private::Ok(__Field::__ignore)); - (Some(ignore_variant), Some(fallthrough)) - }; - - Stmts(deserialize_generated_identifier( - fields, - cattrs, - false, - ignore_variant, - fallthrough, - )) -} - -// Generates `Deserialize::deserialize` body for an enum with -// `serde(field_identifier)` or `serde(variant_identifier)` attribute. -fn deserialize_custom_identifier( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, -) -> Fragment { - let is_variant = match cattrs.identifier() { - attr::Identifier::Variant => true, - attr::Identifier::Field => false, - attr::Identifier::No => unreachable!(), - }; - - let this_type = params.this_type.to_token_stream(); - let this_value = params.this_value.to_token_stream(); - - let (ordinary, fallthrough, fallthrough_borrowed) = if let Some(last) = variants.last() { - let last_ident = &last.ident; - if last.attrs.other() { - // Process `serde(other)` attribute. It would always be found on the - // last variant (checked in `check_identifier`), so all preceding - // are ordinary variants. - let ordinary = &variants[..variants.len() - 1]; - let fallthrough = quote!(_serde::__private::Ok(#this_value::#last_ident)); - (ordinary, Some(fallthrough), None) - } else if let Style::Newtype = last.style { - let ordinary = &variants[..variants.len() - 1]; - let fallthrough = |value| { - quote! { - _serde::__private::Result::map( - _serde::Deserialize::deserialize( - _serde::__private::de::IdentifierDeserializer::from(#value) - ), - #this_value::#last_ident) - } - }; - ( - ordinary, - Some(fallthrough(quote!(__value))), - Some(fallthrough(quote!(_serde::__private::de::Borrowed( - __value - )))), - ) - } else { - (variants, None, None) - } - } else { - (variants, None, None) - }; - - let names_idents: Vec<_> = ordinary - .iter() - .map(|variant| { - ( - variant.attrs.name().deserialize_name(), - variant.ident.clone(), - variant.attrs.aliases(), - ) - }) - .collect(); - - let names = names_idents.iter().flat_map(|&(_, _, aliases)| aliases); - - let names_const = if fallthrough.is_some() { - None - } else if is_variant { - let variants = quote! { - #[doc(hidden)] - const VARIANTS: &'static [&'static str] = &[ #(#names),* ]; - }; - Some(variants) - } else { - let fields = quote! { - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ #(#names),* ]; - }; - Some(fields) - }; - - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - let visitor_impl = Stmts(deserialize_identifier( - &this_value, - &names_idents, - is_variant, - fallthrough, - fallthrough_borrowed, - false, - cattrs.expecting(), - )); - - quote_block! { - #names_const - - #[doc(hidden)] - struct __FieldVisitor #de_impl_generics #where_clause { - marker: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, - } - - impl #de_impl_generics _serde::de::Visitor<#delife> for __FieldVisitor #de_ty_generics #where_clause { - type Value = #this_type #ty_generics; - - #visitor_impl - } - - let __visitor = __FieldVisitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - }; - _serde::Deserializer::deserialize_identifier(__deserializer, __visitor) - } -} - -fn deserialize_identifier( - this_value: &TokenStream, - fields: &[(&str, Ident, &BTreeSet)], - is_variant: bool, - fallthrough: Option, - fallthrough_borrowed: Option, - collect_other_fields: bool, - expecting: Option<&str>, -) -> Fragment { - let str_mapping = fields.iter().map(|(_, ident, aliases)| { - // `aliases` also contains a main name - quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident)) - }); - let bytes_mapping = fields.iter().map(|(_, ident, aliases)| { - // `aliases` also contains a main name - let aliases = aliases - .iter() - .map(|alias| Literal::byte_string(alias.as_bytes())); - quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident)) - }); - - let expecting = expecting.unwrap_or(if is_variant { - "variant identifier" - } else { - "field identifier" - }); - - let bytes_to_str = if fallthrough.is_some() || collect_other_fields { - None - } else { - Some(quote! { - let __value = &_serde::__private::from_utf8_lossy(__value); - }) - }; - - let ( - value_as_str_content, - value_as_borrowed_str_content, - value_as_bytes_content, - value_as_borrowed_bytes_content, - ) = if collect_other_fields { - ( - Some(quote! { - let __value = _serde::__private::de::Content::String(_serde::__private::ToString::to_string(__value)); - }), - Some(quote! { - let __value = _serde::__private::de::Content::Str(__value); - }), - Some(quote! { - let __value = _serde::__private::de::Content::ByteBuf(__value.to_vec()); - }), - Some(quote! { - let __value = _serde::__private::de::Content::Bytes(__value); - }), - ) - } else { - (None, None, None, None) - }; - - let fallthrough_arm_tokens; - let fallthrough_arm = if let Some(fallthrough) = &fallthrough { - fallthrough - } else if is_variant { - fallthrough_arm_tokens = quote! { - _serde::__private::Err(_serde::de::Error::unknown_variant(__value, VARIANTS)) - }; - &fallthrough_arm_tokens - } else { - fallthrough_arm_tokens = quote! { - _serde::__private::Err(_serde::de::Error::unknown_field(__value, FIELDS)) - }; - &fallthrough_arm_tokens - }; - - let visit_other = if collect_other_fields { - quote! { - fn visit_bool<__E>(self, __value: bool) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::Bool(__value))) - } - - fn visit_i8<__E>(self, __value: i8) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::I8(__value))) - } - - fn visit_i16<__E>(self, __value: i16) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::I16(__value))) - } - - fn visit_i32<__E>(self, __value: i32) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::I32(__value))) - } - - fn visit_i64<__E>(self, __value: i64) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::I64(__value))) - } - - fn visit_u8<__E>(self, __value: u8) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::U8(__value))) - } - - fn visit_u16<__E>(self, __value: u16) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::U16(__value))) - } - - fn visit_u32<__E>(self, __value: u32) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::U32(__value))) - } - - fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::U64(__value))) - } - - fn visit_f32<__E>(self, __value: f32) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::F32(__value))) - } - - fn visit_f64<__E>(self, __value: f64) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::F64(__value))) - } - - fn visit_char<__E>(self, __value: char) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::Char(__value))) - } - - fn visit_unit<__E>(self) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - _serde::__private::Ok(__Field::__other(_serde::__private::de::Content::Unit)) - } - } - } else { - let u64_mapping = fields.iter().enumerate().map(|(i, (_, ident, _))| { - let i = i as u64; - quote!(#i => _serde::__private::Ok(#this_value::#ident)) - }); - - let u64_fallthrough_arm_tokens; - let u64_fallthrough_arm = if let Some(fallthrough) = &fallthrough { - fallthrough - } else { - let index_expecting = if is_variant { "variant" } else { "field" }; - let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len()); - u64_fallthrough_arm_tokens = quote! { - _serde::__private::Err(_serde::de::Error::invalid_value( - _serde::de::Unexpected::Unsigned(__value), - &#fallthrough_msg, - )) - }; - &u64_fallthrough_arm_tokens - }; - - quote! { - fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - #(#u64_mapping,)* - _ => #u64_fallthrough_arm, - } - } - } - }; - - let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields { - let str_mapping = str_mapping.clone(); - let bytes_mapping = bytes_mapping.clone(); - let fallthrough_borrowed_arm = fallthrough_borrowed.as_ref().unwrap_or(fallthrough_arm); - Some(quote! { - fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - #(#str_mapping,)* - _ => { - #value_as_borrowed_str_content - #fallthrough_borrowed_arm - } - } - } - - fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - #(#bytes_mapping,)* - _ => { - #bytes_to_str - #value_as_borrowed_bytes_content - #fallthrough_borrowed_arm - } - } - } - }) - } else { - None - }; - - quote_block! { - fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str(__formatter, #expecting) - } - - #visit_other - - fn visit_str<__E>(self, __value: &str) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - #(#str_mapping,)* - _ => { - #value_as_str_content - #fallthrough_arm - } - } - } - - fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - #(#bytes_mapping,)* - _ => { - #bytes_to_str - #value_as_bytes_content - #fallthrough_arm - } - } - } - - #visit_borrowed - } -} - -fn deserialize_map( - struct_path: &TokenStream, - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, -) -> Fragment { - // Create the field names for the fields. - let fields_names: Vec<_> = fields - .iter() - .enumerate() - .map(|(i, field)| (field, field_i(i))) - .collect(); - - // Declare each field that will be deserialized. - let let_values = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(field, name)| { - let field_ty = field.ty; - quote! { - let mut #name: _serde::__private::Option<#field_ty> = _serde::__private::None; - } - }); - - // Collect contents for flatten fields into a buffer - let let_collect = if cattrs.has_flatten() { - Some(quote! { - let mut __collect = _serde::__private::Vec::<_serde::__private::Option<( - _serde::__private::de::Content, - _serde::__private::de::Content - )>>::new(); - }) - } else { - None - }; - - // Match arms to extract a value for a field. - let value_arms = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(field, name)| { - let deser_name = field.attrs.name().deserialize_name(); - - let visit = match field.attrs.deserialize_with() { - None => { - let field_ty = field.ty; - let span = field.original.span(); - let func = - quote_spanned!(span=> _serde::de::MapAccess::next_value::<#field_ty>); - quote! { - #func(&mut __map)? - } - } - Some(path) => { - let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); - quote!({ - #wrapper - match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) { - _serde::__private::Ok(__wrapper) => __wrapper.value, - _serde::__private::Err(__err) => { - return _serde::__private::Err(__err); - } - } - }) - } - }; - quote! { - __Field::#name => { - if _serde::__private::Option::is_some(&#name) { - return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); - } - #name = _serde::__private::Some(#visit); - } - } - }); - - // Visit ignored values to consume them - let ignored_arm = if cattrs.has_flatten() { - Some(quote! { - __Field::__other(__name) => { - __collect.push(_serde::__private::Some(( - __name, - _serde::de::MapAccess::next_value(&mut __map)?))); - } - }) - } else if cattrs.deny_unknown_fields() { - None - } else { - Some(quote! { - _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; } - }) - }; - - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - let match_keys = if cattrs.deny_unknown_fields() && all_skipped { - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; - _serde::__private::Option::map( - _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, - |__impossible| match __impossible {}); - } - } else { - quote! { - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - #(#value_arms)* - #ignored_arm - } - } - } - }; - - let extract_values = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(field, name)| { - let missing_expr = Match(expr_is_missing(field, cattrs)); - - quote! { - let #name = match #name { - _serde::__private::Some(#name) => #name, - _serde::__private::None => #missing_expr - }; - } - }); - - let extract_collected = fields_names - .iter() - .filter(|&&(field, _)| field.attrs.flatten() && !field.attrs.skip_deserializing()) - .map(|(field, name)| { - let field_ty = field.ty; - let func = match field.attrs.deserialize_with() { - None => { - let span = field.original.span(); - quote_spanned!(span=> _serde::de::Deserialize::deserialize) - } - Some(path) => quote!(#path), - }; - quote! { - let #name: #field_ty = #func( - _serde::__private::de::FlatMapDeserializer( - &mut __collect, - _serde::__private::PhantomData))?; - } - }); - - let collected_deny_unknown_fields = if cattrs.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() - { - if let _serde::__private::Some(__key) = __key.as_str() { - return _serde::__private::Err( - _serde::de::Error::custom(format_args!("unknown field `{}`", &__key))); - } else { - return _serde::__private::Err( - _serde::de::Error::custom(format_args!("unexpected map key"))); - } - } - }) - } else { - None - }; - - let result = fields_names.iter().map(|(field, name)| { - let member = &field.member; - if field.attrs.skip_deserializing() { - let value = Expr(expr_is_missing(field, cattrs)); - quote!(#member: #value) - } else { - quote!(#member: #name) - } - }); - - let let_default = match cattrs.default() { - attr::Default::Default => Some(quote!( - let __default: Self::Value = _serde::__private::Default::default(); - )), - attr::Default::Path(path) => Some(quote!( - let __default: Self::Value = #path(); - )), - attr::Default::None => { - // We don't need the default value, to prevent an unused variable warning - // we'll leave the line empty. - None - } - }; - - let mut result = quote!(#struct_path { #(#result),* }); - if params.has_getter { - let this_type = ¶ms.this_type; - let (_, ty_generics, _) = params.generics.split_for_impl(); - result = quote! { - _serde::__private::Into::<#this_type #ty_generics>::into(#result) - }; - } - - quote_block! { - #(#let_values)* - - #let_collect - - #match_keys - - #let_default - - #(#extract_values)* - - #(#extract_collected)* - - #collected_deny_unknown_fields - - _serde::__private::Ok(#result) - } -} - -#[cfg(feature = "deserialize_in_place")] -fn deserialize_map_in_place( - params: &Parameters, - fields: &[Field], - cattrs: &attr::Container, -) -> Fragment { - assert!(!cattrs.has_flatten()); - - // Create the field names for the fields. - let fields_names: Vec<_> = fields - .iter() - .enumerate() - .map(|(i, field)| (field, field_i(i))) - .collect(); - - // For deserialize_in_place, declare booleans for each field that will be - // deserialized. - let let_flags = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|(_, name)| { - quote! { - let mut #name: bool = false; - } - }); - - // Match arms to extract a value for a field. - let value_arms_from = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|(field, name)| { - let deser_name = field.attrs.name().deserialize_name(); - let member = &field.member; - - let visit = match field.attrs.deserialize_with() { - None => { - quote! { - _serde::de::MapAccess::next_value_seed(&mut __map, _serde::__private::de::InPlaceSeed(&mut self.place.#member))? - } - } - Some(path) => { - let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); - quote!({ - #wrapper - self.place.#member = match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) { - _serde::__private::Ok(__wrapper) => __wrapper.value, - _serde::__private::Err(__err) => { - return _serde::__private::Err(__err); - } - }; - }) - } - }; - quote! { - __Field::#name => { - if #name { - return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); - } - #visit; - #name = true; - } - } - }); - - // Visit ignored values to consume them - let ignored_arm = if cattrs.deny_unknown_fields() { - None - } else { - Some(quote! { - _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; } - }) - }; - - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - - let match_keys = if cattrs.deny_unknown_fields() && all_skipped { - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; - _serde::__private::Option::map( - _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, - |__impossible| match __impossible {}); - } - } else { - quote! { - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - #(#value_arms_from)* - #ignored_arm - } - } - } - }; - - let check_flags = fields_names - .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|(field, name)| { - let missing_expr = expr_is_missing(field, cattrs); - // If missing_expr unconditionally returns an error, don't try - // to assign its value to self.place. - if field.attrs.default().is_none() - && cattrs.default().is_none() - && field.attrs.deserialize_with().is_some() - { - let missing_expr = Stmts(missing_expr); - quote! { - if !#name { - #missing_expr; - } - } - } else { - let member = &field.member; - let missing_expr = Expr(missing_expr); - quote! { - if !#name { - self.place.#member = #missing_expr; - }; - } - } - }); - - let this_type = ¶ms.this_type; - let (_, _, ty_generics, _) = split_with_de_lifetime(params); - - let let_default = match cattrs.default() { - attr::Default::Default => Some(quote!( - let __default: #this_type #ty_generics = _serde::__private::Default::default(); - )), - attr::Default::Path(path) => Some(quote!( - let __default: #this_type #ty_generics = #path(); - )), - attr::Default::None => { - // We don't need the default value, to prevent an unused variable warning - // we'll leave the line empty. - None - } - }; - - quote_block! { - #(#let_flags)* - - #match_keys - - #let_default - - #(#check_flags)* - - _serde::__private::Ok(()) - } +struct FieldWithAliases<'a> { + ident: Ident, + aliases: &'a BTreeSet, } fn field_i(i: usize) -> Ident { @@ -2837,26 +665,35 @@ fn wrap_deserialize_with( ) -> (TokenStream, TokenStream) { let this_type = ¶ms.this_type; let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); + params.generics_with_de_lifetime(); 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 { value: #value_ty, - phantom: _serde::__private::PhantomData<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData<&#delife ()>, + phantom: _serde::#private::PhantomData<#this_type #ty_generics>, + 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)?, - phantom: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData, + _serde::#private::Ok(__DeserializeWith { + value: #value, + phantom: _serde::#private::PhantomData, + lifetime: _serde::#private::PhantomData, }) } } @@ -2875,20 +712,6 @@ fn wrap_deserialize_field_with( wrap_deserialize_with(params, "e!(#field_ty), deserialize_with) } -fn wrap_deserialize_variant_with( - params: &Parameters, - variant: &Variant, - deserialize_with: &syn::ExprPath, -) -> (TokenStream, TokenStream, TokenStream) { - let field_tys = variant.fields.iter().map(|field| field.ty); - let (wrapper, wrapper_ty) = - wrap_deserialize_with(params, "e!((#(#field_tys),*)), deserialize_with); - - let unwrap_fn = unwrap_to_variant_closure(params, variant, true); - - (wrapper, wrapper_ty, unwrap_fn) -} - // Generates closure that converts single input parameter to the final value. fn unwrap_to_variant_closure( params: &Parameters, @@ -2941,11 +764,15 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { match field.attrs.default() { attr::Default::Default => { let span = field.original.span(); - let func = quote_spanned!(span=> _serde::__private::Default::default); + let func = quote_spanned!(span=> _serde::#private::Default::default); 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 */ } } @@ -2962,14 +789,14 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { match field.attrs.deserialize_with() { None => { let span = field.original.span(); - let func = quote_spanned!(span=> _serde::__private::de::missing_field); + let func = quote_spanned!(span=> _serde::#private::de::missing_field); quote_expr! { #func(#name)? } } Some(_) => { quote_expr! { - return _serde::__private::Err(<__A::Error as _serde::de::Error>::missing_field(#name)) + return _serde::#private::Err(<__A::Error as _serde::de::Error>::missing_field(#name)) } } } @@ -2985,9 +812,13 @@ fn expr_is_missing_seq( match field.attrs.default() { attr::Default::Default => { let span = field.original.span(); - return quote_spanned!(span=> #assign_to _serde::__private::Default::default()); + 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,7 +830,7 @@ fn expr_is_missing_seq( quote!(#assign_to __default.#member) } attr::Default::None => quote!( - return _serde::__private::Err(_serde::de::Error::invalid_length(#index, &#expecting)) + return _serde::#private::Err(_serde::de::Error::invalid_length(#index, &#expecting)) ), } } @@ -3011,6 +842,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); @@ -3132,17 +971,3 @@ fn place_lifetime() -> syn::LifetimeParam { bounds: Punctuated::new(), } } - -fn split_with_de_lifetime( - params: &Parameters, -) -> ( - DeImplGenerics, - DeTypeGenerics, - syn::TypeGenerics, - Option<&syn::WhereClause>, -) { - let de_impl_generics = DeImplGenerics(params); - let de_ty_generics = DeTypeGenerics(params); - let (_, ty_generics, where_clause) = params.generics.split_for_impl(); - (de_impl_generics, de_ty_generics, ty_generics, where_clause) -} diff --git a/serde_derive/src/de/enum_.rs b/serde_derive/src/de/enum_.rs new file mode 100644 index 00000000..6f5094ff --- /dev/null +++ b/serde_derive/src/de/enum_.rs @@ -0,0 +1,96 @@ +use crate::de::enum_adjacently; +use crate::de::enum_externally; +use crate::de::enum_internally; +use crate::de::enum_untagged; +use crate::de::identifier; +use crate::de::{field_i, FieldWithAliases, Parameters}; +use crate::fragment::{Expr, Fragment, Stmts}; +use crate::internals::ast::Variant; +use crate::internals::attr; +use crate::private; +use proc_macro2::TokenStream; +use quote::quote; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` +pub(super) fn deserialize( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, +) -> Fragment { + // The variants have already been checked (in ast.rs) that all untagged variants appear at the end + match variants.iter().position(|var| var.attrs.untagged()) { + Some(variant_idx) => { + let (tagged, untagged) = variants.split_at(variant_idx); + let tagged_frag = Expr(deserialize_homogeneous_enum(params, tagged, cattrs)); + // 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 = quote! { + if let _serde::#private::Result::<_, __D::Error>::Ok(__ok) = (|| #tagged_frag)() { + return _serde::#private::Ok(__ok); + } + }; + enum_untagged::deserialize(params, untagged, cattrs, Some(first_attempt)) + } + None => deserialize_homogeneous_enum(params, variants, cattrs), + } +} + +fn deserialize_homogeneous_enum( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, +) -> Fragment { + match cattrs.tag() { + attr::TagType::External => enum_externally::deserialize(params, variants, cattrs), + attr::TagType::Internal { tag } => { + enum_internally::deserialize(params, variants, cattrs, tag) + } + attr::TagType::Adjacent { tag, content } => { + enum_adjacently::deserialize(params, variants, cattrs, tag, content) + } + attr::TagType::None => enum_untagged::deserialize(params, variants, cattrs, None), + } +} + +pub fn prepare_enum_variant_enum(variants: &[Variant]) -> (TokenStream, Stmts) { + let deserialized_variants = variants + .iter() + .enumerate() + .filter(|&(_i, variant)| !variant.attrs.skip_deserializing()); + + let fallthrough = deserialized_variants + .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 = 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(identifier::deserialize_generated( + &deserialized_variants, + false, // variant identifiers do not depend on the presence of flatten fields + true, + None, + fallthrough, + )); + + (variants_stmt, variant_visitor) +} diff --git a/serde_derive/src/de/enum_adjacently.rs b/serde_derive/src/de/enum_adjacently.rs new file mode 100644 index 00000000..467cfc0d --- /dev/null +++ b/serde_derive/src/de/enum_adjacently.rs @@ -0,0 +1,323 @@ +//! Deserialization for adjacently tagged enums: +//! +//! ```ignore +//! #[serde(tag = "...", content = "...")] +//! enum Enum {} +//! ``` + +use crate::de::enum_; +use crate::de::enum_untagged; +use crate::de::{field_i, Parameters}; +use crate::fragment::{Fragment, Match}; +use crate::internals::ast::{Style, Variant}; +use crate::internals::attr; +use crate::private; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(tag, content)]` attributes +pub(super) fn deserialize( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, + tag: &str, + content: &str, +) -> Fragment { + let this_type = ¶ms.this_type; + let this_value = ¶ms.this_value; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + let (variants_stmt, variant_visitor) = enum_::prepare_enum_variant_enum(variants); + + let variant_arms: &Vec<_> = &variants + .iter() + .enumerate() + .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) + .map(|(i, variant)| { + let variant_index = field_i(i); + + let block = Match(enum_untagged::deserialize_variant(params, variant, cattrs)); + + quote! { + __Field::#variant_index => #block + } + }) + .collect(); + + let rust_name = params.type_name(); + let expecting = format!("adjacently tagged enum {}", rust_name); + let expecting = cattrs.expecting().unwrap_or(&expecting); + let type_name = cattrs.name().deserialize_name(); + let deny_unknown_fields = cattrs.deny_unknown_fields(); + + // If unknown fields are allowed, we pick the visitor that can step over + // those. Otherwise we pick the visitor that fails on unknown keys. + let field_visitor_ty = if deny_unknown_fields { + quote! { _serde::#private::de::TagOrContentFieldVisitor } + } else { + quote! { _serde::#private::de::TagContentOtherFieldVisitor } + }; + + let mut missing_content = quote! { + _serde::#private::Err(<__A::Error as _serde::de::Error>::missing_field(#content)) + }; + let mut missing_content_fallthrough = quote!(); + let missing_content_arms = variants + .iter() + .enumerate() + .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) + .filter_map(|(i, variant)| { + let variant_index = field_i(i); + let variant_ident = &variant.ident; + + let arm = match variant.style { + Style::Unit => quote! { + _serde::#private::Ok(#this_value::#variant_ident) + }, + Style::Newtype if variant.attrs.deserialize_with().is_none() => { + let span = variant.original.span(); + let func = quote_spanned!(span=> _serde::#private::de::missing_field); + quote! { + #func(#content).map(#this_value::#variant_ident) + } + } + _ => { + missing_content_fallthrough = quote!(_ => #missing_content); + return None; + } + }; + Some(quote! { + __Field::#variant_index => #arm, + }) + }) + .collect::>(); + if !missing_content_arms.is_empty() { + missing_content = quote! { + match __field { + #(#missing_content_arms)* + #missing_content_fallthrough + } + }; + } + + // Advance the map by one key, returning early in case of error. + let next_key = quote! { + _serde::de::MapAccess::next_key_seed(&mut __map, #field_visitor_ty { + tag: #tag, + content: #content, + })? + }; + + let variant_from_map = quote! { + _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::AdjacentlyTaggedEnumVariantSeed::<__Field> { + enum_name: #rust_name, + variants: VARIANTS, + fields_enum: _serde::#private::PhantomData + })? + }; + + // When allowing unknown fields, we want to transparently step through keys + // we don't care about until we find `tag`, `content`, or run out of keys. + let next_relevant_key = if deny_unknown_fields { + next_key + } else { + quote!({ + let mut __rk : _serde::#private::Option<_serde::#private::de::TagOrContentField> = _serde::#private::None; + while let _serde::#private::Some(__k) = #next_key { + match __k { + _serde::#private::de::TagContentOtherField::Other => { + let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; + continue; + }, + _serde::#private::de::TagContentOtherField::Tag => { + __rk = _serde::#private::Some(_serde::#private::de::TagOrContentField::Tag); + break; + } + _serde::#private::de::TagContentOtherField::Content => { + __rk = _serde::#private::Some(_serde::#private::de::TagOrContentField::Content); + break; + } + } + } + + __rk + }) + }; + + // Step through remaining keys, looking for duplicates of previously-seen + // keys. When unknown fields are denied, any key that isn't a duplicate will + // at this point immediately produce an error. + let visit_remaining_keys = quote! { + match #next_relevant_key { + _serde::#private::Some(_serde::#private::de::TagOrContentField::Tag) => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag)) + } + _serde::#private::Some(_serde::#private::de::TagOrContentField::Content) => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content)) + } + _serde::#private::None => _serde::#private::Ok(__ret), + } + }; + + let finish_content_then_tag = if variant_arms.is_empty() { + quote! { + match #variant_from_map {} + } + } else { + quote! { + let __seed = __Seed { + variant: #variant_from_map, + marker: _serde::#private::PhantomData, + lifetime: _serde::#private::PhantomData, + }; + let __deserializer = _serde::#private::de::ContentDeserializer::<__A::Error>::new(__content); + let __ret = _serde::de::DeserializeSeed::deserialize(__seed, __deserializer)?; + // Visit remaining keys, looking for duplicates. + #visit_remaining_keys + } + }; + + quote_block! { + #variant_visitor + + #variants_stmt + + #[doc(hidden)] + struct __Seed #de_impl_generics #where_clause { + variant: __Field, + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn deserialize<__D>(self, __deserializer: __D) -> _serde::#private::Result + where + __D: _serde::Deserializer<#delife>, + { + match self.variant { + #(#variant_arms)* + } + } + } + + #[doc(hidden)] + struct __Visitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + fn visit_map<__A>(self, mut __map: __A) -> _serde::#private::Result + where + __A: _serde::de::MapAccess<#delife>, + { + // Visit the first relevant key. + match #next_relevant_key { + // First key is the tag. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Tag) => { + // Parse the tag. + let __field = #variant_from_map; + // Visit the second key. + match #next_relevant_key { + // Second key is a duplicate of the tag. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Tag) => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag)) + } + // Second key is the content. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Content) => { + let __ret = _serde::de::MapAccess::next_value_seed(&mut __map, + __Seed { + variant: __field, + marker: _serde::#private::PhantomData, + lifetime: _serde::#private::PhantomData, + })?; + // Visit remaining keys, looking for duplicates. + #visit_remaining_keys + } + // There is no second key; might be okay if the we have a unit variant. + _serde::#private::None => #missing_content + } + } + // First key is the content. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Content) => { + // Buffer up the content. + let __content = _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::ContentVisitor::new())?; + // Visit the second key. + match #next_relevant_key { + // Second key is the tag. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Tag) => { + #finish_content_then_tag + } + // Second key is a duplicate of the content. + _serde::#private::Some(_serde::#private::de::TagOrContentField::Content) => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content)) + } + // There is no second key. + _serde::#private::None => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::missing_field(#tag)) + } + } + } + // There is no first key. + _serde::#private::None => { + _serde::#private::Err(<__A::Error as _serde::de::Error>::missing_field(#tag)) + } + } + } + + fn visit_seq<__A>(self, mut __seq: __A) -> _serde::#private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + // Visit the first element - the tag. + match _serde::de::SeqAccess::next_element(&mut __seq)? { + _serde::#private::Some(__variant) => { + // Visit the second element - the content. + match _serde::de::SeqAccess::next_element_seed( + &mut __seq, + __Seed { + variant: __variant, + marker: _serde::#private::PhantomData, + lifetime: _serde::#private::PhantomData, + }, + )? { + _serde::#private::Some(__ret) => _serde::#private::Ok(__ret), + // There is no second element. + _serde::#private::None => { + _serde::#private::Err(_serde::de::Error::invalid_length(1, &self)) + } + } + } + // There is no first element. + _serde::#private::None => { + _serde::#private::Err(_serde::de::Error::invalid_length(0, &self)) + } + } + } + } + + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[#tag, #content]; + _serde::Deserializer::deserialize_struct( + __deserializer, + #type_name, + FIELDS, + __Visitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + }, + ) + } +} diff --git a/serde_derive/src/de/enum_externally.rs b/serde_derive/src/de/enum_externally.rs new file mode 100644 index 00000000..9e5bde3d --- /dev/null +++ b/serde_derive/src/de/enum_externally.rs @@ -0,0 +1,212 @@ +//! Deserialization for externally tagged enums: +//! +//! ```ignore +//! enum Enum {} +//! ``` + +use crate::de::enum_; +use crate::de::struct_; +use crate::de::tuple; +use crate::de::{ + expr_is_missing, field_i, unwrap_to_variant_closure, wrap_deserialize_field_with, + wrap_deserialize_with, Parameters, StructForm, TupleForm, +}; +use crate::fragment::{Expr, Fragment, Match}; +use crate::internals::ast::{Field, Style, Variant}; +use crate::internals::attr; +use crate::private; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` without additional attributes +pub(super) fn deserialize( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, +) -> Fragment { + let this_type = ¶ms.this_type; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + let type_name = cattrs.name().deserialize_name(); + let expecting = format!("enum {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + let (variants_stmt, variant_visitor) = enum_::prepare_enum_variant_enum(variants); + + // Match arms to extract a variant from a string + let variant_arms = variants + .iter() + .enumerate() + .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) + .map(|(i, variant)| { + let variant_name = field_i(i); + + let block = Match(deserialize_externally_tagged_variant( + params, variant, cattrs, + )); + + quote! { + (__Field::#variant_name, __variant) => #block + } + }); + + let all_skipped = variants + .iter() + .all(|variant| variant.attrs.skip_deserializing()); + let match_variant = if all_skipped { + // This is an empty enum like `enum Impossible {}` or an enum in which + // all variants have `#[serde(skip_deserializing)]`. + quote! { + // FIXME: Once feature(exhaustive_patterns) is stable: + // let _serde::#private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data); + // _serde::#private::Err(__err) + _serde::#private::Result::map( + _serde::de::EnumAccess::variant::<__Field>(__data), + |(__impossible, _)| match __impossible {}) + } + } else { + quote! { + match _serde::de::EnumAccess::variant(__data)? { + #(#variant_arms)* + } + } + }; + + quote_block! { + #variant_visitor + + #[doc(hidden)] + struct __Visitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + fn visit_enum<__A>(self, __data: __A) -> _serde::#private::Result + where + __A: _serde::de::EnumAccess<#delife>, + { + #match_variant + } + } + + #variants_stmt + + _serde::Deserializer::deserialize_enum( + __deserializer, + #type_name, + VARIANTS, + __Visitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + }, + ) + } +} + +fn deserialize_externally_tagged_variant( + params: &Parameters, + variant: &Variant, + cattrs: &attr::Container, +) -> Fragment { + if let Some(path) = variant.attrs.deserialize_with() { + let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path); + return quote_block! { + #wrapper + _serde::#private::Result::map( + _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), #unwrap_fn) + }; + } + + let variant_ident = &variant.ident; + + match variant.style { + Style::Unit => { + let this_value = ¶ms.this_value; + quote_block! { + _serde::de::VariantAccess::unit_variant(__variant)?; + _serde::#private::Ok(#this_value::#variant_ident) + } + } + Style::Newtype => deserialize_externally_tagged_newtype_variant( + variant_ident, + params, + &variant.fields[0], + cattrs, + ), + Style::Tuple => tuple::deserialize( + params, + &variant.fields, + cattrs, + TupleForm::ExternallyTagged(variant_ident), + ), + Style::Struct => struct_::deserialize( + params, + &variant.fields, + cattrs, + StructForm::ExternallyTagged(variant_ident), + ), + } +} + +fn wrap_deserialize_variant_with( + params: &Parameters, + variant: &Variant, + deserialize_with: &syn::ExprPath, +) -> (TokenStream, TokenStream, TokenStream) { + let field_tys = variant.fields.iter().map(|field| field.ty); + let (wrapper, wrapper_ty) = + wrap_deserialize_with(params, "e!((#(#field_tys),*)), deserialize_with); + + let unwrap_fn = unwrap_to_variant_closure(params, variant, true); + + (wrapper, wrapper_ty, unwrap_fn) +} + +fn deserialize_externally_tagged_newtype_variant( + variant_ident: &syn::Ident, + params: &Parameters, + field: &Field, + cattrs: &attr::Container, +) -> Fragment { + let this_value = ¶ms.this_value; + + if field.attrs.skip_deserializing() { + let default = Expr(expr_is_missing(field, cattrs)); + return quote_block! { + _serde::de::VariantAccess::unit_variant(__variant)?; + _serde::#private::Ok(#this_value::#variant_ident(#default)) + }; + } + + match field.attrs.deserialize_with() { + None => { + let field_ty = field.ty; + let span = field.original.span(); + let func = + quote_spanned!(span=> _serde::de::VariantAccess::newtype_variant::<#field_ty>); + quote_expr! { + _serde::#private::Result::map(#func(__variant), #this_value::#variant_ident) + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); + quote_block! { + #wrapper + _serde::#private::Result::map( + _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), + |__wrapper| #this_value::#variant_ident(__wrapper.value)) + } + } + } +} diff --git a/serde_derive/src/de/enum_internally.rs b/serde_derive/src/de/enum_internally.rs new file mode 100644 index 00000000..b669eef7 --- /dev/null +++ b/serde_derive/src/de/enum_internally.rs @@ -0,0 +1,106 @@ +//! Deserialization for internally tagged enums: +//! +//! ```ignore +//! #[serde(tag = "...")] +//! enum Enum {} +//! ``` + +use crate::de::enum_; +use crate::de::enum_untagged; +use crate::de::struct_; +use crate::de::{ + effective_style, expr_is_missing, field_i, unwrap_to_variant_closure, Parameters, StructForm, +}; +use crate::fragment::{Expr, Fragment, Match}; +use crate::internals::ast::{Style, Variant}; +use crate::internals::attr; +use crate::private; +use quote::quote; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(tag)]` attribute +pub(super) fn deserialize( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, + tag: &str, +) -> Fragment { + let (variants_stmt, variant_visitor) = enum_::prepare_enum_variant_enum(variants); + + // Match arms to extract a variant from a string + let variant_arms = variants + .iter() + .enumerate() + .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) + .map(|(i, variant)| { + let variant_name = field_i(i); + + let block = Match(deserialize_internally_tagged_variant( + params, variant, cattrs, + )); + + quote! { + __Field::#variant_name => #block + } + }); + + let expecting = format!("internally tagged enum {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + quote_block! { + #variant_visitor + + #variants_stmt + + let (__tag, __content) = _serde::Deserializer::deserialize_any( + __deserializer, + _serde::#private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting))?; + let __deserializer = _serde::#private::de::ContentDeserializer::<__D::Error>::new(__content); + + match __tag { + #(#variant_arms)* + } + } +} + +// Generates significant part of the visit_seq and visit_map bodies of visitors +// for the variants of internally tagged enum. +fn deserialize_internally_tagged_variant( + params: &Parameters, + variant: &Variant, + cattrs: &attr::Container, +) -> Fragment { + if let Some(path) = variant.attrs.deserialize_with() { + let unwrap_fn = unwrap_to_variant_closure(params, variant, false); + return quote_block! { + _serde::#private::Result::map(#path(__deserializer), #unwrap_fn) + }; + } + + let variant_ident = &variant.ident; + + match effective_style(variant) { + Style::Unit => { + let this_value = ¶ms.this_value; + let type_name = params.type_name(); + let variant_name = variant.ident.to_string(); + let default = variant.fields.first().map(|field| { + let default = Expr(expr_is_missing(field, cattrs)); + quote!((#default)) + }); + quote_block! { + _serde::Deserializer::deserialize_any(__deserializer, _serde::#private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name))?; + _serde::#private::Ok(#this_value::#variant_ident #default) + } + } + Style::Newtype => { + enum_untagged::deserialize_newtype_variant(variant_ident, params, &variant.fields[0]) + } + Style::Struct => struct_::deserialize( + params, + &variant.fields, + cattrs, + StructForm::InternallyTagged(variant_ident), + ), + Style::Tuple => unreachable!("checked in serde_derive_internals"), + } +} diff --git a/serde_derive/src/de/enum_untagged.rs b/serde_derive/src/de/enum_untagged.rs new file mode 100644 index 00000000..f66d98de --- /dev/null +++ b/serde_derive/src/de/enum_untagged.rs @@ -0,0 +1,135 @@ +//! Deserialization for untagged enums: +//! +//! ```ignore +//! #[serde(untagged)] +//! enum Enum {} +//! ``` + +use crate::de::struct_; +use crate::de::tuple; +use crate::de::{ + effective_style, expr_is_missing, unwrap_to_variant_closure, Parameters, StructForm, TupleForm, +}; +use crate::fragment::{Expr, Fragment}; +use crate::internals::ast::{Field, Style, Variant}; +use crate::internals::attr; +use crate::private; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(untagged)]` attribute +pub(super) fn deserialize( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, + first_attempt: Option, +) -> Fragment { + let attempts = variants + .iter() + .filter(|variant| !variant.attrs.skip_deserializing()) + .map(|variant| Expr(deserialize_variant(params, variant, cattrs))); + // 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 + // largest number of fields. I'm not sure I like that. Maybe it would be + // better to save all the errors and combine them into one message that + // explains why none of the variants matched. + let fallthrough_msg = format!( + "data did not match any variant of untagged enum {}", + params.type_name() + ); + let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg); + + let private2 = private; + quote_block! { + let __content = _serde::de::DeserializeSeed::deserialize(_serde::#private::de::ContentVisitor::new(), __deserializer)?; + let __deserializer = _serde::#private::de::ContentRefDeserializer::<__D::Error>::new(&__content); + + #first_attempt + + #( + if let _serde::#private2::Ok(__ok) = #attempts { + return _serde::#private2::Ok(__ok); + } + )* + + _serde::#private::Err(_serde::de::Error::custom(#fallthrough_msg)) + } +} + +// Also used by adjacently tagged enums +pub(super) fn deserialize_variant( + params: &Parameters, + variant: &Variant, + cattrs: &attr::Container, +) -> Fragment { + if let Some(path) = variant.attrs.deserialize_with() { + let unwrap_fn = unwrap_to_variant_closure(params, variant, false); + return quote_block! { + _serde::#private::Result::map(#path(__deserializer), #unwrap_fn) + }; + } + + let variant_ident = &variant.ident; + + match effective_style(variant) { + Style::Unit => { + let this_value = ¶ms.this_value; + let type_name = params.type_name(); + let variant_name = variant.ident.to_string(); + let default = variant.fields.first().map(|field| { + let default = Expr(expr_is_missing(field, cattrs)); + quote!((#default)) + }); + quote_expr! { + match _serde::Deserializer::deserialize_any( + __deserializer, + _serde::#private::de::UntaggedUnitVisitor::new(#type_name, #variant_name) + ) { + _serde::#private::Ok(()) => _serde::#private::Ok(#this_value::#variant_ident #default), + _serde::#private::Err(__err) => _serde::#private::Err(__err), + } + } + } + Style::Newtype => deserialize_newtype_variant(variant_ident, params, &variant.fields[0]), + Style::Tuple => tuple::deserialize( + params, + &variant.fields, + cattrs, + TupleForm::Untagged(variant_ident), + ), + Style::Struct => struct_::deserialize( + params, + &variant.fields, + cattrs, + StructForm::Untagged(variant_ident), + ), + } +} + +// Also used by internally tagged enums +// Implicitly (via `generate_variant`) used by adjacently tagged enums +pub(super) fn deserialize_newtype_variant( + variant_ident: &syn::Ident, + params: &Parameters, + field: &Field, +) -> Fragment { + let this_value = ¶ms.this_value; + let field_ty = field.ty; + match field.attrs.deserialize_with() { + None => { + let span = field.original.span(); + let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize); + quote_expr! { + _serde::#private::Result::map(#func(__deserializer), #this_value::#variant_ident) + } + } + Some(path) => { + quote_block! { + let __value: _serde::#private::Result<#field_ty, _> = #path(__deserializer); + _serde::#private::Result::map(__value, #this_value::#variant_ident) + } + } + } +} diff --git a/serde_derive/src/de/identifier.rs b/serde_derive/src/de/identifier.rs new file mode 100644 index 00000000..8e67b963 --- /dev/null +++ b/serde_derive/src/de/identifier.rs @@ -0,0 +1,477 @@ +//! Deserialization of struct field identifiers and enum variant identifiers by +//! way of a Rust enum. + +use crate::de::{FieldWithAliases, Parameters}; +use crate::fragment::{Fragment, Stmts}; +use crate::internals::ast::{Style, Variant}; +use crate::internals::attr; +use crate::private; +use proc_macro2::{Literal, TokenStream}; +use quote::{quote, ToTokens}; + +// Generates `Deserialize::deserialize` body for an enum with +// `serde(field_identifier)` or `serde(variant_identifier)` attribute. +pub(super) fn deserialize_custom( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, +) -> Fragment { + let is_variant = match cattrs.identifier() { + attr::Identifier::Variant => true, + attr::Identifier::Field => false, + attr::Identifier::No => unreachable!(), + }; + + let this_type = params.this_type.to_token_stream(); + let this_value = params.this_value.to_token_stream(); + + let (ordinary, fallthrough, fallthrough_borrowed) = if let Some(last) = variants.last() { + let last_ident = &last.ident; + if last.attrs.other() { + // Process `serde(other)` attribute. It would always be found on the + // last variant (checked in `check_identifier`), so all preceding + // are ordinary variants. + let ordinary = &variants[..variants.len() - 1]; + let fallthrough = quote!(_serde::#private::Ok(#this_value::#last_ident)); + (ordinary, Some(fallthrough), None) + } else if let Style::Newtype = last.style { + let ordinary = &variants[..variants.len() - 1]; + let fallthrough = |value| { + quote! { + _serde::#private::Result::map( + _serde::Deserialize::deserialize( + _serde::#private::de::IdentifierDeserializer::from(#value) + ), + #this_value::#last_ident) + } + }; + ( + ordinary, + Some(fallthrough(quote!(__value))), + Some(fallthrough(quote!(_serde::#private::de::Borrowed( + __value + )))), + ) + } else { + (variants, None, None) + } + } else { + (variants, None, None) + }; + + let idents_aliases: Vec<_> = ordinary + .iter() + .map(|variant| FieldWithAliases { + ident: variant.ident.clone(), + aliases: variant.attrs.aliases(), + }) + .collect(); + + let names = idents_aliases.iter().flat_map(|variant| variant.aliases); + + let names_const = if fallthrough.is_some() { + None + } else if is_variant { + let variants = quote! { + #[doc(hidden)] + const VARIANTS: &'static [&'static str] = &[ #(#names),* ]; + }; + Some(variants) + } else { + let fields = quote! { + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ #(#names),* ]; + }; + Some(fields) + }; + + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + let visitor_impl = Stmts(deserialize_identifier( + &this_value, + &idents_aliases, + is_variant, + fallthrough, + fallthrough_borrowed, + false, + cattrs.expecting(), + )); + + quote_block! { + #names_const + + #[doc(hidden)] + struct __FieldVisitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + #visitor_impl + } + + let __visitor = __FieldVisitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + }; + _serde::Deserializer::deserialize_identifier(__deserializer, __visitor) + } +} + +pub(super) fn deserialize_generated( + deserialized_fields: &[FieldWithAliases], + has_flatten: bool, + is_variant: bool, + ignore_variant: Option, + fallthrough: Option, +) -> Fragment { + let this_value = quote!(__Field); + let field_idents: &Vec<_> = &deserialized_fields + .iter() + .map(|field| &field.ident) + .collect(); + + let visitor_impl = Stmts(deserialize_identifier( + &this_value, + deserialized_fields, + is_variant, + fallthrough, + None, + !is_variant && has_flatten, + None, + )); + + let lifetime = if !is_variant && has_flatten { + Some(quote!(<'de>)) + } else { + None + }; + + quote_block! { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field #lifetime { + #(#field_idents,)* + #ignore_variant + } + + #[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 + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor) + } + } + } +} + +fn deserialize_identifier( + this_value: &TokenStream, + deserialized_fields: &[FieldWithAliases], + is_variant: bool, + fallthrough: Option, + fallthrough_borrowed: Option, + collect_other_fields: bool, + expecting: Option<&str>, +) -> Fragment { + let str_mapping = deserialized_fields.iter().map(|field| { + let ident = &field.ident; + let aliases = field.aliases; + let private2 = private; + // `aliases` also contains a main name + quote! { + #( + #aliases => _serde::#private2::Ok(#this_value::#ident), + )* + } + }); + let bytes_mapping = deserialized_fields.iter().map(|field| { + let ident = &field.ident; + // `aliases` also contains a main name + let aliases = field + .aliases + .iter() + .map(|alias| Literal::byte_string(alias.value.as_bytes())); + let private2 = private; + quote! { + #( + #aliases => _serde::#private2::Ok(#this_value::#ident), + )* + } + }); + + let expecting = expecting.unwrap_or(if is_variant { + "variant identifier" + } else { + "field identifier" + }); + + let bytes_to_str = if fallthrough.is_some() || collect_other_fields { + None + } else { + Some(quote! { + let __value = &_serde::#private::from_utf8_lossy(__value); + }) + }; + + let ( + value_as_str_content, + value_as_borrowed_str_content, + value_as_bytes_content, + value_as_borrowed_bytes_content, + ) = if collect_other_fields { + ( + Some(quote! { + let __value = _serde::#private::de::Content::String(_serde::#private::ToString::to_string(__value)); + }), + Some(quote! { + let __value = _serde::#private::de::Content::Str(__value); + }), + Some(quote! { + let __value = _serde::#private::de::Content::ByteBuf(__value.to_vec()); + }), + Some(quote! { + let __value = _serde::#private::de::Content::Bytes(__value); + }), + ) + } else { + (None, None, None, None) + }; + + let fallthrough_arm_tokens; + let fallthrough_arm = if let Some(fallthrough) = &fallthrough { + fallthrough + } else if is_variant { + fallthrough_arm_tokens = quote! { + _serde::#private::Err(_serde::de::Error::unknown_variant(__value, VARIANTS)) + }; + &fallthrough_arm_tokens + } else { + fallthrough_arm_tokens = quote! { + _serde::#private::Err(_serde::de::Error::unknown_field(__value, FIELDS)) + }; + &fallthrough_arm_tokens + }; + + let visit_other = if collect_other_fields { + quote! { + fn visit_bool<__E>(self, __value: bool) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::Bool(__value))) + } + + fn visit_i8<__E>(self, __value: i8) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::I8(__value))) + } + + fn visit_i16<__E>(self, __value: i16) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::I16(__value))) + } + + fn visit_i32<__E>(self, __value: i32) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::I32(__value))) + } + + fn visit_i64<__E>(self, __value: i64) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::I64(__value))) + } + + fn visit_u8<__E>(self, __value: u8) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::U8(__value))) + } + + fn visit_u16<__E>(self, __value: u16) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::U16(__value))) + } + + fn visit_u32<__E>(self, __value: u32) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::U32(__value))) + } + + fn visit_u64<__E>(self, __value: u64) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::U64(__value))) + } + + fn visit_f32<__E>(self, __value: f32) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::F32(__value))) + } + + fn visit_f64<__E>(self, __value: f64) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::F64(__value))) + } + + fn visit_char<__E>(self, __value: char) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::Char(__value))) + } + + fn visit_unit<__E>(self) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(__Field::__other(_serde::#private::de::Content::Unit)) + } + } + } else { + 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)) + }); + + let u64_fallthrough_arm_tokens; + let u64_fallthrough_arm = if let Some(fallthrough) = &fallthrough { + fallthrough + } else { + let index_expecting = if is_variant { "variant" } else { "field" }; + 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), + &#fallthrough_msg, + )) + }; + &u64_fallthrough_arm_tokens + }; + + quote! { + fn visit_u64<__E>(self, __value: u64) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + match __value { + #(#u64_mapping,)* + _ => #u64_fallthrough_arm, + } + } + } + }; + + let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields { + let str_mapping = str_mapping.clone(); + let bytes_mapping = bytes_mapping.clone(); + let fallthrough_borrowed_arm = fallthrough_borrowed.as_ref().unwrap_or(fallthrough_arm); + Some(quote! { + fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + match __value { + #(#str_mapping)* + _ => { + #value_as_borrowed_str_content + #fallthrough_borrowed_arm + } + } + } + + fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + match __value { + #(#bytes_mapping)* + _ => { + #bytes_to_str + #value_as_borrowed_bytes_content + #fallthrough_borrowed_arm + } + } + } + }) + } else { + None + }; + + quote_block! { + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #visit_other + + fn visit_str<__E>(self, __value: &str) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + match __value { + #(#str_mapping)* + _ => { + #value_as_str_content + #fallthrough_arm + } + } + } + + fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + match __value { + #(#bytes_mapping)* + _ => { + #bytes_to_str + #value_as_bytes_content + #fallthrough_arm + } + } + } + + #visit_borrowed + } +} diff --git a/serde_derive/src/de/struct_.rs b/serde_derive/src/de/struct_.rs new file mode 100644 index 00000000..6a5da232 --- /dev/null +++ b/serde_derive/src/de/struct_.rs @@ -0,0 +1,697 @@ +use crate::de::identifier; +use crate::de::{ + deserialize_seq, expr_is_missing, field_i, has_flatten, wrap_deserialize_field_with, + FieldWithAliases, Parameters, StructForm, +}; +#[cfg(feature = "deserialize_in_place")] +use crate::de::{deserialize_seq_in_place, place_lifetime}; +use crate::fragment::{Expr, Fragment, Match, Stmts}; +use crate::internals::ast::Field; +use crate::internals::attr; +use crate::private; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +/// Generates `Deserialize::deserialize` body for a `struct Struct {...}` +pub(super) fn deserialize( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, + form: StructForm, +) -> Fragment { + let this_type = ¶ms.this_type; + let this_value = ¶ms.this_value; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + // If there are getters (implying private fields), construct the local type + // and use an `Into` conversion to get the remote type. If there are no + // getters then construct the target type directly. + let construct = if params.has_getter { + let local = ¶ms.local; + quote!(#local) + } else { + quote!(#this_value) + }; + + let type_path = match form { + StructForm::Struct => construct, + StructForm::ExternallyTagged(variant_ident) + | StructForm::InternallyTagged(variant_ident) + | StructForm::Untagged(variant_ident) => quote!(#construct::#variant_ident), + }; + let expecting = match form { + StructForm::Struct => format!("struct {}", params.type_name()), + StructForm::ExternallyTagged(variant_ident) + | StructForm::InternallyTagged(variant_ident) + | StructForm::Untagged(variant_ident) => { + format!("struct variant {}::{}", params.type_name(), variant_ident) + } + }; + let expecting = cattrs.expecting().unwrap_or(&expecting); + + 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)| FieldWithAliases { + ident: field_i(i), + aliases: field.attrs.aliases(), + }) + .collect(); + + 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 has_flatten => None, + _ => { + let mut_seq = if deserialized_fields.is_empty() { + quote!(_) + } else { + quote!(mut __seq) + }; + + let visit_seq = Stmts(deserialize_seq( + &type_path, params, fields, true, cattrs, expecting, + )); + + Some(quote! { + #[inline] + fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::#private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + }) + } + }; + let visit_map = Stmts(deserialize_map( + &type_path, + params, + fields, + cattrs, + has_flatten, + )); + + let visitor_seed = match form { + 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; + + fn deserialize<__D>(self, __deserializer: __D) -> _serde::#private::Result + where + __D: _serde::Deserializer<#delife>, + { + _serde::Deserializer::deserialize_map(__deserializer, self) + } + } + }), + _ => None, + }; + + let fields_stmt = if has_flatten { + None + } else { + let field_names = deserialized_fields.iter().flat_map(|field| field.aliases); + + Some(quote! { + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + }) + }; + + let visitor_expr = quote! { + __Visitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + } + }; + let dispatch = match form { + StructForm::Struct if has_flatten => quote! { + _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) + }, + StructForm::Struct => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) + } + } + StructForm::ExternallyTagged(_) if has_flatten => quote! { + _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) + }, + StructForm::ExternallyTagged(_) => quote! { + _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) + }, + StructForm::InternallyTagged(_) => quote! { + _serde::Deserializer::deserialize_any(__deserializer, #visitor_expr) + }, + StructForm::Untagged(_) => quote! { + _serde::Deserializer::deserialize_any(__deserializer, #visitor_expr) + }, + }; + + quote_block! { + #field_visitor + + #[doc(hidden)] + struct __Visitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #visit_seq + + #[inline] + fn visit_map<__A>(self, mut __map: __A) -> _serde::#private::Result + where + __A: _serde::de::MapAccess<#delife>, + { + #visit_map + } + } + + #visitor_seed + + #fields_stmt + + #dispatch + } +} + +fn deserialize_map( + struct_path: &TokenStream, + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, + has_flatten: bool, +) -> Fragment { + // Create the field names for the fields. + let fields_names: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, field)| (field, field_i(i))) + .collect(); + + // Declare each field that will be deserialized. + let let_values = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(field, name)| { + let field_ty = field.ty; + quote! { + let mut #name: _serde::#private::Option<#field_ty> = _serde::#private::None; + } + }); + + // Collect contents for flatten fields into a buffer + let let_collect = if has_flatten { + Some(quote! { + let mut __collect = _serde::#private::Vec::<_serde::#private::Option<( + _serde::#private::de::Content, + _serde::#private::de::Content + )>>::new(); + }) + } else { + None + }; + + // Match arms to extract a value for a field. + let value_arms = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(field, name)| { + let deser_name = field.attrs.name().deserialize_name(); + + let visit = match field.attrs.deserialize_with() { + None => { + let field_ty = field.ty; + let span = field.original.span(); + let func = + quote_spanned!(span=> _serde::de::MapAccess::next_value::<#field_ty>); + quote! { + #func(&mut __map)? + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); + quote!({ + #wrapper + match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) { + _serde::#private::Ok(__wrapper) => __wrapper.value, + _serde::#private::Err(__err) => { + return _serde::#private::Err(__err); + } + } + }) + } + }; + quote! { + __Field::#name => { + if _serde::#private::Option::is_some(&#name) { + return _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); + } + #name = _serde::#private::Some(#visit); + } + } + }); + + // Visit ignored values to consume them + let ignored_arm = if has_flatten { + Some(quote! { + __Field::__other(__name) => { + __collect.push(_serde::#private::Some(( + __name, + _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::ContentVisitor::new())?))); + } + }) + } else if cattrs.deny_unknown_fields() { + None + } else { + Some(quote! { + _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; } + }) + }; + + let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); + let match_keys = if cattrs.deny_unknown_fields() && all_skipped { + quote! { + // FIXME: Once feature(exhaustive_patterns) is stable: + // let _serde::#private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; + _serde::#private::Option::map( + _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, + |__impossible| match __impossible {}); + } + } else { + quote! { + while let _serde::#private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + #(#value_arms)* + #ignored_arm + } + } + } + }; + + let extract_values = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(field, name)| { + let missing_expr = Match(expr_is_missing(field, cattrs)); + + quote! { + let #name = match #name { + _serde::#private::Some(#name) => #name, + _serde::#private::None => #missing_expr + }; + } + }); + + let extract_collected = fields_names + .iter() + .filter(|&&(field, _)| field.attrs.flatten() && !field.attrs.skip_deserializing()) + .map(|(field, name)| { + let field_ty = field.ty; + let func = match field.attrs.deserialize_with() { + None => { + let span = field.original.span(); + quote_spanned!(span=> _serde::de::Deserialize::deserialize) + } + Some(path) => quote!(#path), + }; + quote! { + let #name: #field_ty = #func( + _serde::#private::de::FlatMapDeserializer( + &mut __collect, + _serde::#private::PhantomData))?; + } + }); + + 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() + { + if let _serde::#private::Some(__key) = _serde::#private::de::content_as_str(&__key) { + return _serde::#private::Err( + _serde::de::Error::custom(format_args!("unknown field `{}`", &__key))); + } else { + return _serde::#private::Err( + _serde::de::Error::custom(format_args!("unexpected map key"))); + } + } + }) + } else { + None + }; + + let result = fields_names.iter().map(|(field, name)| { + let member = &field.member; + if field.attrs.skip_deserializing() { + let value = Expr(expr_is_missing(field, cattrs)); + quote!(#member: #value) + } else { + quote!(#member: #name) + } + }); + + let let_default = match cattrs.default() { + attr::Default::Default => Some(quote!( + let __default: Self::Value = _serde::#private::Default::default(); + )), + // 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 => { + // We don't need the default value, to prevent an unused variable warning + // we'll leave the line empty. + None + } + }; + + let mut result = quote!(#struct_path { #(#result),* }); + if params.has_getter { + let this_type = ¶ms.this_type; + let (_, ty_generics, _) = params.generics.split_for_impl(); + result = quote! { + _serde::#private::Into::<#this_type #ty_generics>::into(#result) + }; + } + + quote_block! { + #(#let_values)* + + #let_collect + + #match_keys + + #let_default + + #(#extract_values)* + + #(#extract_collected)* + + #collected_deny_unknown_fields + + _serde::#private::Ok(#result) + } +} + +/// Generates `Deserialize::deserialize_in_place` body for a `struct Struct {...}` +#[cfg(feature = "deserialize_in_place")] +pub(super) fn deserialize_in_place( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> Option { + // for now we do not support in_place deserialization for structs that + // are represented as map. + if has_flatten(fields) { + return None; + } + + let this_type = ¶ms.this_type; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + let expecting = format!("struct {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + let deserialized_fields: Vec<_> = fields + .iter() + .enumerate() + .filter(|&(_, field)| !field.attrs.skip_deserializing()) + .map(|(i, field)| FieldWithAliases { + ident: field_i(i), + aliases: field.attrs.aliases(), + }) + .collect(); + + let field_visitor = deserialize_field_identifier(&deserialized_fields, cattrs, false); + + 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 = 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(); + let in_place_ty_generics = de_ty_generics.in_place(); + let place_life = place_lifetime(); + + Some(quote_block! { + #field_visitor + + #[doc(hidden)] + struct __Visitor #in_place_impl_generics #where_clause { + place: &#place_life mut #this_type #ty_generics, + 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 = (); + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #[inline] + fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::#private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + + #[inline] + fn visit_map<__A>(self, mut __map: __A) -> _serde::#private::Result + where + __A: _serde::de::MapAccess<#delife>, + { + #visit_map + } + } + + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + + _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, __Visitor { + place: __place, + lifetime: _serde::#private::PhantomData, + }) + }) +} + +#[cfg(feature = "deserialize_in_place")] +fn deserialize_map_in_place( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> Fragment { + 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 + .iter() + .enumerate() + .map(|(i, field)| (field, field_i(i))) + .collect(); + + // For deserialize_in_place, declare booleans for each field that will be + // deserialized. + let let_flags = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map(|(_, name)| { + quote! { + let mut #name: bool = false; + } + }); + + // Match arms to extract a value for a field. + let value_arms_from = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map(|(field, name)| { + let deser_name = field.attrs.name().deserialize_name(); + let member = &field.member; + + let visit = match field.attrs.deserialize_with() { + None => { + quote! { + _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::InPlaceSeed(&mut self.place.#member))? + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); + quote!({ + #wrapper + self.place.#member = match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) { + _serde::#private::Ok(__wrapper) => __wrapper.value, + _serde::#private::Err(__err) => { + return _serde::#private::Err(__err); + } + }; + }) + } + }; + quote! { + __Field::#name => { + if #name { + return _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); + } + #visit; + #name = true; + } + } + }); + + // Visit ignored values to consume them + let ignored_arm = if cattrs.deny_unknown_fields() { + None + } else { + Some(quote! { + _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; } + }) + }; + + let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); + + let match_keys = if cattrs.deny_unknown_fields() && all_skipped { + quote! { + // FIXME: Once feature(exhaustive_patterns) is stable: + // let _serde::#private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; + _serde::#private::Option::map( + _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, + |__impossible| match __impossible {}); + } + } else { + quote! { + while let _serde::#private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + #(#value_arms_from)* + #ignored_arm + } + } + } + }; + + let check_flags = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map(|(field, name)| { + let missing_expr = expr_is_missing(field, cattrs); + // If missing_expr unconditionally returns an error, don't try + // to assign its value to self.place. + if field.attrs.default().is_none() + && cattrs.default().is_none() + && field.attrs.deserialize_with().is_some() + { + let missing_expr = Stmts(missing_expr); + quote! { + if !#name { + #missing_expr; + } + } + } else { + let member = &field.member; + let missing_expr = Expr(missing_expr); + quote! { + if !#name { + self.place.#member = #missing_expr; + }; + } + } + }); + + let this_type = ¶ms.this_type; + let (_, ty_generics, _) = params.generics.split_for_impl(); + + let let_default = match cattrs.default() { + attr::Default::Default => Some(quote!( + let __default: #this_type #ty_generics = _serde::#private::Default::default(); + )), + // 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 => { + // We don't need the default value, to prevent an unused variable warning + // we'll leave the line empty. + None + } + }; + + quote_block! { + #(#let_flags)* + + #match_keys + + #let_default + + #(#check_flags)* + + _serde::#private::Ok(()) + } +} + +/// Generates enum and its `Deserialize` implementation that represents each +/// non-skipped field of the struct +fn deserialize_field_identifier( + deserialized_fields: &[FieldWithAliases], + cattrs: &attr::Container, + has_flatten: bool, +) -> Stmts { + 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)) + } else if cattrs.deny_unknown_fields() { + (None, None) + } else { + let ignore_variant = quote!(__ignore,); + let fallthrough = quote!(_serde::#private::Ok(__Field::__ignore)); + (Some(ignore_variant), Some(fallthrough)) + }; + + Stmts(identifier::deserialize_generated( + deserialized_fields, + has_flatten, + false, + ignore_variant, + fallthrough, + )) +} diff --git a/serde_derive/src/de/tuple.rs b/serde_derive/src/de/tuple.rs new file mode 100644 index 00000000..a76089fe --- /dev/null +++ b/serde_derive/src/de/tuple.rs @@ -0,0 +1,283 @@ +use crate::de::{deserialize_seq, has_flatten, Parameters, TupleForm}; +#[cfg(feature = "deserialize_in_place")] +use crate::de::{deserialize_seq_in_place, place_lifetime}; +use crate::fragment::{Fragment, Stmts}; +use crate::internals::ast::Field; +use crate::internals::attr; +use crate::private; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +/// Generates `Deserialize::deserialize` body for a `struct Tuple(...);` including `struct Newtype(T);` +pub(super) fn deserialize( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, + form: TupleForm, +) -> Fragment { + assert!( + !has_flatten(fields), + "tuples and tuple variants cannot have flatten fields" + ); + + let field_count = fields + .iter() + .filter(|field| !field.attrs.skip_deserializing()) + .count(); + + let this_type = ¶ms.this_type; + let this_value = ¶ms.this_value; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + // If there are getters (implying private fields), construct the local type + // and use an `Into` conversion to get the remote type. If there are no + // getters then construct the target type directly. + let construct = if params.has_getter { + let local = ¶ms.local; + quote!(#local) + } else { + quote!(#this_value) + }; + + let type_path = match form { + TupleForm::Tuple => construct, + TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident) => { + quote!(#construct::#variant_ident) + } + }; + let expecting = match form { + TupleForm::Tuple => format!("tuple struct {}", params.type_name()), + TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident) => { + format!("tuple variant {}::{}", params.type_name(), variant_ident) + } + }; + let expecting = cattrs.expecting().unwrap_or(&expecting); + + let nfields = fields.len(); + + let visit_newtype_struct = match form { + TupleForm::Tuple if nfields == 1 => { + Some(deserialize_newtype_struct(&type_path, params, &fields[0])) + } + _ => None, + }; + + let visit_seq = Stmts(deserialize_seq( + &type_path, params, fields, false, cattrs, expecting, + )); + + let visitor_expr = quote! { + __Visitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + } + }; + let dispatch = match form { + TupleForm::Tuple if nfields == 1 => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr) + } + } + TupleForm::Tuple => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr) + } + } + TupleForm::ExternallyTagged(_) => quote! { + _serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr) + }, + TupleForm::Untagged(_) => quote! { + _serde::Deserializer::deserialize_tuple(__deserializer, #field_count, #visitor_expr) + }, + }; + + let visitor_var = if field_count == 0 { + quote!(_) + } else { + quote!(mut __seq) + }; + + quote_block! { + #[doc(hidden)] + struct __Visitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #visit_newtype_struct + + #[inline] + fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::#private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + } + + #dispatch + } +} + +fn deserialize_newtype_struct( + type_path: &TokenStream, + params: &Parameters, + field: &Field, +) -> 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(#deserializer_var)? + } + } + Some(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(with = "...")] + // ^^^^^ + quote_spanned! {path.span()=> + #path(#deserializer_var)? + } + } + }; + + let mut result = quote!(#type_path(__field0)); + if params.has_getter { + let this_type = ¶ms.this_type; + let (_, ty_generics, _) = params.generics.split_for_impl(); + result = quote! { + _serde::#private::Into::<#this_type #ty_generics>::into(#result) + }; + } + + quote! { + #[inline] + fn visit_newtype_struct<__E>(self, #deserializer_var: __E) -> _serde::#private::Result + where + __E: _serde::Deserializer<#delife>, + { + let __field0: #field_ty = #value; + _serde::#private::Ok(#result) + } + } +} + +/// Generates `Deserialize::deserialize_in_place` body for a `struct Tuple(...);` including `struct Newtype(T);` +#[cfg(feature = "deserialize_in_place")] +pub(super) fn deserialize_in_place( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> Fragment { + assert!( + !has_flatten(fields), + "tuples and tuple variants cannot have flatten fields" + ); + + let field_count = fields + .iter() + .filter(|field| !field.attrs.skip_deserializing()) + .count(); + + let this_type = ¶ms.this_type; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + let expecting = format!("tuple struct {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + let nfields = fields.len(); + + let visit_newtype_struct = if nfields == 1 { + // We do not generate deserialize_in_place if every field has a + // deserialize_with. + assert!(fields[0].attrs.deserialize_with().is_none()); + + Some(quote! { + #[inline] + fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::#private::Result + where + __E: _serde::Deserializer<#delife>, + { + _serde::Deserialize::deserialize_in_place(__e, &mut self.place.0) + } + }) + } else { + None + }; + + let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting)); + + let visitor_expr = quote! { + __Visitor { + place: __place, + lifetime: _serde::#private::PhantomData, + } + }; + + let type_name = cattrs.name().deserialize_name(); + let dispatch = if nfields == 1 { + quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr)) + } else { + quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr)) + }; + + let visitor_var = if field_count == 0 { + quote!(_) + } else { + quote!(mut __seq) + }; + + let in_place_impl_generics = de_impl_generics.in_place(); + let in_place_ty_generics = de_ty_generics.in_place(); + let place_life = place_lifetime(); + + quote_block! { + #[doc(hidden)] + struct __Visitor #in_place_impl_generics #where_clause { + place: &#place_life mut #this_type #ty_generics, + 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 = (); + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #visit_newtype_struct + + #[inline] + fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::#private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + } + + #dispatch + } +} diff --git a/serde_derive/src/de/unit.rs b/serde_derive/src/de/unit.rs new file mode 100644 index 00000000..0d15ef4d --- /dev/null +++ b/serde_derive/src/de/unit.rs @@ -0,0 +1,52 @@ +use crate::de::Parameters; +use crate::fragment::Fragment; +use crate::internals::attr; +use crate::private; +use quote::quote; + +/// Generates `Deserialize::deserialize` body for a `struct Unit;` +pub(super) fn deserialize(params: &Parameters, cattrs: &attr::Container) -> Fragment { + let this_type = ¶ms.this_type; + let this_value = ¶ms.this_value; + let type_name = cattrs.name().deserialize_name(); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + params.generics_with_de_lifetime(); + let delife = params.borrowed.de_lifetime(); + + let expecting = format!("unit struct {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + quote_block! { + #[doc(hidden)] + struct __Visitor #de_impl_generics #where_clause { + marker: _serde::#private::PhantomData<#this_type #ty_generics>, + 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; + + fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result { + _serde::#private::Formatter::write_str(__formatter, #expecting) + } + + #[inline] + fn visit_unit<__E>(self) -> _serde::#private::Result + where + __E: _serde::de::Error, + { + _serde::#private::Ok(#this_value) + } + } + + _serde::Deserializer::deserialize_unit_struct( + __deserializer, + #type_name, + __Visitor { + marker: _serde::#private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::#private::PhantomData, + }, + ) + } +} diff --git a/serde_derive/src/deprecated.rs b/serde_derive/src/deprecated.rs new file mode 100644 index 00000000..3abdc1b2 --- /dev/null +++ b/serde_derive/src/deprecated.rs @@ -0,0 +1,56 @@ +use proc_macro2::TokenStream; +use quote::quote; + +pub fn allow_deprecated(input: &syn::DeriveInput) -> Option { + if should_allow_deprecated(input) { + Some(quote! { #[allow(deprecated)] }) + } else { + None + } +} + +/// Determine if an `#[allow(deprecated)]` should be added to the derived impl. +/// +/// This should happen if the derive input or an enum variant it contains has +/// one of: +/// - `#[deprecated]` +/// - `#[allow(deprecated)]` +fn should_allow_deprecated(input: &syn::DeriveInput) -> bool { + if contains_deprecated(&input.attrs) { + return true; + } + if let syn::Data::Enum(data_enum) = &input.data { + for variant in &data_enum.variants { + if contains_deprecated(&variant.attrs) { + return true; + } + } + } + false +} + +/// Check whether the given attributes contains one of: +/// - `#[deprecated]` +/// - `#[allow(deprecated)]` +fn contains_deprecated(attrs: &[syn::Attribute]) -> bool { + for attr in attrs { + if attr.path().is_ident("deprecated") { + return true; + } + if let syn::Meta::List(meta_list) = &attr.meta { + if meta_list.path.is_ident("allow") { + let mut allow_deprecated = false; + let _ = meta_list.parse_nested_meta(|meta| { + if meta.path.is_ident("deprecated") { + allow_deprecated = true; + } + Ok(()) + }); + if allow_deprecated { + return true; + } + } + } + } + false +} diff --git a/serde_derive/src/dummy.rs b/serde_derive/src/dummy.rs index 095f950f..e0bca647 100644 --- a/serde_derive/src/dummy.rs +++ b/serde_derive/src/dummy.rs @@ -14,9 +14,17 @@ pub fn wrap_in_const(serde_path: Option<&syn::Path>, code: TokenStream) -> Token quote! { #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + #[allow( + non_upper_case_globals, + unused_attributes, + unused_qualifications, + clippy::absolute_paths, + )] const _: () = { #use_serde + + _serde::__require_serde_not_serde_core!(); + #code }; } diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index a28d3ae7..5a760c65 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -1,6 +1,7 @@ //! A Serde ast, parsed from the Syn ast and ready to generate Rust code. use crate::internals::{attr, check, Ctxt, Derive}; +use proc_macro2::Ident; use syn::punctuated::Punctuated; use syn::Token; @@ -62,13 +63,17 @@ impl<'a> Container<'a> { cx: &Ctxt, item: &'a syn::DeriveInput, derive: Derive, + private: &Ident, ) -> 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())), + syn::Data::Enum(data) => { + Data::Enum(enum_from_ast(cx, &data.variants, attrs.default(), private)) + } syn::Data::Struct(data) => { - let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default()); + let (style, fields) = + struct_from_ast(cx, &data.fields, None, attrs.default(), private); Data::Struct(style, fields) } syn::Data::Union(_) => { @@ -77,15 +82,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 +98,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, @@ -140,13 +134,19 @@ fn enum_from_ast<'a>( cx: &Ctxt, variants: &'a Punctuated, container_default: &attr::Default, + private: &Ident, ) -> Vec> { let variants: Vec = variants .iter() .map(|variant| { let attrs = attr::Variant::from_ast(cx, variant); - let (style, fields) = - struct_from_ast(cx, &variant.fields, Some(&attrs), container_default); + let (style, fields) = struct_from_ast( + cx, + &variant.fields, + Some(&attrs), + container_default, + private, + ); Variant { ident: variant.ident.clone(), attrs, @@ -176,19 +176,20 @@ fn struct_from_ast<'a>( fields: &'a syn::Fields, attrs: Option<&attr::Variant>, container_default: &attr::Default, + private: &Ident, ) -> (Style, Vec>) { match fields { syn::Fields::Named(fields) => ( Style::Struct, - fields_from_ast(cx, &fields.named, attrs, container_default), + fields_from_ast(cx, &fields.named, attrs, container_default, private), ), syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => ( Style::Newtype, - fields_from_ast(cx, &fields.unnamed, attrs, container_default), + fields_from_ast(cx, &fields.unnamed, attrs, container_default, private), ), syn::Fields::Unnamed(fields) => ( Style::Tuple, - fields_from_ast(cx, &fields.unnamed, attrs, container_default), + fields_from_ast(cx, &fields.unnamed, attrs, container_default, private), ), syn::Fields::Unit => (Style::Unit, Vec::new()), } @@ -199,6 +200,7 @@ fn fields_from_ast<'a>( fields: &'a Punctuated, attrs: Option<&attr::Variant>, container_default: &attr::Default, + private: &Ident, ) -> Vec> { fields .iter() @@ -208,7 +210,7 @@ fn fields_from_ast<'a>( Some(ident) => syn::Member::Named(ident.clone()), None => syn::Member::Unnamed(i.into()), }, - attrs: attr::Field::from_ast(cx, i, field, attrs, container_default), + attrs: attr::Field::from_ast(cx, i, field, attrs, container_default, private), ty: &field.ty, original: field, }) diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index bb9de328..f982f03a 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -1,14 +1,15 @@ +use crate::internals::name::{MultiName, Name}; use crate::internals::symbol::*; use crate::internals::{ungroup, Ctxt}; use proc_macro2::{Spacing, Span, TokenStream, TokenTree}; use quote::ToTokens; -use std::borrow::Cow; use std::collections::BTreeSet; use std::iter::FromIterator; use syn::meta::ParseNestedMeta; use syn::parse::ParseStream; use syn::punctuated::Punctuated; -use syn::{parse_quote, token, Ident, Lifetime, Token}; +use syn::spanned::Spanned; +use syn::{token, Ident, Lifetime, Token}; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints // are `attr::Container::from_ast`, `attr::Variant::from_ast`, and @@ -20,7 +21,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 +62,7 @@ impl<'c, T> Attr<'c, T> { } } - fn get(self) -> Option { + pub(crate) fn get(self) -> Option { self.value } @@ -89,7 +90,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 +125,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 +153,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 +167,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 +277,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 +517,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 +537,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 +544,7 @@ impl Container { } } - pub fn name(&self) -> &Name { + pub fn name(&self) -> &MultiName { &self.name } @@ -655,23 +604,10 @@ 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() } - pub fn serde_path(&self) -> Cow { - self.custom_serde_path() - .map_or_else(|| Cow::Owned(parse_quote!(_serde)), Cow::Borrowed) - } - /// Error message generated when type can't be deserialized. /// If `None`, default message will be used pub fn expecting(&self) -> Option<&str> { @@ -790,7 +726,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 +777,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 +834,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 +892,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 +914,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 +976,7 @@ impl Variant { /// Represents field attribute information pub struct Field { - name: Name, + name: MultiName, skip_serializing: bool, skip_deserializing: bool, skip_serializing_if: Option, @@ -1074,6 +1018,7 @@ impl Field { field: &syn::Field, attrs: Option<&Variant>, container_default: &Default, + private: &Ident, ) -> Self { let mut ser_name = Attr::none(cx, RENAME); let mut de_name = Attr::none(cx, RENAME); @@ -1091,8 +1036,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 +1076,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 +1128,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 { @@ -1264,7 +1212,7 @@ impl Field { }; let span = Span::call_site(); path.segments.push(Ident::new("_serde", span).into()); - path.segments.push(Ident::new("__private", span).into()); + path.segments.push(private.clone().into()); path.segments.push(Ident::new("de", span).into()); path.segments .push(Ident::new("borrow_cow_str", span).into()); @@ -1281,7 +1229,7 @@ impl Field { }; let span = Span::call_site(); path.segments.push(Ident::new("_serde", span).into()); - path.segments.push(Ident::new("__private", span).into()); + path.segments.push(private.clone().into()); path.segments.push(Ident::new("de", span).into()); path.segments .push(Ident::new("borrow_cow_bytes", span).into()); @@ -1299,7 +1247,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 +1263,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 +1728,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(); diff --git a/serde_derive/src/internals/check.rs b/serde_derive/src/internals/check.rs index 52b0f379..da2a0fbd 100644 --- a/serde_derive/src/internals/check.rs +++ b/serde_derive/src/internals/check.rs @@ -329,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 f98ef08e..cd1e8105 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 00000000..4c59f963 --- /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 fa2a77d2..a326f667 100644 --- a/serde_derive/src/internals/receiver.rs +++ b/serde_derive/src/internals/receiver.rs @@ -2,7 +2,6 @@ use crate::internals::respan::respan; use proc_macro2::Span; use quote::ToTokens; use std::mem; -use syn::punctuated::Punctuated; use syn::{ parse_quote, Data, DeriveInput, Expr, ExprPath, GenericArgument, GenericParam, Generics, Macro, Path, PathArguments, QSelf, ReturnType, Token, Type, TypeParamBound, TypePath, WherePredicate, @@ -49,7 +48,7 @@ impl ReplaceReceiver<'_> { path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap()); - let segments = mem::replace(&mut path.segments, Punctuated::new()); + let segments = mem::take(&mut path.segments); path.segments = segments.into_pairs().skip(1).collect(); } @@ -84,7 +83,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` @@ -209,7 +208,9 @@ impl ReplaceReceiver<'_> { 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(_) => {} + TypeParamBound::Lifetime(_) + | TypeParamBound::PreciseCapture(_) + | TypeParamBound::Verbatim(_) => {} _ => {} } } diff --git a/serde_derive/src/internals/symbol.rs b/serde_derive/src/internals/symbol.rs index 572391a8..59ef8de7 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 b91f17b1..f2c3048b 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.197")] +#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.228")] +#![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, @@ -39,6 +41,7 @@ clippy::cast_possible_truncation, clippy::checked_conversions, clippy::doc_markdown, + clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::indexing_slicing, clippy::items_after_statements, @@ -54,12 +57,14 @@ 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, clippy::wildcard_imports )] #![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))] +#![allow(unknown_lints, mismatched_lifetime_syntaxes)] extern crate proc_macro2; extern crate quote; @@ -70,6 +75,8 @@ extern crate proc_macro; mod internals; use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::{ToTokens, TokenStreamExt as _}; use syn::parse_macro_input; use syn::DeriveInput; @@ -79,11 +86,30 @@ mod bound; mod fragment; mod de; +mod deprecated; mod dummy; mod pretend; mod ser; mod this; +#[allow(non_camel_case_types)] +struct private; + +impl private { + fn ident(&self) -> Ident { + Ident::new( + concat!("__private", "228"), + Span::call_site(), + ) + } +} + +impl ToTokens for private { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + tokens.append(self.ident()); + } +} + #[proc_macro_derive(Serialize, attributes(serde))] pub fn derive_serialize(input: TokenStream) -> TokenStream { let mut input = parse_macro_input!(input as DeriveInput); diff --git a/serde_derive/src/pretend.rs b/serde_derive/src/pretend.rs index 2c9e7793..cfa1a87d 100644 --- a/serde_derive/src/pretend.rs +++ b/serde_derive/src/pretend.rs @@ -1,4 +1,5 @@ use crate::internals::ast::{Container, Data, Field, Style, Variant}; +use crate::private; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -83,8 +84,8 @@ fn pretend_fields_used_struct(cont: &Container, fields: &[Field]) -> TokenStream let placeholders = (0usize..).map(|i| format_ident!("__v{}", i)); quote! { - match _serde::__private::None::<&#type_ident #ty_generics> { - _serde::__private::Some(#type_ident { #(#members: #placeholders),* }) => {} + match _serde::#private::None::<&#type_ident #ty_generics> { + _serde::#private::Some(#type_ident { #(#members: #placeholders),* }) => {} _ => {} } } @@ -96,11 +97,12 @@ fn pretend_fields_used_struct_packed(cont: &Container, fields: &[Field]) -> Toke let members = fields.iter().map(|field| &field.member).collect::>(); + let private2 = private; quote! { - match _serde::__private::None::<&#type_ident #ty_generics> { - _serde::__private::Some(__v @ #type_ident { #(#members: _),* }) => { + match _serde::#private::None::<&#type_ident #ty_generics> { + _serde::#private::Some(__v @ #type_ident { #(#members: _),* }) => { #( - let _ = _serde::__private::ptr::addr_of!(__v.#members); + let _ = _serde::#private2::ptr::addr_of!(__v.#members); )* } _ => {} @@ -125,10 +127,11 @@ fn pretend_fields_used_enum(cont: &Container, variants: &[Variant]) -> TokenStre }) .collect::>(); + let private2 = private; quote! { - match _serde::__private::None::<&#type_ident #ty_generics> { + match _serde::#private::None::<&#type_ident #ty_generics> { #( - _serde::__private::Some(#patterns) => {} + _serde::#private2::Some(#patterns) => {} )* _ => {} } @@ -172,8 +175,8 @@ fn pretend_variants_used(cont: &Container) -> TokenStream { }; quote! { - match _serde::__private::None { - _serde::__private::Some((#(#placeholders,)*)) => { + match _serde::#private::None { + _serde::#private::Some((#(#placeholders,)*)) => { let _ = #type_ident::#variant_ident #turbofish #pat; } _ => {} diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 3be51ee5..ad1fb174 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1,7 +1,9 @@ +use crate::deprecated::allow_deprecated; 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 crate::{bound, dummy, pretend, private, this}; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; @@ -11,7 +13,7 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result cont, None => return Err(ctxt.check().unwrap_err()), }; @@ -22,16 +24,18 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result(__self: &#remote #ty_generics, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> + #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error> where - __S: #serde::Serializer, + __S: _serde::Serializer, { #used #body @@ -41,10 +45,11 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result(&self, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> + #allow_deprecated + impl #impl_generics _serde::Serialize for #ident #ty_generics #where_clause { + fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error> where - __S: #serde::Serializer, + __S: _serde::Serializer, { #body } @@ -148,7 +153,7 @@ fn build_generics(cont: &Container) -> syn::Generics { } // Fields with a `skip_serializing` or `serialize_with` attribute, or which -// belong to a variant with a 'skip_serializing` or `serialize_with` attribute, +// belong to a variant with a `skip_serializing` or `serialize_with` attribute, // are not serialized by us so we do not generate a bound. Fields with a `bound` // attribute specify their own bound so we do not generate one. All other fields // may need a `T: Serialize` bound where T is the type of the field. @@ -210,7 +215,7 @@ fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment { let self_var = ¶ms.self_var; quote_block! { _serde::Serialize::serialize( - &_serde::__private::Into::<#type_into>::into(_serde::__private::Clone::clone(#self_var)), + &_serde::#private::Into::<#type_into>::into(_serde::#private::Clone::clone(#self_var)), __serializer) } } @@ -289,9 +294,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 +384,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 +393,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; @@ -411,7 +407,7 @@ fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Cont if cattrs.remote().is_some() && cattrs.non_exhaustive() { arms.push(quote! { - ref unrecognized => _serde::__private::Err(_serde::ser::Error::custom(_serde::__private::ser::CannotSerializeVariant(unrecognized))), + ref unrecognized => _serde::#private::Err(_serde::ser::Error::custom(_serde::#private::ser::CannotSerializeVariant(unrecognized))), }); } @@ -438,7 +434,7 @@ fn serialize_variant( variant_ident ); let skipped_err = quote! { - _serde::__private::Err(_serde::ser::Error::custom(#skipped_msg)) + _serde::#private::Err(_serde::ser::Error::custom(#skipped_msg)) }; let fields_pat = match variant.style { Style::Unit => quote!(), @@ -592,7 +588,7 @@ fn serialize_internally_tagged_variant( if let Some(path) = variant.attrs.serialize_with() { let ser = wrap_serialize_variant_with(params, path, variant); return quote_expr! { - _serde::__private::ser::serialize_tagged_newtype( + _serde::#private::ser::serialize_tagged_newtype( __serializer, #enum_ident_str, #variant_ident_str, @@ -621,7 +617,7 @@ fn serialize_internally_tagged_variant( } let span = field.original.span(); - let func = quote_spanned!(span=> _serde::__private::ser::serialize_tagged_newtype); + let func = quote_spanned!(span=> _serde::#private::ser::serialize_tagged_newtype); quote_expr! { #func( __serializer, @@ -655,7 +651,7 @@ fn serialize_adjacently_tagged_variant( let type_name = cattrs.name().serialize_name(); let variant_name = variant.attrs.name().serialize_name(); let serialize_variant = quote! { - &_serde::__private::ser::AdjacentlyTaggedEnumVariant { + &_serde::#private::ser::AdjacentlyTaggedEnumVariant { enum_name: #type_name, variant_index: #variant_index, variant_name: #variant_name, @@ -738,11 +734,12 @@ fn serialize_adjacently_tagged_variant( #[doc(hidden)] struct __AdjacentlyTagged #wrapper_generics #where_clause { data: (#(&'__a #fields_ty,)*), - phantom: _serde::__private::PhantomData<#this_type #ty_generics>, + 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> + fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error> where __S: _serde::Serializer, { @@ -760,7 +757,7 @@ fn serialize_adjacently_tagged_variant( _serde::ser::SerializeStruct::serialize_field( &mut __struct, #content, &__AdjacentlyTagged { data: (#(#fields_ident,)*), - phantom: _serde::__private::PhantomData::<#this_type #ty_generics>, + phantom: _serde::#private::PhantomData::<#this_type #ty_generics>, })?; _serde::ser::SerializeStruct::end(__struct) } @@ -807,9 +804,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 +873,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 +886,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 +970,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); @@ -1002,18 +999,19 @@ fn serialize_struct_variant_with_flatten( #[doc(hidden)] struct __EnumFlatten #wrapper_generics #where_clause { data: (#(&'__a #fields_ty,)*), - phantom: _serde::__private::PhantomData<#this_type #ty_generics>, + 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> + fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error> where __S: _serde::Serializer, { let (#(#members,)*) = self.data; let #let_mut __serde_state = _serde::Serializer::serialize_map( __serializer, - _serde::__private::None)?; + _serde::#private::None)?; #(#serialize_fields)* _serde::ser::SerializeMap::end(__serde_state) } @@ -1026,7 +1024,7 @@ fn serialize_struct_variant_with_flatten( #variant_name, &__EnumFlatten { data: (#(#members,)*), - phantom: _serde::__private::PhantomData::<#this_type #ty_generics>, + phantom: _serde::#private::PhantomData::<#this_type #ty_generics>, }) } } @@ -1034,7 +1032,7 @@ fn serialize_struct_variant_with_flatten( quote_block! { let #let_mut __serde_state = _serde::Serializer::serialize_map( __serializer, - _serde::__private::None)?; + _serde::#private::None)?; _serde::ser::SerializeMap::serialize_entry( &mut __serde_state, #tag, @@ -1048,7 +1046,7 @@ fn serialize_struct_variant_with_flatten( quote_block! { let #let_mut __serde_state = _serde::Serializer::serialize_map( __serializer, - _serde::__private::None)?; + _serde::#private::None)?; #(#serialize_fields)* _serde::ser::SerializeMap::end(__serde_state) } @@ -1137,7 +1135,7 @@ fn serialize_struct_visitor( let ser = if field.attrs.flatten() { let func = quote_spanned!(span=> _serde::Serialize::serialize); quote! { - #func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?; + #func(&#field_expr, _serde::#private::ser::FlatMapSerializer(&mut __serde_state))?; } } else { let func = struct_trait.serialize_field(span); @@ -1229,25 +1227,37 @@ fn wrap_serialize_with( }) }); - quote!({ + 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 { values: (#(&'__a #field_tys, )*), - phantom: _serde::__private::PhantomData<#this_type #ty_generics>, + 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 } } - &__SerializeWith { + __SerializeWith { values: (#(#field_exprs, )*), - phantom: _serde::__private::PhantomData::<#this_type #ty_generics>, + phantom: _serde::#private::PhantomData::<#this_type #ty_generics>, } }) } @@ -1283,11 +1293,11 @@ fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStrea quote!(&#self_var.#member) }; let ty = field.ty; - quote!(_serde::__private::ser::constrain::<#ty>(#inner)) + quote!(_serde::#private::ser::constrain::<#ty>(#inner)) } (true, Some(getter)) => { let ty = field.ty; - quote!(_serde::__private::ser::constrain::<#ty>(&#getter(#self_var))) + quote!(_serde::#private::ser::constrain::<#ty>(&#getter(#self_var))) } (false, Some(_)) => { unreachable!("getter is only allowed for remote impls"); diff --git a/serde_derive_internals/Cargo.toml b/serde_derive_internals/Cargo.toml index 6ce27797..abcc11dd 100644 --- a/serde_derive_internals/Cargo.toml +++ b/serde_derive_internals/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "serde_derive_internals" -version = "0.29.0" +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 = "2021" 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" @@ -21,4 +22,11 @@ syn = { workspace = true, features = ["clone-impls", "derive", "parsing", "print [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "--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 25b5ef31..b2bbb3a5 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 1053b009..73680082 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, @@ -19,6 +21,7 @@ // Ignored clippy_pedantic lints #![allow( clippy::doc_markdown, + clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::items_after_statements, clippy::let_underscore_untyped, @@ -34,9 +37,11 @@ clippy::single_match_else, clippy::struct_excessive_bools, clippy::too_many_lines, + clippy::uninlined_format_args, clippy::unused_self, clippy::wildcard_imports )] +#![allow(unknown_lints, mismatched_lifetime_syntaxes)] extern crate proc_macro2; extern crate quote; diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index b7686baf..df1cdc37 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -2,7 +2,7 @@ name = "serde_test_suite" version = "0.0.0" authors = ["Erick Tryzelaar ", "David Tolnay "] -edition = "2018" +edition = "2021" publish = false [features] @@ -13,9 +13,9 @@ serde = { path = "../serde" } [dev-dependencies] automod = "1.0.1" -fnv = "1.0" +foldhash = "0.2" 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.108", features = ["diff"] } diff --git a/test_suite/no_std/.gitignore b/test_suite/no_std/.gitignore new file mode 100644 index 00000000..e9e21997 --- /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 5bf7eb2d..7909a2d6 100644 --- a/test_suite/no_std/Cargo.toml +++ b/test_suite/no_std/Cargo.toml @@ -2,7 +2,7 @@ name = "serde_derive_tests_no_std" version = "0.0.0" authors = ["David Tolnay "] -edition = "2018" +edition = "2021" publish = false [dependencies] @@ -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 57b3f315..71d553e1 100644 --- a/test_suite/no_std/src/main.rs +++ b/test_suite/no_std/src/main.rs @@ -1,16 +1,13 @@ -#![allow(internal_features)] -#![feature(lang_items, start)] #![no_std] +#![no_main] -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { +use core::ffi::c_int; + +#[no_mangle] +extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int { 0 } -#[lang = "eh_personality"] -#[no_mangle] -pub extern "C" fn rust_eh_personality() {} - #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { @@ -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 621660b0..8ab6342f 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 d6632446..44b98ae2 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 00000000..b1d5c731 --- /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 e4832af3..668ee5c7 100644 --- a/test_suite/tests/regression/issue2371.rs +++ b/test_suite/tests/regression/issue2371.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use serde_derive::Deserialize; #[derive(Deserialize)] @@ -7,7 +9,9 @@ pub struct Nested; pub enum ExternallyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -17,7 +21,9 @@ pub enum ExternallyTagged { pub enum InternallyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -27,7 +33,9 @@ pub enum InternallyTagged { pub enum AdjacentlyTagged { Flatten { #[serde(flatten)] + #[allow(dead_code)] nested: Nested, + #[allow(dead_code)] string: &'static str, }, } @@ -37,7 +45,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/issue2409.rs b/test_suite/tests/regression/issue2409.rs index cdfe4dc5..335344a2 100644 --- a/test_suite/tests/regression/issue2409.rs +++ b/test_suite/tests/regression/issue2409.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use serde_derive::Deserialize; macro_rules! bug { diff --git a/test_suite/tests/regression/issue2415.rs b/test_suite/tests/regression/issue2415.rs index 31c52270..58a6b60b 100644 --- a/test_suite/tests/regression/issue2415.rs +++ b/test_suite/tests/regression/issue2415.rs @@ -2,4 +2,5 @@ use serde_derive::Serialize; #[derive(Serialize)] #[serde()] +#[allow(dead_code)] pub struct S; diff --git a/test_suite/tests/regression/issue2565.rs b/test_suite/tests/regression/issue2565.rs new file mode 100644 index 00000000..f13cacf0 --- /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 00000000..a8c1604c --- /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 00000000..024e62fb --- /dev/null +++ b/test_suite/tests/regression/issue2844.rs @@ -0,0 +1,33 @@ +#![allow(clippy::trivially_copy_pass_by_ref, dead_code)] + +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 00000000..4a6d3953 --- /dev/null +++ b/test_suite/tests/regression/issue2846.rs @@ -0,0 +1,27 @@ +#![allow(clippy::trivially_copy_pass_by_ref, dead_code)] + +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 566f7d43..3f02a61f 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)] @@ -2410,133 +1793,6 @@ fn test_partially_untagged_internally_tagged_enum() { // TODO test error output } -#[test] -fn test_partially_untagged_adjacently_tagged_enum() { - #[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 test_flatten_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 test_flatten_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 test_transparent_struct() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2575,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)] @@ -2785,65 +1895,6 @@ fn test_expecting_message_externally_tagged_enum() { ); } -#[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], - "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], - "invalid type: unit value, expected variant of enum Enum", - ); -} - -#[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")], "something strange..."); -} - #[test] fn test_expecting_message_identifier_enum() { #[derive(Deserialize)] @@ -2894,6 +1945,660 @@ fn test_expecting_message_identifier_enum() { 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::*; @@ -2901,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)] @@ -2912,30 +2655,84 @@ mod flatten { #[derive(Debug, PartialEq, Serialize, Deserialize)] enum Enum { + Unit, Newtype(HashMap), Tuple(u32, u32), Struct { index: u32, value: u32 }, } #[test] - fn newtype() { + fn unit() { + let value = Flatten { + data: Enum::Unit, + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; assert_tokens( - &Flatten { - data: Enum::Newtype(HashMap::from_iter([("key".into(), "value".into())])), - extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), - }, + &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() { + let value = Flatten { + data: Enum::Newtype(HashMap::from_iter([("key".into(), "value".into())])), + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; + assert_tokens( + &value, + &[ + Token::Map { len: None }, + // data Token::Str("Newtype"), // variant Token::Map { len: Some(1) }, Token::Str("key"), Token::Str("value"), Token::MapEnd, + // 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("Newtype"), // variant + Token::Map { len: Some(1) }, + Token::Str("key"), + Token::Str("value"), + Token::MapEnd, + Token::MapEnd, + ], + ); } // Reaches crate::private::de::content::VariantDeserializer::tuple_variant @@ -2943,23 +2740,42 @@ mod flatten { // via FlatMapDeserializer::deserialize_enum #[test] fn tuple() { + let value = Flatten { + data: Enum::Tuple(0, 42), + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; assert_tokens( - &Flatten { - data: Enum::Tuple(0, 42), - extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), - }, + &value, &[ Token::Map { len: None }, + // data Token::Str("Tuple"), // variant Token::Seq { len: Some(2) }, Token::U32(0), Token::U32(42), Token::SeqEnd, + // 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("Tuple"), // variant + Token::Seq { len: Some(2) }, + Token::U32(0), + Token::U32(42), + Token::SeqEnd, + Token::MapEnd, + ], + ); } // Reaches crate::private::de::content::VariantDeserializer::struct_variant @@ -2967,26 +2783,45 @@ mod flatten { // via FlatMapDeserializer::deserialize_enum #[test] fn struct_from_seq() { - assert_de_tokens( - &Flatten { - data: Enum::Struct { - index: 0, - value: 42, - }, - extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + let value = Flatten { + data: Enum::Struct { + index: 0, + value: 42, }, + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; + assert_de_tokens( + &value, &[ Token::Map { len: None }, + // data Token::Str("Struct"), // variant Token::Seq { len: Some(2) }, Token::U32(0), // index Token::U32(42), // value Token::SeqEnd, + // 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("Struct"), // variant + Token::Seq { len: Some(2) }, + Token::U32(0), // index + Token::U32(42), // value + Token::SeqEnd, + Token::MapEnd, + ], + ); } // Reaches crate::private::de::content::VariantDeserializer::struct_variant @@ -2994,16 +2829,18 @@ mod flatten { // via FlatMapDeserializer::deserialize_enum #[test] fn struct_from_map() { - assert_tokens( - &Flatten { - data: Enum::Struct { - index: 0, - value: 42, - }, - extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + let value = Flatten { + data: Enum::Struct { + index: 0, + value: 42, }, + extra: HashMap::from_iter([("extra_key".into(), "extra value".into())]), + }; + assert_tokens( + &value, &[ Token::Map { len: None }, + // data Token::Str("Struct"), // variant Token::Struct { len: 2, @@ -3014,11 +2851,33 @@ mod flatten { Token::Str("value"), Token::U32(42), Token::StructEnd, + // 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("Struct"), // variant + Token::Struct { + len: 2, + name: "Struct", + }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + Token::MapEnd, + ], + ); } } @@ -3039,6 +2898,7 @@ mod flatten { #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Enum { + Unit, Newtype(NewtypeVariant), Struct { index: u32, value: u32 }, } @@ -3049,24 +2909,357 @@ mod flatten { } #[test] - fn struct_() { + fn unit() { + let value = Flatten { + outer: 42, + data: NewtypeWrapper(Enum::Unit), + }; + // Field order: outer, [tag] assert_tokens( - &Flatten { - outer: 42, - data: NewtypeWrapper(Enum::Struct { - index: 0, - value: 42, - }), - }, + &value, &[ Token::Map { len: None }, + // outer Token::Str("outer"), Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // content missing + Token::MapEnd, + ], + ); + // Field order: [tag], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // content missing + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: outer, [tag, content] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // outer + Token::Str("outer"), + Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // content + Token::Str("content"), + Token::Unit, + Token::MapEnd, + ], + ); + // Field order: outer, [content, tag] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // outer + Token::Str("outer"), + Token::U32(42), + // content + Token::Str("content"), + Token::Unit, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + Token::MapEnd, + ], + ); + // Field order: [tag, content], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // content + Token::Str("content"), + Token::Unit, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [content, tag], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Unit, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [tag], outer, [content] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + // outer + Token::Str("outer"), + Token::U32(42), + // content + Token::Str("content"), + Token::Unit, + Token::MapEnd, + ], + ); + // Field order: [content], outer, [tag] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Unit, + // outer + Token::Str("outer"), + Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Unit", + }, + Token::MapEnd, + ], + ); + } + + #[test] + fn newtype() { + let value = Flatten { + outer: 42, + data: NewtypeWrapper(Enum::Newtype(NewtypeVariant { value: 23 })), + }; + // Field order: outer, [tag, content] + assert_tokens( + &value, + &[ + Token::Map { len: None }, + // outer + Token::Str("outer"), + Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + Token::MapEnd, + ], + ); + // Field order: outer, [content, tag] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // outer + Token::Str("outer"), + Token::U32(42), + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + Token::MapEnd, + ], + ); + // Field order: [tag, content], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [content, tag], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [tag], outer, [content] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + // outer + Token::Str("outer"), + Token::U32(42), + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + Token::MapEnd, + ], + ); + // Field order: [content], outer, [tag] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Struct { + len: 1, + name: "NewtypeVariant", + }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + // outer + Token::Str("outer"), + Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Newtype", + }, + Token::MapEnd, + ], + ); + } + + #[test] + fn struct_() { + let value = Flatten { + outer: 42, + data: NewtypeWrapper(Enum::Struct { + index: 0, + value: 42, + }), + }; + // Field order: outer, [tag, content] + assert_tokens( + &value, + &[ + Token::Map { len: None }, + // outer + Token::Str("outer"), + Token::U32(42), + // tag Token::Str("tag"), Token::UnitVariant { name: "Enum", variant: "Struct", }, + // content Token::Str("content"), Token::Struct { len: 2, @@ -3080,32 +3273,143 @@ mod flatten { Token::MapEnd, ], ); - } - - #[test] - fn newtype() { - assert_tokens( - &Flatten { - outer: 42, - data: NewtypeWrapper(Enum::Newtype(NewtypeVariant { value: 23 })), - }, + // Field order: outer, [content, tag] + assert_de_tokens( + &value, &[ Token::Map { len: None }, + // outer Token::Str("outer"), Token::U32(42), + // content + Token::Str("content"), + Token::Struct { + len: 2, + name: "Struct", + }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + // tag Token::Str("tag"), Token::UnitVariant { name: "Enum", - variant: "Newtype", + variant: "Struct", }, + Token::MapEnd, + ], + ); + // Field order: [tag, content], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Struct", + }, + // content Token::Str("content"), Token::Struct { - len: 1, - name: "NewtypeVariant", + len: 2, + name: "Struct", }, + Token::Str("index"), + Token::U32(0), Token::Str("value"), - Token::U32(23), + Token::U32(42), Token::StructEnd, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [content, tag], outer + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Struct { + len: 2, + name: "Struct", + }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Struct", + }, + // outer + Token::Str("outer"), + Token::U32(42), + Token::MapEnd, + ], + ); + // Field order: [tag], outer, [content] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Struct", + }, + // outer + Token::Str("outer"), + Token::U32(42), + // content + Token::Str("content"), + Token::Struct { + len: 2, + name: "Struct", + }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + Token::MapEnd, + ], + ); + // Field order: [content], outer, [tag] + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // content + Token::Str("content"), + Token::Struct { + len: 2, + name: "Struct", + }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + // outer + Token::Str("outer"), + Token::U32(42), + // tag + Token::Str("tag"), + Token::UnitVariant { + name: "Enum", + variant: "Struct", + }, Token::MapEnd, ], ); @@ -3139,17 +3443,20 @@ mod flatten { D { d: i32 }, } + let value = Flatten { + x: X::B { b: 1 }, + y: Y::D { d: 2 }, + }; assert_tokens( - &Flatten { - x: X::B { b: 1 }, - y: Y::D { d: 2 }, - }, + &value, &[ Token::Map { len: None }, + // x Token::Str("typeX"), Token::Str("B"), Token::Str("b"), Token::I32(1), + // y Token::Str("typeY"), Token::Str("D"), Token::Str("d"), @@ -3157,11 +3464,28 @@ mod flatten { Token::MapEnd, ], ); + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // y + Token::Str("typeY"), + Token::Str("D"), + Token::Str("d"), + Token::I32(2), + // x + Token::Str("typeX"), + Token::Str("B"), + Token::Str("b"), + Token::I32(1), + Token::MapEnd, + ], + ); } #[test] fn unit_enum_with_unknown_fields() { - #[derive(Debug, PartialEq, Deserialize)] + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Flatten { #[serde(flatten)] x: X, @@ -3169,31 +3493,49 @@ mod flatten { y: Y, } - #[derive(Debug, PartialEq, Deserialize)] + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "typeX")] enum X { A, } - #[derive(Debug, PartialEq, Deserialize)] + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "typeY")] enum Y { B { c: u32 }, } - assert_de_tokens( - &Flatten { - x: X::A, - y: Y::B { c: 0 }, - }, + let value = Flatten { + x: X::A, + y: Y::B { c: 0 }, + }; + assert_tokens( + &value, &[ Token::Map { len: None }, + // x Token::Str("typeX"), Token::Str("A"), + // y Token::Str("typeY"), Token::Str("B"), Token::Str("c"), - Token::I32(0), + Token::U32(0), + Token::MapEnd, + ], + ); + assert_de_tokens( + &value, + &[ + Token::Map { len: None }, + // y + Token::Str("typeY"), + Token::Str("B"), + Token::Str("c"), + Token::U32(0), + // x + Token::Str("typeX"), + Token::Str("A"), Token::MapEnd, ], ); diff --git a/test_suite/tests/test_borrow.rs b/test_suite/tests/test_borrow.rs index f749ec32..6fcdd7c9 100644 --- a/test_suite/tests/test_borrow.rs +++ b/test_suite/tests/test_borrow.rs @@ -1,7 +1,10 @@ #![allow( clippy::derive_partial_eq_without_eq, clippy::items_after_statements, - clippy::used_underscore_binding + clippy::used_underscore_binding, + // We use lots of declarations inside function bodies to avoid conflicts, + // but they aren't used. We just want to make sure they compile. + dead_code, )] use serde::de::value::{BorrowedStrDeserializer, MapDeserializer}; @@ -162,7 +165,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 +181,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 3ca0fde3..2e80fbaf 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -10,7 +10,6 @@ )] #![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; @@ -23,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}; @@ -93,7 +92,7 @@ struct StructSkipDefault { #[derive(PartialEq, Debug, Deserialize)] #[serde(default)] -struct StructSkipDefaultGeneric { +pub struct StructSkipDefaultGeneric { #[serde(skip_deserializing)] t: T, } @@ -1040,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), @@ -1275,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), @@ -2065,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 d1ea2b91..cae5edda 100644 --- a/test_suite/tests/test_de_error.rs +++ b/test_suite/tests/test_de_error.rs @@ -1438,6 +1438,14 @@ fn test_integer_from_float() { ); } +#[test] +fn test_nan_no_decimal_point() { + assert_de_tokens_error::( + &[Token::F32(f32::NAN)], + "invalid type: floating point `NaN`, expected isize", + ); +} + #[test] fn test_unit_struct_from_seq() { assert_de_tokens_error::( @@ -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_deprecated.rs b/test_suite/tests/test_deprecated.rs new file mode 100644 index 00000000..0ddf641a --- /dev/null +++ b/test_suite/tests/test_deprecated.rs @@ -0,0 +1,30 @@ +#![deny(deprecated)] +#![allow(dead_code)] + +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +#[deprecated] +enum DeprecatedEnum { + A, + B, +} + +#[derive(Serialize, Deserialize)] +#[deprecated] +struct DeprecatedStruct { + a: bool, +} + +#[derive(Serialize, Deserialize)] +enum DeprecatedVariant { + A, + #[deprecated] + B, +} + +#[derive(Serialize, Deserialize)] +struct DeprecatedField { + #[deprecated] + a: bool, +} 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 00000000..0bcdbc39 --- /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 00000000..b4d428c4 --- /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 00000000..48f50c9c --- /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 97b0a96e..2c038ae8 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, @@ -17,8 +18,12 @@ clippy::ptr_arg, clippy::too_many_lines, clippy::trivially_copy_pass_by_ref, - clippy::type_repetition_in_bounds + clippy::type_repetition_in_bounds, + // We use lots of declarations inside function bodies to avoid conflicts, + // but they aren't used. We just want to make sure they compile. + dead_code, )] +#![deny(clippy::collection_is_never_read)] use serde::de::{Deserialize, DeserializeOwned, Deserializer}; use serde::ser::{Serialize, Serializer}; @@ -287,60 +292,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 +357,7 @@ fn test_gen() { #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] - enum EmptyVariantsDenyUnknown { + pub enum EmptyVariantsDenyUnknown { Braced {}, Tuple(), BracedSkip { @@ -364,21 +369,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 +395,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 +403,7 @@ fn test_gen() { } #[derive(Serialize, Deserialize)] - enum BorrowVariant<'a> { + pub enum BorrowVariant<'a> { #[serde(borrow, with = "StrDef")] S(Str<'a>), } @@ -415,14 +420,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 { @@ -546,26 +551,45 @@ fn test_gen() { assert::(); #[derive(Serialize, Deserialize)] - #[serde(deny_unknown_fields)] - struct FlattenDenyUnknown { + pub struct Flatten { #[serde(flatten)] t: T, } #[derive(Serialize, Deserialize)] - struct StaticStrStruct<'a> { + #[serde(deny_unknown_fields)] + pub struct FlattenDenyUnknown { + #[serde(flatten)] + t: T, + } + + #[derive(Serialize, Deserialize)] + 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 +663,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 +673,7 @@ fn test_gen() { #[derive(Deserialize)] #[serde(tag = "t", content = "c")] - enum AdjacentlyTaggedVoid {} + pub enum AdjacentlyTaggedVoid {} #[derive(Serialize, Deserialize)] enum SkippedVariant { @@ -661,14 +686,14 @@ 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 { + #[allow(dead_code)] + pub enum UntaggedNewtypeVariantWith { Newtype( #[serde(serialize_with = "ser_x")] #[serde(deserialize_with = "de_x")] @@ -678,7 +703,8 @@ fn test_gen() { #[derive(Serialize, Deserialize)] #[serde(transparent)] - struct TransparentWith { + #[allow(dead_code)] + pub struct TransparentWith { #[serde(serialize_with = "ser_x")] #[serde(deserialize_with = "de_x")] x: X, @@ -686,41 +712,61 @@ fn test_gen() { #[derive(Deserialize)] #[serde(untagged)] + #[allow(dead_code)] pub enum UntaggedWithBorrow<'a> { - Single(#[serde(borrow)] RelObject<'a>), - Many(#[serde(borrow)] Vec>), + 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 +775,8 @@ fn test_gen() { } #[derive(Serialize)] - #[repr(packed)] + #[repr(C, packed)] + #[allow(dead_code)] struct Packed { x: u8, y: u16, @@ -738,8 +785,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 +800,22 @@ 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, } + assert_ser::(); + #[derive(Deserialize)] #[serde(bound(deserialize = "[&'de str; N]: Copy"))] - struct GenericUnitStruct; + pub struct GenericUnitStruct; } ////////////////////////////////////////////////////////////////////////// @@ -855,7 +902,7 @@ where #[derive(Debug, PartialEq, Deserialize)] #[serde(tag = "tag")] -enum InternallyTagged { +pub enum InternallyTagged { #[serde(deserialize_with = "deserialize_generic")] Unit, @@ -876,14 +923,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 +941,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 02c36c41..6b2fc4c5 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( @@ -605,1038 +552,6 @@ fn test_enum_state_field() { ); } -#[test] -fn test_untagged_enum() { - #[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 test_internally_tagged_enum() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Newtype(BTreeMap); - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Struct { - f: u8, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "type")] - enum InternallyTagged { - A { a: u8 }, - B, - C(BTreeMap), - D(Newtype), - E(Struct), - } - - assert_tokens( - &InternallyTagged::A { a: 1 }, - &[ - Token::Struct { - name: "InternallyTagged", - len: 2, - }, - Token::Str("type"), - Token::Str("A"), - Token::Str("a"), - Token::U8(1), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::A { a: 1 }, - &[ - Token::Seq { len: Some(2) }, - Token::Str("A"), - Token::U8(1), - Token::SeqEnd, - ], - ); - - assert_tokens( - &InternallyTagged::B, - &[ - Token::Struct { - name: "InternallyTagged", - len: 1, - }, - Token::Str("type"), - Token::Str("B"), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::B, - &[Token::Seq { len: Some(1) }, Token::Str("B"), Token::SeqEnd], - ); - - assert_tokens( - &InternallyTagged::C(BTreeMap::new()), - &[ - Token::Map { len: Some(1) }, - Token::Str("type"), - Token::Str("C"), - Token::MapEnd, - ], - ); - - assert_de_tokens_error::( - &[ - Token::Seq { len: Some(2) }, - Token::Str("C"), - Token::Map { len: Some(0) }, - Token::MapEnd, - Token::SeqEnd, - ], - "invalid type: sequence, expected a map", - ); - - assert_tokens( - &InternallyTagged::D(Newtype(BTreeMap::new())), - &[ - Token::Map { len: Some(1) }, - Token::Str("type"), - Token::Str("D"), - Token::MapEnd, - ], - ); - - assert_tokens( - &InternallyTagged::E(Struct { f: 6 }), - &[ - Token::Struct { - name: "Struct", - len: 2, - }, - Token::Str("type"), - Token::Str("E"), - Token::Str("f"), - Token::U8(6), - 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_enum_with_untagged_variant() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(tag = "kind")] - enum InternallyTagged { - Tagged { - a: u8, - }, - #[serde(untagged)] - Untagged { - kind: String, - b: u8, - }, - } - - assert_de_tokens( - &InternallyTagged::Tagged { a: 1 }, - &[ - Token::Map { len: Some(2) }, - Token::Str("kind"), - 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("kind"), - Token::Str("Tagged"), - Token::Str("a"), - Token::U8(1), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &InternallyTagged::Untagged { - kind: "Foo".to_owned(), - b: 2, - }, - &[ - Token::Map { len: Some(2) }, - Token::Str("kind"), - Token::Str("Foo"), - Token::Str("b"), - Token::U8(2), - Token::MapEnd, - ], - ); - - assert_tokens( - &InternallyTagged::Untagged { - kind: "Foo".to_owned(), - b: 2, - }, - &[ - Token::Struct { - name: "InternallyTagged", - len: 2, - }, - Token::Str("kind"), - Token::Str("Foo"), - Token::Str("b"), - Token::U8(2), - Token::StructEnd, - ], - ); - - assert_tokens( - &InternallyTagged::Untagged { - kind: "Tagged".to_owned(), - b: 2, - }, - &[ - Token::Struct { - name: "InternallyTagged", - len: 2, - }, - Token::Str("kind"), - Token::Str("Tagged"), - Token::Str("b"), - Token::U8(2), - Token::StructEnd, - ], - ); -} - -#[test] -fn test_internally_tagged_bytes() { - #[derive(Debug, PartialEq, Deserialize)] - #[serde(tag = "type")] - enum InternallyTagged { - String { - string: String, - }, - 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)] @@ -1736,240 +651,6 @@ fn test_internally_tagged_struct_with_flattened_field() { ); } -#[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)] @@ -2153,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 c1f152eb..919b3e28 100644 --- a/test_suite/tests/test_remote.rs +++ b/test_suite/tests/test_remote.rs @@ -1,4 +1,4 @@ -#![allow(clippy::redundant_field_names)] +#![allow(clippy::redundant_field_names, dead_code)] use serde_derive::{Deserialize, Serialize}; @@ -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 516bdeae..ed71d42d 100644 --- a/test_suite/tests/test_self.rs +++ b/test_suite/tests/test_self.rs @@ -1,4 +1,4 @@ -#![allow(clippy::used_underscore_binding)] +#![allow(clippy::used_underscore_binding, dead_code)] use serde_derive::{Deserialize, Serialize}; @@ -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 71ec3bc5..1c806318 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), @@ -624,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 127b557e..8b04ab43 100644 --- a/test_suite/tests/test_serde_path.rs +++ b/test_suite/tests/test_serde_path.rs @@ -1,5 +1,7 @@ #![allow( + clippy::elidable_lifetime_names, clippy::extra_unused_type_parameters, + clippy::needless_lifetimes, clippy::type_repetition_in_bounds )] 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 00000000..5ca958b6 --- /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 00000000..b19c04c1 --- /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)]` (part of `#[warn(unused)]`) 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 00000000..e1825475 --- /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 00000000..cd747fac --- /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)]` (part of `#[warn(unused)]`) 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/conflict/internal-tag-alias.stderr b/test_suite/tests/ui/conflict/internal-tag-alias.stderr index 8a3df92c..8afa6a9b 100644 --- a/test_suite/tests/ui/conflict/internal-tag-alias.stderr +++ b/test_suite/tests/ui/conflict/internal-tag-alias.stderr @@ -1,11 +1,11 @@ error: variant field name `conflict` conflicts with internal tag --> tests/ui/conflict/internal-tag-alias.rs:4:1 | -4 | / #[serde(tag = "conflict")] -5 | | enum E { -6 | | A { -7 | | #[serde(alias = "conflict")] -8 | | x: (), -9 | | }, + 4 | / #[serde(tag = "conflict")] + 5 | | enum E { + 6 | | A { + 7 | | #[serde(alias = "conflict")] + 8 | | x: (), + 9 | | }, 10 | | } | |_^ diff --git a/test_suite/tests/ui/conflict/internal-tag.stderr b/test_suite/tests/ui/conflict/internal-tag.stderr index d1792306..97f4bc90 100644 --- a/test_suite/tests/ui/conflict/internal-tag.stderr +++ b/test_suite/tests/ui/conflict/internal-tag.stderr @@ -1,11 +1,11 @@ error: variant field name `conflict` conflicts with internal tag --> tests/ui/conflict/internal-tag.rs:4:1 | -4 | / #[serde(tag = "conflict")] -5 | | enum E { -6 | | A { -7 | | #[serde(rename = "conflict")] -8 | | x: (), -9 | | }, + 4 | / #[serde(tag = "conflict")] + 5 | | enum E { + 6 | | A { + 7 | | #[serde(rename = "conflict")] + 8 | | x: (), + 9 | | }, 10 | | } | |_^ 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 00000000..2e0fc867 --- /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 00000000..50c0ae97 --- /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 00000000..7c92b15f --- /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 00000000..125b7262 --- /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 00000000..5e1a4e84 --- /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 00000000..fed3e06a --- /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 00000000..1c5e910d --- /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 00000000..027b665d --- /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 00000000..84b60942 --- /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 00000000..04bb8349 --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_newtype.stderr @@ -0,0 +1,34 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_newtype.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Newtype`, found `()` + +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 00000000..3c5370b8 --- /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 00000000..4973c03d --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_struct.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_struct.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Struct`, found `()` + +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 00000000..f40e1165 --- /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 00000000..dba33bfb --- /dev/null +++ b/test_suite/tests/ui/default-attribute/incorrect_type_tuple.stderr @@ -0,0 +1,34 @@ +error[E0308]: mismatched types + --> tests/ui/default-attribute/incorrect_type_tuple.rs:6:19 + | +6 | #[serde(default = "main")] + | ^^^^^^ expected `Tuple`, found `()` + +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 00000000..296512ff --- /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 00000000..d0268ce3 --- /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 00000000..e57f4d1a --- /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 00000000..08e89ee1 --- /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 00000000..c0500545 --- /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 00000000..c99c3bb1 --- /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 00000000..25705fad --- /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 00000000..43013612 --- /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/deprecated/deprecated_de_with.rs b/test_suite/tests/ui/deprecated/deprecated_de_with.rs new file mode 100644 index 00000000..83466525 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_de_with.rs @@ -0,0 +1,20 @@ +#![deny(deprecated)] + +use serde::Deserializer; +use serde_derive::Deserialize; + +#[derive(Deserialize)] +pub struct Struct { + #[serde(deserialize_with = "deprecated_with")] + pub field: i32, +} + +#[deprecated] +fn deprecated_with<'de, D>(_deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + unimplemented!() +} + +fn main() {} diff --git a/test_suite/tests/ui/deprecated/deprecated_de_with.stderr b/test_suite/tests/ui/deprecated/deprecated_de_with.stderr new file mode 100644 index 00000000..6e4ede67 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_de_with.stderr @@ -0,0 +1,11 @@ +error: use of deprecated function `deprecated_with` + --> tests/ui/deprecated/deprecated_de_with.rs:8:32 + | +8 | #[serde(deserialize_with = "deprecated_with")] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/deprecated/deprecated_de_with.rs:1:9 + | +1 | #![deny(deprecated)] + | ^^^^^^^^^^ diff --git a/test_suite/tests/ui/deprecated/deprecated_ser_with.rs b/test_suite/tests/ui/deprecated/deprecated_ser_with.rs new file mode 100644 index 00000000..72f55a36 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_ser_with.rs @@ -0,0 +1,20 @@ +#![deny(deprecated)] + +use serde::Serializer; +use serde_derive::Serialize; + +#[derive(Serialize)] +pub struct Struct { + #[serde(serialize_with = "deprecated_with")] + pub field: i32, +} + +#[deprecated] +fn deprecated_with(_field: &i32, _serializer: S) -> Result +where + S: Serializer, +{ + unimplemented!() +} + +fn main() {} diff --git a/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr b/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr new file mode 100644 index 00000000..574c7116 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr @@ -0,0 +1,11 @@ +error: use of deprecated function `deprecated_with` + --> tests/ui/deprecated/deprecated_ser_with.rs:8:30 + | +8 | #[serde(serialize_with = "deprecated_with")] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/deprecated/deprecated_ser_with.rs:1:9 + | +1 | #![deny(deprecated)] + | ^^^^^^^^^^ diff --git a/test_suite/tests/ui/remote/unknown_field.stderr b/test_suite/tests/ui/remote/unknown_field.stderr index f7efacde..5c851ce1 100644 --- a/test_suite/tests/ui/remote/unknown_field.stderr +++ b/test_suite/tests/ui/remote/unknown_field.stderr @@ -6,8 +6,9 @@ error[E0609]: no field `b` on type `&remote::S` | help: a field with a similar name exists | -12 | a: u8, - | ~ +12 - b: u8, +12 + a: u8, + | error[E0560]: struct `remote::S` has no field named `b` --> tests/ui/remote/unknown_field.rs:12:5 diff --git a/test_suite/tests/ui/transparent/de_at_least_one.stderr b/test_suite/tests/ui/transparent/de_at_least_one.stderr index 6fb2fb20..97d7f52a 100644 --- a/test_suite/tests/ui/transparent/de_at_least_one.stderr +++ b/test_suite/tests/ui/transparent/de_at_least_one.stderr @@ -1,11 +1,11 @@ error: #[serde(transparent)] requires at least one field that is neither skipped nor has a default --> tests/ui/transparent/de_at_least_one.rs:4:1 | -4 | / #[serde(transparent)] -5 | | struct S { -6 | | #[serde(skip)] -7 | | a: u8, -8 | | #[serde(default)] -9 | | b: u8, + 4 | / #[serde(transparent)] + 5 | | struct S { + 6 | | #[serde(skip)] + 7 | | a: u8, + 8 | | #[serde(default)] + 9 | | b: u8, 10 | | } | |_^ diff --git a/test_suite/tests/ui/unimplemented/required_by_dependency.rs b/test_suite/tests/ui/unimplemented/required_by_dependency.rs new file mode 100644 index 00000000..d3f88f11 --- /dev/null +++ b/test_suite/tests/ui/unimplemented/required_by_dependency.rs @@ -0,0 +1,6 @@ +struct MyStruct; + +fn main() { + serde_test::assert_ser_tokens(&MyStruct, &[]); + serde_test::assert_de_tokens(&MyStruct, &[]); +} diff --git a/test_suite/tests/ui/unimplemented/required_by_dependency.stderr b/test_suite/tests/ui/unimplemented/required_by_dependency.stderr new file mode 100644 index 00000000..2473a812 --- /dev/null +++ b/test_suite/tests/ui/unimplemented/required_by_dependency.stderr @@ -0,0 +1,113 @@ +error[E0277]: the trait bound `MyStruct: serde::Serialize` is not satisfied + --> tests/ui/unimplemented/required_by_dependency.rs:4:35 + | +4 | serde_test::assert_ser_tokens(&MyStruct, &[]); + | ----------------------------- ^^^^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `serde_core::ser::Serialize` is not implemented for `MyStruct` + --> tests/ui/unimplemented/required_by_dependency.rs:1:1 + | +1 | struct MyStruct; + | ^^^^^^^^^^^^^^^ + = 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 `serde_core::ser::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 `assert_ser_tokens` + --> $CARGO/serde_test-$VERSION/src/assert.rs + | + | pub fn assert_ser_tokens(value: &T, tokens: &[Token]) + | ----------------- required by a bound in this function + | where + | T: ?Sized + Serialize, + | ^^^^^^^^^ required by this bound in `assert_ser_tokens` + +error[E0277]: the trait bound `MyStruct: serde::Deserialize<'de>` is not satisfied + --> tests/ui/unimplemented/required_by_dependency.rs:5:34 + | +5 | serde_test::assert_de_tokens(&MyStruct, &[]); + | ---------------------------- ^^^^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `serde_core::de::Deserialize<'_>` is not implemented for `MyStruct` + --> tests/ui/unimplemented/required_by_dependency.rs:1:1 + | +1 | struct 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 `serde_core::de::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 `assert_de_tokens` + --> $CARGO/serde_test-$VERSION/src/assert.rs + | + | pub fn assert_de_tokens<'de, T>(value: &T, tokens: &'de [Token]) + | ---------------- required by a bound in this function + | where + | T: Deserialize<'de> + PartialEq + Debug, + | ^^^^^^^^^^^^^^^^ required by this bound in `assert_de_tokens` + +error[E0277]: can't compare `MyStruct` with `MyStruct` + --> tests/ui/unimplemented/required_by_dependency.rs:5:34 + | +5 | serde_test::assert_de_tokens(&MyStruct, &[]); + | ---------------------------- ^^^^^^^^^ no implementation for `MyStruct == MyStruct` + | | + | required by a bound introduced by this call + | + = help: the trait `PartialEq` is not implemented for `MyStruct` +note: required by a bound in `assert_de_tokens` + --> $CARGO/serde_test-$VERSION/src/assert.rs + | + | pub fn assert_de_tokens<'de, T>(value: &T, tokens: &'de [Token]) + | ---------------- required by a bound in this function + | where + | T: Deserialize<'de> + PartialEq + Debug, + | ^^^^^^^^^ required by this bound in `assert_de_tokens` +help: consider annotating `MyStruct` with `#[derive(PartialEq)]` + | + 1 + #[derive(PartialEq)] + 2 | struct MyStruct; + | + +error[E0277]: `MyStruct` doesn't implement `Debug` + --> tests/ui/unimplemented/required_by_dependency.rs:5:34 + | +5 | serde_test::assert_de_tokens(&MyStruct, &[]); + | ---------------------------- ^^^^^^^^^ the trait `Debug` is not implemented for `MyStruct` + | | + | required by a bound introduced by this call + | + = note: add `#[derive(Debug)]` to `MyStruct` or manually `impl Debug for MyStruct` +note: required by a bound in `assert_de_tokens` + --> $CARGO/serde_test-$VERSION/src/assert.rs + | + | pub fn assert_de_tokens<'de, T>(value: &T, tokens: &'de [Token]) + | ---------------- required by a bound in this function + | where + | T: Deserialize<'de> + PartialEq + Debug, + | ^^^^^ required by this bound in `assert_de_tokens` +help: consider annotating `MyStruct` with `#[derive(Debug)]` + | + 1 + #[derive(Debug)] + 2 | struct MyStruct; + | diff --git a/test_suite/tests/ui/unimplemented/required_locally.rs b/test_suite/tests/ui/unimplemented/required_locally.rs new file mode 100644 index 00000000..fab56751 --- /dev/null +++ b/test_suite/tests/ui/unimplemented/required_locally.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/unimplemented/required_locally.stderr b/test_suite/tests/ui/unimplemented/required_locally.stderr new file mode 100644 index 00000000..e64fd411 --- /dev/null +++ b/test_suite/tests/ui/unimplemented/required_locally.stderr @@ -0,0 +1,65 @@ +error[E0277]: the trait bound `MyStruct: serde::Serialize` is not satisfied + --> tests/ui/unimplemented/required_locally.rs:21:15 + | +21 | to_string(&MyStruct); + | --------- ^^^^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `Serialize` is not implemented for `MyStruct` + --> tests/ui/unimplemented/required_locally.rs:18:1 + | +18 | struct MyStruct; + | ^^^^^^^^^^^^^^^ + = 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/unimplemented/required_locally.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: serde::Deserialize<'de>` is not satisfied + --> tests/ui/unimplemented/required_locally.rs:22:23 + | +22 | let _: MyStruct = from_str(""); + | ^^^^^^^^^^^^ unsatisfied trait bound + | +help: the trait `Deserialize<'_>` is not implemented for `MyStruct` + --> tests/ui/unimplemented/required_locally.rs:18:1 + | +18 | struct 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/unimplemented/required_locally.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/with-variant/skip_de_struct_field.stderr b/test_suite/tests/ui/with-variant/skip_de_struct_field.stderr index 34bb1f73..5632b67d 100644 --- a/test_suite/tests/ui/with-variant/skip_de_struct_field.stderr +++ b/test_suite/tests/ui/with-variant/skip_de_struct_field.stderr @@ -1,10 +1,10 @@ error: variant `Struct` cannot have both #[serde(deserialize_with)] and a field `f1` marked with #[serde(skip_deserializing)] --> tests/ui/with-variant/skip_de_struct_field.rs:5:5 | -5 | / #[serde(deserialize_with = "deserialize_some_other_variant")] -6 | | Struct { -7 | | #[serde(skip_deserializing)] -8 | | f1: String, -9 | | f2: u8, + 5 | / #[serde(deserialize_with = "deserialize_some_other_variant")] + 6 | | Struct { + 7 | | #[serde(skip_deserializing)] + 8 | | f1: String, + 9 | | f2: u8, 10 | | }, | |_____^ diff --git a/test_suite/tests/ui/with-variant/skip_ser_struct_field.stderr b/test_suite/tests/ui/with-variant/skip_ser_struct_field.stderr index f2fe26be..f6792159 100644 --- a/test_suite/tests/ui/with-variant/skip_ser_struct_field.stderr +++ b/test_suite/tests/ui/with-variant/skip_ser_struct_field.stderr @@ -1,10 +1,10 @@ error: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing)] --> tests/ui/with-variant/skip_ser_struct_field.rs:5:5 | -5 | / #[serde(serialize_with = "serialize_some_other_variant")] -6 | | Struct { -7 | | #[serde(skip_serializing)] -8 | | f1: String, -9 | | f2: u8, + 5 | / #[serde(serialize_with = "serialize_some_other_variant")] + 6 | | Struct { + 7 | | #[serde(skip_serializing)] + 8 | | f1: String, + 9 | | f2: u8, 10 | | }, | |_____^ diff --git a/test_suite/tests/ui/with-variant/skip_ser_struct_field_if.stderr b/test_suite/tests/ui/with-variant/skip_ser_struct_field_if.stderr index bd83aa34..c06c3404 100644 --- a/test_suite/tests/ui/with-variant/skip_ser_struct_field_if.stderr +++ b/test_suite/tests/ui/with-variant/skip_ser_struct_field_if.stderr @@ -1,10 +1,10 @@ error: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing_if)] --> tests/ui/with-variant/skip_ser_struct_field_if.rs:5:5 | -5 | / #[serde(serialize_with = "serialize_some_newtype_variant")] -6 | | Struct { -7 | | #[serde(skip_serializing_if = "always")] -8 | | f1: String, -9 | | f2: u8, + 5 | / #[serde(serialize_with = "serialize_some_newtype_variant")] + 6 | | Struct { + 7 | | #[serde(skip_serializing_if = "always")] + 8 | | f1: String, + 9 | | f2: u8, 10 | | }, | |_____^ 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 00000000..c1502bb4 --- /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 00000000..016a0a02 --- /dev/null +++ b/test_suite/tests/ui/with/incorrect_type.stderr @@ -0,0 +1,89 @@ +error[E0277]: the trait bound `&u8: serde::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 trait `Serializer` is implemented for `&mut Formatter<'a>` +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: serde::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 trait `Serializer` is implemented for `&mut Formatter<'a>` + +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: serde::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 trait `Serializer` is implemented for `&mut Formatter<'a>` +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: serde::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 trait `Serializer` is implemented for `&mut Formatter<'a>` + +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`