You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
exec/test with lastIndex > 0 slices the haystack — ^, \b and lookbehind lose their context #9429
regex/exec.rs:61 does &str_data[search_start_byte..] when lastIndex > 0, so every zero-width assertion is evaluated against the slice rather than the real haystack. Wrong in both directions, and no m flag is needed:
Under /m it is severe — while ((m = /^/gm.exec("one\r\ntwo"))) yields an empty match at every index.
Why this is worth doing next
It limits how much of #9408's fix reaches real code. #9427 made multiline ^/$ recognise every LineTerminator, but any exec-loop with a sticky/global regex still evaluates those anchors against a slice — so the correct anchor semantics are undone at the call site for exactly the iteration pattern that scans line by line. Together they're one user-visible behaviour; separately, each is half fixed.
The fix, already scoped
Use the positional search APIs on the full haystack instead of slicing:
regex::Regex::captures_at
fancy_regex::Regex::captures_from_pos
regress::Regex::find_from
with the sticky check becoming start() == search_start_byte, and OwnedExecMatch::from_*'s base offset becoming 0 (it currently compensates for the slice).
Found while fixing #9408/#9409; deliberately left out of #9427 as a third independent root cause, with the exclusion documented in that PR's fixture.
Fixed by PR #9437: positional search on the full haystack in all three engine lanes (captures_at / captures_from_pos / find_from), sticky as start() == search_start, and the OwnedExecMatch constructors lost the base-offset parameter so a slice cannot come back without a signature change. Also fixed alongside: lastIndex > length is now a UTF-16 no-match (RegExpBuiltinExec 12.a) in both exec and — per review — matchAll. test_gap_9429_regexp_exec_lastindex_context.ts (78 diff lines before). Verified on main 0a1c137d90 with a fresh build: fixture byte-identical to node --experimental-strip-types (stdout, stderr, and exit code).
Activity
fix(runtime): exec/test at lastIndex > 0 keep the whole subject (Perr…
fix(runtime): a global scan keeps the empty match at a match's end (P…
fix(runtime): exec/test keep the whole subject, and a global scan kee…
fix(runtime): matchAll past the subject is empty, not clamped to the …
proggeramlug commentedon Sep 2, 2026
Fixed by PR #9437: positional search on the full haystack in all three engine lanes (
captures_at/captures_from_pos/find_from), sticky asstart() == search_start, and theOwnedExecMatchconstructors lost the base-offset parameter so a slice cannot come back without a signature change. Also fixed alongside:lastIndex > lengthis now a UTF-16 no-match (RegExpBuiltinExec 12.a) in bothexecand — per review —matchAll.test_gap_9429_regexp_exec_lastindex_context.ts(78 diff lines before). Verified on main0a1c137d90with a fresh build: fixture byte-identical tonode --experimental-strip-types(stdout, stderr, and exit code).