Skip to content

Commit 8fd12d5

Browse files
Earlopainkddnewton
authored andcommitted
[Bug #17398] Allow private def hello = puts "Hello"
This was a limitation of parse.y that prism intentionally replicated.
1 parent 8955403 commit 8fd12d5

12 files changed

Lines changed: 524 additions & 15 deletions

snapshots/endless_methods_command_call.txt

Lines changed: 475 additions & 0 deletions
Large diffs are not rendered by default.

src/prism.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19585,13 +19585,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1958519585
pm_do_loop_stack_push(parser, false);
1958619586
statements = (pm_node_t *) pm_statements_node_create(parser);
1958719587

19588-
// In endless method bodies, we need to handle command calls carefully.
19589-
// We want to allow command calls in assignment context but maintain
19590-
// the same binding power to avoid changing how operators are parsed.
19591-
// Note that we're intentionally NOT allowing code like `private def foo = puts "Hello"`
19592-
// because the original parser, parse.y, can't handle it and we want to maintain the same behavior
19593-
bool allow_command_call = (binding_power == PM_BINDING_POWER_ASSIGNMENT) ||
19594-
(binding_power < PM_BINDING_POWER_COMPOSITION);
19588+
bool allow_command_call;
19589+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
19590+
allow_command_call = accepts_command_call;
19591+
} else {
19592+
// Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"`
19593+
allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION;
19594+
}
1959519595

1959619596
pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1));
1959719597

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
private :m, def hello = puts "Hello"
2+
^ unexpected string literal, expecting end-of-input
3+

test/prism/errors/private_endless_method.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

test/prism/errors_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ def foo(bar: bar) = 42
100100
end
101101
end
102102

103+
def test_private_endless_method
104+
source = <<~RUBY
105+
private def foo = puts "Hello"
106+
RUBY
107+
108+
assert_predicate Prism.parse(source, version: "3.4"), :failure?
109+
assert_predicate Prism.parse(source), :success?
110+
end
111+
103112
private
104113

105114
def assert_errors(filepath)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
private def foo = puts "Hello"
2+
private def foo = puts "Hello", "World"
3+
private def foo = puts "Hello" do expr end
4+
private def foo() = puts "Hello"
5+
private def foo(x) = puts x
6+
private def obj.foo = puts "Hello"
7+
private def obj.foo() = puts "Hello"
8+
private def obj.foo(x) = puts x

test/prism/fixtures_test.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ module Prism
88
class FixturesTest < TestCase
99
except = []
1010

11-
1211
if RUBY_VERSION < "3.3.0"
1312
# Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace
1413
# characters in the heredoc start.
@@ -25,7 +24,9 @@ class FixturesTest < TestCase
2524
except << "whitequark/ruby_bug_19281.txt"
2625
end
2726

28-
except << "leading_logical.txt" if RUBY_VERSION < "3.5.0"
27+
# Leaving these out until they are supported by parse.y.
28+
except << "leading_logical.txt"
29+
except << "endless_methods_command_call.txt"
2930

3031
Fixture.each(except: except) do |fixture|
3132
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }

test/prism/lex_test.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class LexTest < TestCase
4545
# https://bugs.ruby-lang.org/issues/20925
4646
except << "leading_logical.txt"
4747

48+
# https://bugs.ruby-lang.org/issues/17398#note-12
49+
except << "endless_methods_command_call.txt"
50+
4851
Fixture.each(except: except) do |fixture|
4952
define_method(fixture.test_name) { assert_lex(fixture) }
5053
end

test/prism/locals_test.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ class LocalsTest < TestCase
2929
except = [
3030
# Skip this fixture because it has a different number of locals because
3131
# CRuby is eliminating dead code.
32-
"whitequark/ruby_bug_10653.txt"
32+
"whitequark/ruby_bug_10653.txt",
33+
34+
# Leaving these out until they are supported by parse.y.
35+
"leading_logical.txt",
36+
"endless_methods_command_call.txt"
3337
]
3438

3539
Fixture.each(except: except) do |fixture|

test/prism/ruby/parser_test.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ class ParserTest < TestCase
6767

6868
# Cannot yet handling leading logical operators.
6969
"leading_logical.txt",
70+
71+
# Ruby >= 3.5 specific syntax
72+
"endless_methods_command_call.txt",
7073
]
7174

7275
# These files contain code that is being parsed incorrectly by the parser

0 commit comments

Comments
 (0)