Skip to content

exec/test with lastIndex > 0 slices the haystack — ^, \b and lookbehind lose their context #9429

Description

@proggeramlug

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:

const r = /^b/g;  r.lastIndex = 1;  r.exec("ab")      // node: null    perry: "b"
const l = /(?<=a)b/g; l.lastIndex = 1; l.exec("ab")   // node: "b"     perry: null

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions