Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
58 changes: 33 additions & 25 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,7 @@ impl<'a> Parser<'a> {
Ok(num_acc)
}

fn parse_integer<T: Num>(&mut self, sign: i8) -> Result<T> {
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<T: Num>(&mut self, sign: i8, base: u8) -> Result<T> {
let num_bytes = self.next_chars_while_len(is_int_char);

if num_bytes == 0 {
Expand Down Expand Up @@ -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,
};
Comment thread
torkleyy marked this conversation as resolved.

let num_bytes = self.next_chars_while_len(is_int_char);

if self.src()[num_bytes..].starts_with(['i', 'u']) {
Expand All @@ -356,56 +356,62 @@ impl<'a> Parser<'a> {
let suffix_bytes = self.src();
self.set_cursor(int_cursor);
(
self.parse_integer::<i8>(sign).map(ParsedInteger::I8),
self.parse_integer::<i8>(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::<i16>(sign).map(ParsedInteger::I16),
self.parse_integer::<i16>(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::<i32>(sign).map(ParsedInteger::I32),
self.parse_integer::<i32>(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::<i64>(sign).map(ParsedInteger::I64),
self.parse_integer::<i64>(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::<u8>(sign).map(ParsedInteger::U8),
self.parse_integer::<u8>(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::<u16>(sign).map(ParsedInteger::U16),
self.parse_integer::<u16>(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::<u32>(sign).map(ParsedInteger::U32),
self.parse_integer::<u32>(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::<u64>(sign).map(ParsedInteger::U64),
self.parse_integer::<u64>(sign, base)
.map(ParsedInteger::U64),
suffix_bytes,
)
} else {
Expand All @@ -414,14 +420,16 @@ impl<'a> Parser<'a> {
let suffix_bytes = self.src();
self.set_cursor(int_cursor);
(
self.parse_integer::<i128>(sign).map(ParsedInteger::I128),
self.parse_integer::<i128>(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::<u128>(sign).map(ParsedInteger::U128),
self.parse_integer::<u128>(sign, base)
.map(ParsedInteger::U128),
suffix_bytes,
)
} else {
Expand Down Expand Up @@ -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<Number> {
Expand Down Expand Up @@ -1461,16 +1469,16 @@ 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<Self>;
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self>;

fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self>;
}

macro_rules! impl_integer {
($wrap:ident($ty:ty)) => {
impl Integer for $ty {
fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
parser.parse_integer(sign)
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self> {
parser.parse_integer(sign, base)
}

fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self> {
Expand Down Expand Up @@ -1518,9 +1526,9 @@ pub enum ParsedInteger {
}

impl Integer for ParsedInteger {
fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self> {
if sign < 0 {
let signed = parser.parse_integer::<LargeSInt>(-1)?;
let signed = parser.parse_integer::<LargeSInt>(-1, base)?;

return if let Ok(x) = i8::try_from(signed) {
Ok(ParsedInteger::I8(x))
Expand All @@ -1542,7 +1550,7 @@ impl Integer for ParsedInteger {
};
}

let unsigned = parser.parse_integer::<LargeUInt>(1)?;
let unsigned = parser.parse_integer::<LargeUInt>(1, base)?;

if let Ok(x) = u8::try_from(unsigned) {
Ok(ParsedInteger::U8(x))
Expand Down
69 changes: 69 additions & 0 deletions tests/599_integer_suffix.rs
Comment thread
torkleyy marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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::<i32>("-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::<i32>("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::<i32>("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 }
}
})
);
}
Loading