Skip to content

Commit 978bff0

Browse files
committed
from_str_radix: Error if radix is > 18
Radix 19 and 20 introduce 'i' and 'j' as digits and this conflicts with how complex are parsed. Before this fix, it could parse `Num::from_str_radix("1ij + 1ij", 20)` in a pretty surprising way. The `Num` docs already describe a rule for our situation - we might not support every radix in our implementation; we can return an error in this case instead. A new (internal) error kind is introduced for this. It is necessary to preserve the previous panicking behaviour for radix > 36 (this is what primitive types do - but other types are allowed to do differently, including never panicking).
1 parent 3a89daa commit 978bff0

File tree

1 file changed

+58
-3
lines changed

1 file changed

+58
-3
lines changed

src/lib.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ where
13011301
neg_b = c == b'-';
13021302

13031303
if b.is_empty() || (neg_b && b.starts_with('-')) {
1304-
return Err(ParseComplexError::new());
1304+
return Err(ParseComplexError::expr_error());
13051305
}
13061306
break;
13071307
}
@@ -1328,7 +1328,7 @@ where
13281328
im = b;
13291329
neg_im = neg_b;
13301330
} else {
1331-
return Err(ParseComplexError::new());
1331+
return Err(ParseComplexError::expr_error());
13321332
}
13331333

13341334
// parse re
@@ -1367,7 +1367,25 @@ impl<T: Num + Clone> Num for Complex<T> {
13671367
type FromStrRadixErr = ParseComplexError<T::FromStrRadixErr>;
13681368

13691369
/// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T`
1370+
///
1371+
/// `radix` must be <= 18; larger radix would include *i* and *j* as digits,
1372+
/// which cannot be supported.
1373+
///
1374+
/// The conversion returns an error if 18 <= radix <= 36; it panics if radix > 36.
1375+
///
1376+
/// The elements of `T` are parsed using `Num::from_str_radix` too, and errors
1377+
/// (or panics) from that are reflected here as well.
13701378
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
1379+
assert!(
1380+
radix <= 36,
1381+
"from_str_radix: radix is too high (maximum 36)"
1382+
);
1383+
1384+
// larger radix would include 'i' and 'j' as digits, which cannot be supported
1385+
if radix > 18 {
1386+
return Err(ParseComplexError::unsupported_radix());
1387+
}
1388+
13711389
from_str_generic(s, |x| -> Result<T, T::FromStrRadixErr> {
13721390
T::from_str_radix(x, radix)
13731391
})
@@ -1446,15 +1464,22 @@ pub struct ParseComplexError<E> {
14461464
enum ComplexErrorKind<E> {
14471465
ParseError(E),
14481466
ExprError,
1467+
UnsupportedRadix,
14491468
}
14501469

14511470
impl<E> ParseComplexError<E> {
1452-
fn new() -> Self {
1471+
fn expr_error() -> Self {
14531472
ParseComplexError {
14541473
kind: ComplexErrorKind::ExprError,
14551474
}
14561475
}
14571476

1477+
fn unsupported_radix() -> Self {
1478+
ParseComplexError {
1479+
kind: ComplexErrorKind::UnsupportedRadix,
1480+
}
1481+
}
1482+
14581483
fn from_error(error: E) -> Self {
14591484
ParseComplexError {
14601485
kind: ComplexErrorKind::ParseError(error),
@@ -1469,6 +1494,7 @@ impl<E: Error> Error for ParseComplexError<E> {
14691494
match self.kind {
14701495
ComplexErrorKind::ParseError(ref e) => e.description(),
14711496
ComplexErrorKind::ExprError => "invalid or unsupported complex expression",
1497+
ComplexErrorKind::UnsupportedRadix => "unsupported radix for conversion",
14721498
}
14731499
}
14741500
}
@@ -1478,6 +1504,7 @@ impl<E: fmt::Display> fmt::Display for ParseComplexError<E> {
14781504
match self.kind {
14791505
ComplexErrorKind::ParseError(ref e) => e.fmt(f),
14801506
ComplexErrorKind::ExprError => "invalid or unsupported complex expression".fmt(f),
1507+
ComplexErrorKind::UnsupportedRadix => "unsupported radix for conversion".fmt(f),
14811508
}
14821509
}
14831510
}
@@ -1496,6 +1523,7 @@ mod test {
14961523
#![allow(non_upper_case_globals)]
14971524

14981525
use super::{Complex, Complex64};
1526+
use super::{ComplexErrorKind, ParseComplexError};
14991527
use core::f64;
15001528
use core::str::FromStr;
15011529

@@ -2560,6 +2588,33 @@ mod test {
25602588
test(Complex::new(15.0, 32.0), "1111+100000i", 2);
25612589
test(Complex::new(-15.0, -32.0), "-F-20i", 16);
25622590
test(Complex::new(-15.0, -32.0), "-1111-100000i", 2);
2591+
2592+
fn test_error(s: &str, radix: u32) -> ParseComplexError<<f64 as Num>::FromStrRadixErr> {
2593+
let res = Complex64::from_str_radix(s, radix);
2594+
2595+
res.expect_err(&format!("Expected failure on input {:?}", s))
2596+
}
2597+
2598+
let err = test_error("1ii", 19);
2599+
if let ComplexErrorKind::UnsupportedRadix = err.kind {
2600+
/* pass */
2601+
} else {
2602+
panic!("Expected failure on invalid radix, got {:?}", err);
2603+
}
2604+
2605+
let err = test_error("1 + 0", 16);
2606+
if let ComplexErrorKind::ExprError = err.kind {
2607+
/* pass */
2608+
} else {
2609+
panic!("Expected failure on expr error, got {:?}", err);
2610+
}
2611+
}
2612+
2613+
#[test]
2614+
#[should_panic(expected = "radix is too high")]
2615+
fn test_from_str_radix_fail() {
2616+
// ensure we preserve the underlying panic on radix > 36
2617+
let _complex = Complex64::from_str_radix("1", 37);
25632618
}
25642619

25652620
#[test]

0 commit comments

Comments
 (0)