Skip to content

Fix RuntimeError on () as a method arg or hash splat value#445

Merged
mame merged 1 commit intoruby:masterfrom
mame:fix-anonymous-rest-collision-with-empty-parens
Apr 28, 2026
Merged

Fix RuntimeError on () as a method arg or hash splat value#445
mame merged 1 commit intoruby:masterfrom
mame:fix-anonymous-rest-collision-with-empty-parens

Conversation

@mame
Copy link
Copy Markdown
Member

@mame mame commented Apr 28, 2026

Summary

Follow-up to #441. After empty parentheses () started producing a DummyNilNode, several call/hash sites began misinterpreting () as anonymous-forwarding syntax, raising RuntimeError: *anonymous_rest or **anonymous_keyword from LocalEnv#get_var.

Reproducers (all crash on current master):

foo(())     # RuntimeError: *anonymous_rest
foo(*())    # RuntimeError: *anonymous_rest
{ **() }    # RuntimeError: **anonymous_keyword

Root cause

CallBaseNode and HashNode were overloading DummyNilNode as a sentinel for bare * / ** forwarding. The placeholder created at parse time when the raw arg/value was nil (the anonymous-forwarding case) was later detected with is_a?(DummyNilNode) at install time. Once () also became a DummyNilNode, the two cases collided.

Reported by @sinsoku as the "separate issue" note in #441.

Fix

Use a plain nil as the forwarding-placeholder in @positional_args and @vals, and check arg.nil? / val.nil? at install time. DummyNilNode is then reserved for actual nil-valued expressions like (), with no semantic overlap.

Test plan

  • scenario/misc/parens.rb extended with the new cases (also covers @sinsoku's other suggestions: [()] and if (); 1; end)
  • rake test passes (425 tests, 817 assertions)
  • Existing anonymous-rest forwarding (scenario/args/anonymous_rest.rb) still passes

`CallBaseNode` and `HashNode` used `DummyNilNode` as a sentinel for
anonymous rest forwarding (bare `*`) and anonymous keyword splat
forwarding (bare `**`), checking via `is_a?(DummyNilNode)` at install
time. Once empty parentheses `()` started producing `DummyNilNode`
(via the parentheses_node unwrap in AST.create_node), passing `()` as
a method argument or hash splat value was misinterpreted as anonymous
forwarding, raising `RuntimeError: *anonymous_rest` or
`**anonymous_keyword` from `LocalEnv#get_var`.

Use plain `nil` as the placeholder for those forwarding cases instead,
so `DummyNilNode` is reserved for actual nil-valued expressions.

Also extend `scenario/misc/parens.rb` with cases where empty parens
appear as an array element, an if condition, a method argument, a
splatted argument, and a hash double-splat value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mame mame enabled auto-merge (rebase) April 28, 2026 13:28
@mame mame merged commit ff8a516 into ruby:master Apr 28, 2026
6 checks passed
@mame mame deleted the fix-anonymous-rest-collision-with-empty-parens branch April 28, 2026 13:46
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