Skip to content

fix: detect Statement::Variable in open() without parens#370

Draft
toddr-bot wants to merge 2 commits into
masterfrom
koan.toddr.bot/fix-issue-225
Draft

fix: detect Statement::Variable in open() without parens#370
toddr-bot wants to merge 2 commits into
masterfrom
koan.toddr.bot/fix-issue-225

Conversation

@toddr-bot

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

Copy link
Copy Markdown
Collaborator

Summary

When open my $fh, "<", $path; is parsed without parentheses, PPI did not wrap the my $fh portion in a PPI::Statement::Variable, making the variable declaration invisible to $doc->find('Statement::Variable'). The parenthesized form open(my $fh, ...) already worked correctly.

This fix teaches the lexer to detect my/local/our/state declarations inside plain statements and create a child PPI::Statement::Variable node, consistent with the parenthesized behavior.

Fixes #225

Changes

  • _lex_statement() now detects variable declaration keywords inside plain PPI::Statement nodes and creates a child PPI::Statement::Variable when confirmed by a peek-ahead (next significant token is a symbol or ()
  • PPI::Statement::Variable overrides __LEXER__normal to return false when nested inside another statement, preventing it from consuming the parent's ;
  • _continues() handles nested Variable statements by ending at ; (rolling it back to the parent)

Test plan

  • Added tests for open my $fh without parens: verifies find('Statement::Variable') returns the declaration with correct type() and variables()
  • Added test for print my $x = 1; (another builtin without parens)
  • Added edge case test: func my => 1; (fat comma) does NOT create a Variable
  • Round-trip safety verified for all cases
  • Full test suite passes (53,101 tests, 69 files, 0 failures)

Generated by Kōan /fix


Quality Report

Changes: 3 files changed, 108 insertions(+), 1 deletion(-)

Code scan: clean

Tests: skipped

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

toddr-bot and others added 2 commits April 26, 2026 21:12
When open() is called without parentheses (e.g., `open my $fh, "<", $path`),
PPI does not wrap the `my $fh` portion in a PPI::Statement::Variable, even
though the parenthesized form `open(my $fh, "<", $path)` does. This makes
variable declarations invisible to `$doc->find('Statement::Variable')`.

Tests are marked $TODO and will pass once the lexer fix is applied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…225)

When a variable declaration keyword (my/local/our/state) appears inside
a plain PPI::Statement that already has children (e.g. `open my $fh`),
the lexer now creates a child PPI::Statement::Variable to wrap the
declaration. This makes `$doc->find('Statement::Variable')` correctly
detect variable declarations in builtin calls without parentheses,
matching the behavior when parentheses are used.

Three changes work together:
- _lex_statement() detects my/local/our/state inside plain statements
  and creates a child Variable, confirmed by peeking ahead for a
  symbol or open paren
- Statement::Variable overrides __LEXER__normal to return false when
  nested inside another statement, preventing it from consuming the
  semicolon
- _continues() returns false for ';' on nested Variable statements,
  rolling the semicolon back to the parent

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.

PPI::Statement::Variable not detected inside an open() call without parenthesis

1 participant