Skip to content

Fix Issue 11025 - case-insensitive backreferences in std.regex#11070

Open
niy-ati wants to merge 5 commits into
dlang:masterfrom
niy-ati:fix-11025-regex-backref-casefold
Open

Fix Issue 11025 - case-insensitive backreferences in std.regex#11070
niy-ati wants to merge 5 commits into
dlang:masterfrom
niy-ati:fix-11025-regex-backref-casefold

Conversation

@niy-ati

@niy-ati niy-ati commented Jul 18, 2026

Copy link
Copy Markdown

Summary

  • Under /i, IR.Backref compared captures with ==, so (.)\1 matched BB but not Aa.
  • Compare with simple case folding when RegexOption.casefold is set (backtracking, Thompson, and ctRegex).
  • Add regression vectors for issue 11025.

Fixes #11025

Test plan

  • Added TestVectors for Aa / aA / BB / non-match / multi-char / (?i).
  • CI

Under /i, literals are casefolded at parse time but IR.Backref still
compared codepoints with ==. Use simple case folding when matching
backrefs in the backtracking and Thompson engines (and ctRegex).
@niy-ati
niy-ati requested a review from DmitryOlshansky as a code owner July 18, 2026 15:49
Comment thread std/regex/internal/tests.d Outdated
TestVectors( `[adzУ-Я]{4}`, "DzюЯ", "y", "$&", "DzюЯ", "i"),
TestVectors( `\p{L}\p{Lu}{10}`, "абвгдеЖЗИКЛ", "y", "$&", "абвгдеЖЗИКЛ", "i"),
TestVectors( `(?:Dåb){3}`, "DåbDÅBdÅb", "y", "$&", "DåbDÅBdÅb", "i"),
// Issue 11025: backrefs must casefold under /i

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Issue 11025: backrefs must casefold under /i
// https://github.com/dlang/phobos/issues/11025 backrefs must casefold under /i

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the comment to link the issue.

Comment thread std/regex/internal/ir.d Outdated
{
if (a == b)
return true;
foreach (c; simpleCaseFoldings(a))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slow, see sicmp for how to do proper casefold compare with trie tables

@DmitryOlshansky

Copy link
Copy Markdown
Member

What I meant was not using sicmp as is but getting its internal compare function and using that to compare two dchar case-insensitively. Plain call to sicmp brings its own overheads which are not acceptable.

@niy-ati

niy-ati commented Jul 20, 2026

Copy link
Copy Markdown
Author

Thanks for the clarification ,
I’ll add/reuse the internal simple-case per-dchar compare from sicmp (trie tables) and use that from equalCasefold in std.regex.internal.ir, without calling sicmp itself. I’ll refactor so we don’t duplicate the trie logic if that fits Phobos style.

@DmitryOlshansky

Copy link
Copy Markdown
Member

LGTM

@kubo39

kubo39 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Literals are folded at parse time using the flag active at their position, but a backref here uses one global flag for the entire pattern.
That diverges as soon as (?i)/(?-i) toggles case-insensitivity around the backref:

// over-match: (?i) appears *after* the backref, so \1 should stay case-sensitive
assert( matchFirst("Aax", regex(`(.)\1(?i)X`)).empty);   // fails: matches "Aax"

// under-match: casefold is on at \1 but cleared before X
assert(!matchFirst("AaX", regex(`(?i)(.)\1(?-i)X`)).empty); // fails: no match

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Std.regex: backreferences do not match text with different casing

5 participants