summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEarlopain <14981592+Earlopain@users.noreply.github.com>2025-09-02 13:29:35 +0200
committerKevin Newton <kddnewton@gmail.com>2025-09-12 15:00:01 -0400
commitf2dbc4ec82a0e103ac1e3f64f5983540cdc75fd3 (patch)
treeb57c5d8ef36807b68ea34d4751eb522e717116a3
parent120d3b12a9981f547b07c937dd183c0abe77c6cb (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.c14
-rw-r--r--test/prism/errors/endless_method_command_call.txt3
-rw-r--r--test/prism/errors/private_endless_method.txt3
-rw-r--r--test/prism/errors_test.rb9
-rw-r--r--test/prism/fixtures/endless_methods_command_call.txt8
-rw-r--r--test/prism/fixtures_test.rb5
-rw-r--r--test/prism/lex_test.rb3
-rw-r--r--test/prism/locals_test.rb6
-rw-r--r--test/prism/ruby/parser_test.rb3
-rw-r--r--test/prism/ruby/ripper_test.rb5
-rw-r--r--test/prism/ruby/ruby_parser_test.rb5
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|