Skip to content

fix: validate named backreferences against existing groups#38

Draft
toddr-bot wants to merge 1 commit intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-named-backref-validation
Draft

fix: validate named backreferences against existing groups#38
toddr-bot wants to merge 1 commit intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-named-backref-validation

Conversation

@toddr-bot
Copy link
Copy Markdown
Collaborator

@toddr-bot toddr-bot commented Apr 8, 2026

What

Reject named backreferences and named recursion that reference nonexistent capture groups — matching Perl's own "Reference to nonexistent named group" error.

Why

The parser accepted \k<foo>, \g{name}, (?P=name), (?&name), and (?P>name) without checking whether a named group with that name exists anywhere in the pattern. Perl rejects all of these. Numbered backrefs (\1, \g{N}) were already validated against maxpar, but the named equivalents had no validation at all.

How

  • Populate named_captures during both parser passes (SIZE_ONLY and tree-building). Previously it was only populated during the second pass, so forward references to later-defined groups would fail validation.
  • Add RPe_BGROUP error checks in all 7 named ref/recursion handlers (\k<>, \k'', \k{}, \g{name}, (?P=), (?&), (?P>)), guarded by !&SIZE_ONLY to match the existing numbered backref pattern.
  • Forward references like (?&foo)(?<foo>a) work correctly because the first pass records all names.

Testing

  • 13 new rejection tests (all named ref forms with nonexistent/wrong names)
  • 10 new acceptance tests (valid refs including forward references)
  • All 1214 existing tests pass unchanged

🤖 Generated with Claude Code


Quality Report

Changes: 2 files changed, 80 insertions(+), 9 deletions(-)

Code scan: clean

Tests: passed (OK)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

The parser accepted \k<foo>, \g{name}, (?P=name), (?&name), and
(?P>name) even when no matching named capture group existed anywhere
in the pattern. Perl itself rejects these with "Reference to
nonexistent named group".

Root cause: named_captures hash was only populated during the second
(tree-building) pass, and no validation checked it for backref/recursion
handlers. Numbered backrefs were already validated against maxpar.

Fix: populate named_captures during both passes (SIZE_ONLY uses maxpar,
tree-building uses nparen), then validate all named ref forms during the
second pass. Forward references work correctly because the first pass
has already recorded all group names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant