1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 13:07:46 +00:00

uucore: parser: num_parser: Return error if no digit has been parsed

This is mostly important when parsing digits like `.` and `0x.`
where we should return an error.

Fixes #7684.
This commit is contained in:
Nicolas Boichat 2025-04-08 14:21:12 +02:00
parent 8a5a2eed2a
commit e5eb004793

View file

@ -456,12 +456,12 @@ pub(crate) fn parse<'a>(
// Parse the integral part of the number // Parse the integral part of the number
let mut chars = rest.chars().enumerate().fuse().peekable(); let mut chars = rest.chars().enumerate().fuse().peekable();
let mut digits = BigUint::zero(); let mut digits: Option<BigUint> = None;
let mut scale = 0u64; let mut scale = 0u64;
let mut exponent = BigInt::zero(); let mut exponent = BigInt::zero();
while let Some(d) = chars.peek().and_then(|&(_, c)| base.digit(c)) { while let Some(d) = chars.peek().and_then(|&(_, c)| base.digit(c)) {
chars.next(); chars.next();
digits = digits * base as u8 + d; digits = Some(digits.unwrap_or_default() * base as u8 + d);
} }
// Parse fractional/exponent part of the number for supported bases. // Parse fractional/exponent part of the number for supported bases.
@ -472,7 +472,7 @@ pub(crate) fn parse<'a>(
chars.next(); chars.next();
while let Some(d) = chars.peek().and_then(|&(_, c)| base.digit(c)) { while let Some(d) = chars.peek().and_then(|&(_, c)| base.digit(c)) {
chars.next(); chars.next();
(digits, scale) = (digits * base as u8 + d, scale + 1); (digits, scale) = (Some(digits.unwrap_or_default() * base as u8 + d), scale + 1);
} }
} }
@ -509,8 +509,8 @@ pub(crate) fn parse<'a>(
} }
} }
// If nothing has been parsed, check if this is a special value, or declare the parsing unsuccessful // If no digit has been parsed, check if this is a special value, or declare the parsing unsuccessful
if let Some((0, _)) = chars.peek() { if digits.is_none() {
return if target == ParseTarget::Integral { return if target == ParseTarget::Integral {
Err(ExtendedParserError::NotNumeric) Err(ExtendedParserError::NotNumeric)
} else { } else {
@ -518,6 +518,8 @@ pub(crate) fn parse<'a>(
}; };
} }
let mut digits = digits.unwrap();
if let Some((_, ch)) = chars.peek() { if let Some((_, ch)) = chars.peek() {
if let Some(times) = allowed_suffixes if let Some(times) = allowed_suffixes
.iter() .iter()
@ -529,7 +531,8 @@ pub(crate) fn parse<'a>(
} }
} }
let ebd_result = construct_extended_big_decimal(digits, negative, base, scale, exponent); let ebd_result =
construct_extended_big_decimal(digits, negative, base, scale, exponent);
// Return what has been parsed so far. If there are extra characters, mark the // Return what has been parsed so far. If there are extra characters, mark the
// parsing as a partial match. // parsing as a partial match.
@ -625,6 +628,15 @@ mod tests {
i64::extended_parse(&format!("{}", i64::MIN as i128 - 1)), i64::extended_parse(&format!("{}", i64::MIN as i128 - 1)),
Err(ExtendedParserError::Overflow(i64::MIN)) Err(ExtendedParserError::Overflow(i64::MIN))
)); ));
assert!(matches!(
i64::extended_parse(""),
Err(ExtendedParserError::NotNumeric)
));
assert!(matches!(
i64::extended_parse("."),
Err(ExtendedParserError::NotNumeric)
));
} }
#[test] #[test]
@ -811,6 +823,16 @@ mod tests {
ExtendedBigDecimal::extended_parse(&format!("-0e{}", i64::MIN + 2)), ExtendedBigDecimal::extended_parse(&format!("-0e{}", i64::MIN + 2)),
Ok(ExtendedBigDecimal::MinusZero) Ok(ExtendedBigDecimal::MinusZero)
); );
/* Invalid numbers */
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse(".")
);
} }
#[test] #[test]
@ -887,6 +909,16 @@ mod tests {
ExtendedBigDecimal::MinusZero ExtendedBigDecimal::MinusZero
)) ))
)); ));
// TODO: GNU coreutils treats these 2 as partial match.
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("0x")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("0x.")
);
} }
#[test] #[test]
@ -935,6 +967,20 @@ mod tests {
ebd == ExtendedBigDecimal::zero(), ebd == ExtendedBigDecimal::zero(),
_ => false, _ => false,
}); });
assert!(match ExtendedBigDecimal::extended_parse("0b") {
Err(ExtendedParserError::PartialMatch(ebd, "b")) => ebd == ExtendedBigDecimal::zero(),
_ => false,
});
assert!(match ExtendedBigDecimal::extended_parse("0b.") {
Err(ExtendedParserError::PartialMatch(ebd, "b.")) => ebd == ExtendedBigDecimal::zero(),
_ => false,
});
// TODO: GNU coreutils treats this as partial match.
assert_eq!(
Err(ExtendedParserError::NotNumeric),
u64::extended_parse("0b")
);
} }
#[test] #[test]