diff options
| author | Earlopain <14981592+Earlopain@users.noreply.github.com> | 2025-09-02 13:29:35 +0200 |
|---|---|---|
| committer | Kevin Newton <kddnewton@gmail.com> | 2025-09-12 15:00:01 -0400 |
| commit | f2dbc4ec82a0e103ac1e3f64f5983540cdc75fd3 (patch) | |
| tree | b57c5d8ef36807b68ea34d4751eb522e717116a3 | |
| parent | 120d3b12a9981f547b07c937dd183c0abe77c6cb (diff) | |
[ruby/prism] [Bug #17398] Allow `private def hello = puts "Hello"`
This was a limitation of parse.y that prism intentionally replicated.
https://github.com/ruby/prism/commit/8fd12d594c
| -rw-r--r-- | prism/prism.c | 14 | ||||
| -rw-r--r-- | test/prism/errors/endless_method_command_call.txt | 3 | ||||
| -rw-r--r-- | test/prism/errors/private_endless_method.txt | 3 | ||||
| -rw-r--r-- | test/prism/errors_test.rb | 9 | ||||
| -rw-r--r-- | test/prism/fixtures/endless_methods_command_call.txt | 8 | ||||
| -rw-r--r-- | test/prism/fixtures_test.rb | 5 | ||||
| -rw-r--r-- | test/prism/lex_test.rb | 3 | ||||
| -rw-r--r-- | test/prism/locals_test.rb | 6 | ||||
| -rw-r--r-- | test/prism/ruby/parser_test.rb | 3 | ||||
| -rw-r--r-- | test/prism/ruby/ripper_test.rb | 5 | ||||
| -rw-r--r-- | test/prism/ruby/ruby_parser_test.rb | 5 |
11 files changed, 49 insertions, 15 deletions
diff --git a/prism/prism.c b/prism/prism.c index daf69d2ef9..337b77637b 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -19585,13 +19585,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_do_loop_stack_push(parser, false); statements = (pm_node_t *) pm_statements_node_create(parser); - // In endless method bodies, we need to handle command calls carefully. - // We want to allow command calls in assignment context but maintain - // the same binding power to avoid changing how operators are parsed. - // Note that we're intentionally NOT allowing code like `private def foo = puts "Hello"` - // because the original parser, parse.y, can't handle it and we want to maintain the same behavior - bool allow_command_call = (binding_power == PM_BINDING_POWER_ASSIGNMENT) || - (binding_power < PM_BINDING_POWER_COMPOSITION); + bool allow_command_call; + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) { + allow_command_call = accepts_command_call; + } else { + // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"` + allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION; + } pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); diff --git a/test/prism/errors/endless_method_command_call.txt b/test/prism/errors/endless_method_command_call.txt new file mode 100644 index 0000000000..e6a328c294 --- /dev/null +++ b/test/prism/errors/endless_method_command_call.txt @@ -0,0 +1,3 @@ +private :m, def hello = puts "Hello" + ^ unexpected string literal, expecting end-of-input + diff --git a/test/prism/errors/private_endless_method.txt b/test/prism/errors/private_endless_method.txt deleted file mode 100644 index 8aae5e0cd3..0000000000 --- a/test/prism/errors/private_endless_method.txt +++ /dev/null @@ -1,3 +0,0 @@ -private def foo = puts "Hello" - ^ unexpected string literal, expecting end-of-input - diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 62bbd8458b..9dd4aea728 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -100,6 +100,15 @@ module Prism end end + def test_private_endless_method + source = <<~RUBY + private def foo = puts "Hello" + RUBY + + assert_predicate Prism.parse(source, version: "3.4"), :failure? + assert_predicate Prism.parse(source), :success? + end + private def assert_errors(filepath) diff --git a/test/prism/fixtures/endless_methods_command_call.txt b/test/prism/fixtures/endless_methods_command_call.txt new file mode 100644 index 0000000000..91a9d156d5 --- /dev/null +++ b/test/prism/fixtures/endless_methods_command_call.txt @@ -0,0 +1,8 @@ +private def foo = puts "Hello" +private def foo = puts "Hello", "World" +private def foo = puts "Hello" do expr end +private def foo() = puts "Hello" +private def foo(x) = puts x +private def obj.foo = puts "Hello" +private def obj.foo() = puts "Hello" +private def obj.foo(x) = puts x diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 124a834317..b4b656fcf4 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -8,7 +8,6 @@ module Prism class FixturesTest < TestCase except = [] - if RUBY_VERSION < "3.3.0" # Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace # characters in the heredoc start. @@ -25,7 +24,9 @@ module Prism except << "whitequark/ruby_bug_19281.txt" end - except << "leading_logical.txt" if RUBY_VERSION < "3.5.0" + # Leaving these out until they are supported by parse.y. + except << "leading_logical.txt" + except << "endless_methods_command_call.txt" Fixture.each(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index abce18a0ad..4eacbab3e1 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -45,6 +45,9 @@ module Prism # https://bugs.ruby-lang.org/issues/20925 except << "leading_logical.txt" + # https://bugs.ruby-lang.org/issues/17398#note-12 + except << "endless_methods_command_call.txt" + Fixture.each(except: except) do |fixture| define_method(fixture.test_name) { assert_lex(fixture) } end diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index e0e9a45855..950e7118af 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -29,7 +29,11 @@ module Prism except = [ # Skip this fixture because it has a different number of locals because # CRuby is eliminating dead code. - "whitequark/ruby_bug_10653.txt" + "whitequark/ruby_bug_10653.txt", + + # Leaving these out until they are supported by parse.y. + "leading_logical.txt", + "endless_methods_command_call.txt" ] Fixture.each(except: except) do |fixture| diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 129c38a3b5..98740f0973 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -67,6 +67,9 @@ module Prism # Cannot yet handling leading logical operators. "leading_logical.txt", + + # Ruby >= 3.5 specific syntax + "endless_methods_command_call.txt", ] # These files contain code that is being parsed incorrectly by the parser diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 6372024878..39325137ba 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -29,7 +29,10 @@ module Prism "whitequark/lvar_injecting_match.txt", # Ripper fails to understand some structures that span across heredocs. - "spanning_heredoc.txt" + "spanning_heredoc.txt", + + # https://bugs.ruby-lang.org/issues/17398#note-12 + "endless_methods_command_call.txt", ] # Skip these tests that we haven't implemented yet. diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index f4f0f331fb..bcaed79791 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -74,7 +74,10 @@ module Prism "whitequark/ruby_bug_11989.txt", "whitequark/ruby_bug_18878.txt", "whitequark/ruby_bug_19281.txt", - "whitequark/slash_newline_in_heredocs.txt" + "whitequark/slash_newline_in_heredocs.txt", + + # Ruby >= 3.5 specific syntax + "endless_methods_command_call.txt", ] Fixture.each(except: failures) do |fixture| |
