mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
fix(ecmascript): trim trailing whitespace in string to number (#26148)
## Summary `StringToNumber` previously removed ECMAScript whitespace only from the beginning of a string. Legal trailing whitespace therefore remained in the value passed to Rust’s numeric parsers, causing valid JavaScript numeric strings to produce `NaN`. For example: ```js "1 " * 2 // incorrectly folded to NaN "0x10 " - 0 // incorrectly folded to NaN ``` This changes `trim_start_matches` to `trim_matches`, applying the existing ECMAScript whitespace predicate to both ends of the string. After the change: ```js "1 " * 2 // folds to 2 "0x10 " - 0 // folds to 16 ``` The custom predicate remains important because ECMAScript and Rust define whitespace differently. Tests cover every ECMAScript whitespace character and verify that non-ECMAScript whitespace such as U+0085 is not accepted.
This commit is contained in:
1 parent
243b685c3d
commit
dc7398beae
2 files changed
+27
-1
No files matched your search
@@ -9,7 +9,7 @@ pub trait StringToNumber {
|
||||
/// <https://tc39.es/ecma262/#sec-stringtonumber>
|
||||
impl StringToNumber for &str {
|
||||
fn string_to_number(&self) -> f64 {
|
||||
let s = self.trim_start_matches(is_str_white_space_char);
|
||||
let s = self.trim_matches(is_str_white_space_char);
|
||||
match s {
|
||||
"" => return 0.0,
|
||||
"-Infinity" => return f64::NEG_INFINITY,
|
||||
@@ -74,3 +74,27 @@ impl StringToNumber for &str {
|
||||
fn is_str_white_space_char(c: char) -> bool {
|
||||
is_white_space(c) || is_line_terminator(c)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::StringToNumber;
|
||||
|
||||
#[expect(clippy::float_cmp)]
|
||||
#[test]
|
||||
fn trims_ecmascript_whitespace_from_both_ends() {
|
||||
const WHITESPACE: &str = "\u{0009}\u{000B}\u{000C}\u{0020}\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{202F}\u{205F}\u{3000}\u{FEFF}\u{000A}\u{000D}\u{2028}\u{2029}";
|
||||
|
||||
assert_eq!(format!("{WHITESPACE}1{WHITESPACE}").as_str().string_to_number(), 1.0);
|
||||
assert_eq!(format!("{WHITESPACE}0x10{WHITESPACE}").as_str().string_to_number(), 16.0);
|
||||
assert_eq!(
|
||||
format!("{WHITESPACE}Infinity{WHITESPACE}").as_str().string_to_number(),
|
||||
f64::INFINITY
|
||||
);
|
||||
assert_eq!(format!("{WHITESPACE}{WHITESPACE}").as_str().string_to_number(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_trim_non_ecmascript_whitespace() {
|
||||
assert!("1\u{0085}".string_to_number().is_nan());
|
||||
}
|
||||
}
|
||||
@@ -1039,11 +1039,13 @@ fn test_fold_numeric_expression_only_if_shorter() {
|
||||
#[test]
|
||||
fn test_fold_sub() {
|
||||
fold("x = 10 - 20", "x = -10");
|
||||
fold("x = '0x10 ' - 0", "x = 16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_multiply() {
|
||||
fold("x = 2.25 * 3", "x = 6.75");
|
||||
fold("x = '1 ' * 2", "x = 2");
|
||||
fold_same("z = x * y");
|
||||
fold_same("x = f() * 2");
|
||||
fold_same("x = y * 5");
|
||||
|
||||
Reference in new issue
Block a user