From ab8c208627a897af503014d4b9a948b30b7cbbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 21 Nov 2019 16:09:59 +0100 Subject: [PATCH 1/3] Fix number deserialization with `arbitrary_precision` When we have a struct that uses `#[serde(flatten)]` and the feature `arbitrary_precision` is enabled, the deserialization. The problem was that the arbitrary number was forwarded as a "map" to the visitor and in a later step this map failed as a number was expected. Fixes: https://github.com/serde-rs/json/issues/505 --- Cargo.toml | 3 +++ src/de.rs | 41 +++++++++++++++++++++++++++++++++--- src/number.rs | 2 +- tests/regression/issue505.rs | 22 +++++++++++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 tests/regression/issue505.rs diff --git a/Cargo.toml b/Cargo.toml index 583e2d9f5..22822313d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,3 +66,6 @@ raw_value = [] # overflow the stack after deserialization has completed, including, but not # limited to, Display and Debug and Drop impls. unbounded_depth = [] + +[patch.crates-io] +serde = { git = "https://github.com/bkchr/serde.git", branch = "bkchr-tagged-content-u128-i128" } diff --git a/src/de.rs b/src/de.rs index 5c9c20b20..33885dfbb 100644 --- a/src/de.rs +++ b/src/de.rs @@ -15,8 +15,6 @@ use read::{self, Reference}; pub use read::{IoRead, Read, SliceRead, StrRead}; use number::Number; -#[cfg(feature = "arbitrary_precision")] -use number::NumberDeserializer; ////////////////////////////////////////////////////////////////////////////// @@ -107,6 +105,43 @@ pub enum ParserNumber { String(String), } +#[cfg(feature = "arbitrary_precision")] +impl ParserNumber { + fn parse_arbitrary_precision<'de, V>(number: &str, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + let err = || Err(super::number::invalid_number()); + + serde_if_integer128! { + let res = if let Some(val) = number.parse::().ok() { + visitor.visit_i128(val) + } else if let Some(val) = number.parse::().ok() { + visitor.visit_u128(val) + } else if let Some(val) = number.parse::().ok() { + visitor.visit_f64(val) + } else { + err() + }; + + return res; + } + + #[allow(unreachable_code)] + { + if let Some(val) = number.parse::().ok() { + visitor.visit_i64(val) + } else if let Some(val) = number.parse::().ok() { + visitor.visit_u64(val) + } else if let Some(val) = number.parse::().ok() { + visitor.visit_f64(val) + } else { + err() + } + } + } +} + impl ParserNumber { fn visit<'de, V>(self, visitor: V) -> Result where @@ -117,7 +152,7 @@ impl ParserNumber { ParserNumber::U64(x) => visitor.visit_u64(x), ParserNumber::I64(x) => visitor.visit_i64(x), #[cfg(feature = "arbitrary_precision")] - ParserNumber::String(x) => visitor.visit_map(NumberDeserializer { number: x.into() }), + ParserNumber::String(x) => Self::parse_arbitrary_precision(&x, visitor), } } diff --git a/src/number.rs b/src/number.rs index fd2707aa2..ef0950b39 100644 --- a/src/number.rs +++ b/src/number.rs @@ -444,7 +444,7 @@ impl<'de> de::Deserialize<'de> for NumberFromString { } #[cfg(feature = "arbitrary_precision")] -fn invalid_number() -> Error { +pub(crate) fn invalid_number() -> Error { Error::syntax(ErrorCode::InvalidNumber, 0, 0) } diff --git a/tests/regression/issue505.rs b/tests/regression/issue505.rs new file mode 100644 index 000000000..d337b61a4 --- /dev/null +++ b/tests/regression/issue505.rs @@ -0,0 +1,22 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize)] +struct Data { + _value: i32, + _value2: i128, +} + +#[derive(Deserialize)] +#[serde(tag = "type")] +enum Wrapper { + Data(Data), +} + +#[test] +fn test() { + let json = r#"{"type":"Data","_value":123,"_value2":123244235436463}"#; + // Okay + let _data1: Data = serde_json::from_str(json).unwrap(); + // Fails! + let _data2: Wrapper = serde_json::from_str(json).unwrap(); +} From c161bdf6c144b6253493e31de60e3b6716af02de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 21 Nov 2019 21:12:51 +0100 Subject: [PATCH 2/3] Fix tests --- src/number.rs | 8 -------- src/value/de.rs | 13 +++++++++++++ tests/test.rs | 15 --------------- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/number.rs b/src/number.rs index ef0950b39..0fcde1bdf 100644 --- a/src/number.rs +++ b/src/number.rs @@ -245,14 +245,6 @@ impl Number { None } } - - #[cfg(feature = "arbitrary_precision")] - /// Not public API. Only tests use this. - #[doc(hidden)] - #[inline] - pub fn from_string_unchecked(n: String) -> Self { - Number { n: n } - } } impl fmt::Display for Number { diff --git a/src/value/de.rs b/src/value/de.rs index a1f40cf92..ca9865158 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -50,6 +50,19 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + #[cfg(feature = "arbitrary_precision")] + serde_if_integer128! { + #[inline] + fn visit_i128(self, value: i128) -> Result { + Ok(Value::Number(value.into())) + } + + #[inline] + fn visit_u128(self, value: u128) -> Result { + Ok(Value::Number(value.into())) + } + } + #[inline] fn visit_f64(self, value: f64) -> Result { Ok(Number::from_f64(value).map_or(Value::Null, Value::Number)) diff --git a/tests/test.rs b/tests/test.rs index 8386236ed..d61ec7b38 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -956,21 +956,6 @@ fn test_parse_number() { ("\t1.0", "invalid number at line 1 column 1"), ("1.0\t", "invalid number at line 1 column 4"), ]); - - #[cfg(feature = "arbitrary_precision")] - test_parse_ok(vec![ - ("1e999", Number::from_string_unchecked("1e999".to_owned())), - ("-1e999", Number::from_string_unchecked("-1e999".to_owned())), - ("1e-999", Number::from_string_unchecked("1e-999".to_owned())), - ( - "2.3e999", - Number::from_string_unchecked("2.3e999".to_owned()), - ), - ( - "-2.3e999", - Number::from_string_unchecked("-2.3e999".to_owned()), - ), - ]); } #[test] From 793878903eff95955ee5e657044d7d658064fcce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 21 Nov 2019 21:37:30 +0100 Subject: [PATCH 3/3] Fix compilation on older versions of rustc --- src/de.rs | 2 +- src/number.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de.rs b/src/de.rs index 33885dfbb..9fb5e6a53 100644 --- a/src/de.rs +++ b/src/de.rs @@ -111,7 +111,7 @@ impl ParserNumber { where V: de::Visitor<'de>, { - let err = || Err(super::number::invalid_number()); + let err = || Err(Error::syntax(ErrorCode::InvalidNumber, 0, 0)); serde_if_integer128! { let res = if let Some(val) = number.parse::().ok() { diff --git a/src/number.rs b/src/number.rs index 0fcde1bdf..37b4c7386 100644 --- a/src/number.rs +++ b/src/number.rs @@ -436,7 +436,7 @@ impl<'de> de::Deserialize<'de> for NumberFromString { } #[cfg(feature = "arbitrary_precision")] -pub(crate) fn invalid_number() -> Error { +fn invalid_number() -> Error { Error::syntax(ErrorCode::InvalidNumber, 0, 0) }