fix(minifier): coerce omitted indexOf search argument (#26149)

## Summary

- Apply `ToString(undefined)` when `String.prototype.indexOf` or `lastIndexOf` is called without a search argument.
- Keep folding strings without the resulting `"undefined"` substring to `-1`.
- Add focused helper and minifier regression coverage.

```js
"undefined".indexOf();     // 0
"undefined".lastIndexOf(); // 0
"abcdef".indexOf();        // -1
"abcdef".lastIndexOf();    // -1
```
This commit is contained in:
camc314 committed 2026-08-28 14:18:01 +00:00
1 parent dc7398beae
commit a625378066
3 files changed
+9 -5

No files matched your search

+2 -3
View File
@@ -10,9 +10,7 @@ impl StringIndexOf for &str {
#[expect(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
fn index_of(&self, search_value: Option<&str>, from_index: Option<f64>) -> isize {
let from_index = from_index.map_or(0, |x| x.to_int_32().max(0)) as usize;
let Some(search_value) = search_value else {
return -1;
};
let search_value = search_value.unwrap_or("undefined");
let result = self.chars().skip(from_index).collect::<String>().find(search_value);
result.map(|index| index + from_index).map_or(-1, |index| index as isize)
}
@@ -45,6 +43,7 @@ mod test {
assert_eq!("test test test".index_of(Some("test"), Some(-1_073_741_825.0)), 0);
assert_eq!("test test test".index_of(Some("test"), Some(0.0)), 0);
assert_eq!("test test test".index_of(Some("notpresent"), Some(0.0)), -1);
assert_eq!("undefined".index_of(None, Some(0.0)), 0);
assert_eq!("test test test".index_of(None, Some(0.0)), -1);
}
}
@@ -9,7 +9,7 @@ pub trait StringLastIndexOf {
impl StringLastIndexOf for &str {
#[expect(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
fn last_index_of(&self, search_value: Option<&str>, from_index: Option<f64>) -> isize {
let Some(search_value) = search_value else { return -1 };
let search_value = search_value.unwrap_or("undefined");
let from_index =
from_index.map_or(usize::MAX, |x| x.to_int_32().max(0) as usize + search_value.len());
self.chars()
@@ -34,7 +34,8 @@ mod test {
assert_eq!("test test test".last_index_of(Some("test"), Some(4.0)), 0);
assert_eq!("test test test".last_index_of(Some("test"), Some(0.0)), 0);
assert_eq!("test test test".last_index_of(Some("notpresent"), Some(0.0)), -1);
assert_eq!("test test test".last_index_of(None, Some(1.0)), -1);
assert_eq!("undefined".last_index_of(None, None), 0);
assert_eq!("test test test".last_index_of(None, None), -1);
assert_eq!("abcdef".last_index_of(Some("b"), None), 1);
}
}
@@ -18,6 +18,8 @@ fn test_string_index_of() {
test("x = 'abcdefbe'.indexOf('b', 2)", "x = 6");
test("x = 'abcdef'.indexOf('bcd')", "x = 1");
test("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13");
test("x = 'undefined'.indexOf()", "x = 0");
test("x = 'abcdef'.indexOf()", "x = -1");
test_same("x = 'abcdef'.indexOf(...a, 1)");
test_same("x = 'abcdef'.indexOf('b', ...a)");
test_same("x = 'abcdef'.indexOf(a, 1)");
@@ -26,6 +28,8 @@ fn test_string_index_of() {
test("x = 'abcdef'.lastIndexOf('b')", "x = 1");
test("x = 'abcdefbe'.lastIndexOf('b')", "x = 6");
test("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1");
test("x = 'undefined'.lastIndexOf()", "x = 0");
test("x = 'abcdef'.lastIndexOf()", "x = -1");
test("x = 'abc1def'.indexOf(1)", "x = 3");
test("x = 'abcNaNdef'.indexOf(NaN)", "x = 3");