diff --git a/CHANGELOG.md b/CHANGELOG.md index be17b84b5..c3900fcbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Format Changes + - Ignore `#![type = "..."]` and `#![schema = "..."]` attributes ([#596](https://github.com/ron-rs/ron/pull/596)) +### Bug Fixes + +- Fixed parsing of integer type suffixes for non-decimal numbers ([#600](https://github.com/ron-rs/ron/pull/600)) + ## [0.12.0] - 2025-11-12 ### API Changes diff --git a/src/parse.rs b/src/parse.rs index 5299cb4a4..13a876735 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -276,14 +276,7 @@ impl<'a> Parser<'a> { Ok(num_acc) } - fn parse_integer(&mut self, sign: i8) -> Result { - let base = match () { - () if self.consume_str("0b") => 2, - () if self.consume_str("0o") => 8, - () if self.consume_str("0x") => 16, - () => 10, - }; - + fn parse_integer(&mut self, sign: i8, base: u8) -> Result { let num_bytes = self.next_chars_while_len(is_int_char); if num_bytes == 0 { @@ -344,6 +337,13 @@ impl<'a> Parser<'a> { }; let sign = if is_negative { -1 } else { 1 }; + let base = match () { + () if self.consume_str("0b") => 2, + () if self.consume_str("0o") => 8, + () if self.consume_str("0x") => 16, + () => 10, + }; + let num_bytes = self.next_chars_while_len(is_int_char); if self.src()[num_bytes..].starts_with(['i', 'u']) { @@ -356,56 +356,62 @@ impl<'a> Parser<'a> { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::I8), + self.parse_integer::(sign, base).map(ParsedInteger::I8), suffix_bytes, ) } else if self.consume_ident("i16") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::I16), + self.parse_integer::(sign, base) + .map(ParsedInteger::I16), suffix_bytes, ) } else if self.consume_ident("i32") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::I32), + self.parse_integer::(sign, base) + .map(ParsedInteger::I32), suffix_bytes, ) } else if self.consume_ident("i64") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::I64), + self.parse_integer::(sign, base) + .map(ParsedInteger::I64), suffix_bytes, ) } else if self.consume_ident("u8") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::U8), + self.parse_integer::(sign, base).map(ParsedInteger::U8), suffix_bytes, ) } else if self.consume_ident("u16") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::U16), + self.parse_integer::(sign, base) + .map(ParsedInteger::U16), suffix_bytes, ) } else if self.consume_ident("u32") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::U32), + self.parse_integer::(sign, base) + .map(ParsedInteger::U32), suffix_bytes, ) } else if self.consume_ident("u64") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::U64), + self.parse_integer::(sign, base) + .map(ParsedInteger::U64), suffix_bytes, ) } else { @@ -414,14 +420,16 @@ impl<'a> Parser<'a> { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::I128), + self.parse_integer::(sign, base) + .map(ParsedInteger::I128), suffix_bytes, ) } else if self.consume_ident("u128") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( - self.parse_integer::(sign).map(ParsedInteger::U128), + self.parse_integer::(sign, base) + .map(ParsedInteger::U128), suffix_bytes, ) } else { @@ -449,7 +457,7 @@ impl<'a> Parser<'a> { self.set_cursor(int_cursor); } - T::parse(self, sign) + T::parse(self, sign, base) } pub fn any_number(&mut self) -> Result { @@ -1461,7 +1469,7 @@ impl_num! { i8 i16 i32 i64 u8 u16 u32 u64 } impl_num! { i128 u128 } pub trait Integer: Sized { - fn parse(parser: &mut Parser, sign: i8) -> Result; + fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result; fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result; } @@ -1469,8 +1477,8 @@ pub trait Integer: Sized { macro_rules! impl_integer { ($wrap:ident($ty:ty)) => { impl Integer for $ty { - fn parse(parser: &mut Parser, sign: i8) -> Result { - parser.parse_integer(sign) + fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result { + parser.parse_integer(sign, base) } fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result { @@ -1518,9 +1526,9 @@ pub enum ParsedInteger { } impl Integer for ParsedInteger { - fn parse(parser: &mut Parser, sign: i8) -> Result { + fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result { if sign < 0 { - let signed = parser.parse_integer::(-1)?; + let signed = parser.parse_integer::(-1, base)?; return if let Ok(x) = i8::try_from(signed) { Ok(ParsedInteger::I8(x)) @@ -1542,7 +1550,7 @@ impl Integer for ParsedInteger { }; } - let unsigned = parser.parse_integer::(1)?; + let unsigned = parser.parse_integer::(1, base)?; if let Ok(x) = u8::try_from(unsigned) { Ok(ParsedInteger::U8(x)) diff --git a/tests/599_integer_suffix.rs b/tests/599_integer_suffix.rs new file mode 100644 index 000000000..73b4e27c1 --- /dev/null +++ b/tests/599_integer_suffix.rs @@ -0,0 +1,69 @@ +#[test] +fn base_2() { + assert_eq!(ron::from_str("-0b101010i32"), Ok(-0b101010i32)); + assert_eq!(ron::from_str("0b101010u32"), Ok(0b101010u32)); + assert_eq!(ron::from_str("-0b10_10_10i32"), Ok(-0b101010i32)); + assert_eq!(ron::from_str("0b101_010_u32"), Ok(0b101010u32)); + + assert_eq!( + ron::from_str::("-0b101010i31"), + Err(ron::error::SpannedError { + code: ron::error::Error::TrailingCharacters, + span: ron::error::Span { + start: ron::error::Position { line: 1, col: 10 }, + end: ron::error::Position { line: 1, col: 10 } + } + }) + ); +} + +#[test] +fn base_8() { + assert_eq!(ron::from_str("-0o52i32"), Ok(-0o52i32)); + assert_eq!(ron::from_str("0o52u32"), Ok(0o52u32)); + assert_eq!(ron::from_str("-0o52_i32"), Ok(-0o52i32)); + assert_eq!(ron::from_str("0o5_2u32"), Ok(0o52u32)); + + assert_eq!( + ron::from_str::("0o_52_i32"), + Err(ron::error::SpannedError { + code: ron::error::Error::UnderscoreAtBeginning, + span: ron::error::Span { + start: ron::error::Position { line: 1, col: 7 }, + end: ron::error::Position { + line: 1, + col: 3, // FIXME + } + } + }) + ); +} + +#[test] +fn base_10() { + assert_eq!(ron::from_str("-42i32"), Ok(-42i32)); + assert_eq!(ron::from_str("42u32"), Ok(42u32)); + assert_eq!(ron::from_str("-42_i32"), Ok(-42i32)); + assert_eq!(ron::from_str("4_2u32"), Ok(42u32)); + + assert_eq!(ron::from_str("00i32"), Ok(0i32)); +} + +#[test] +fn base_16() { + assert_eq!(ron::from_str("-0x2Ai32"), Ok(-0x2Ai32)); + assert_eq!(ron::from_str("0x2Au32"), Ok(0x2Au32)); + assert_eq!(ron::from_str("-0x2_Ai32"), Ok(-0x2Ai32)); + assert_eq!(ron::from_str("0x2A_u32"), Ok(0x2Au32)); + + assert_eq!( + ron::from_str::("0x2Aj32"), + Err(ron::error::SpannedError { + code: ron::error::Error::TrailingCharacters, + span: ron::error::Span { + start: ron::error::Position { line: 1, col: 5 }, + end: ron::error::Position { line: 1, col: 5 } + } + }) + ); +}