diff --git a/doc/AST_FORMAT.md b/doc/AST_FORMAT.md index ede684a1f..fcb1fe774 100644 --- a/doc/AST_FORMAT.md +++ b/doc/AST_FORMAT.md @@ -802,6 +802,31 @@ Format: ~~~~~~~~~~~~~~~~~ expression ~~~ +### "Endless" method + +Format: + +~~~ +(def_head :foo (args) (int 42)) +"def foo = 42" + ~~~ keyword + ~~~ name + ~~~~~~~~~~~~ expression +~~~ + + +### "Endless" singleton method + +Format: + +~~~ +(defs_head (self) :foo (args) (int 42)) +"def self.foo = 42" + ~~~ keyword + ~~~ name + ~~~~~~~~~~~~ expression +~~~ + ### Undefinition Format: diff --git a/lib/parser/meta.rb b/lib/parser/meta.rb index ab516aad5..22d1adb9f 100644 --- a/lib/parser/meta.rb +++ b/lib/parser/meta.rb @@ -16,7 +16,7 @@ module Meta op_asgn and_asgn ensure rescue arg_expr or_asgn back_ref nth_ref match_with_lvasgn match_current_line - module class sclass def defs undef alias args + module class sclass def defs def_e defs_e undef alias args cbase arg optarg restarg blockarg block_pass kwarg kwoptarg kwrestarg kwnilarg send csend super zsuper yield block and not or if when case while until while_post diff --git a/lib/parser/ruby28.y b/lib/parser/ruby28.y index d95ec3a09..46f509f3e 100644 --- a/lib/parser/ruby28.y +++ b/lib/parser/ruby28.y @@ -1232,10 +1232,10 @@ rule @context.push(:def) @current_arg_stack.push(nil) } - f_arglist bodystmt kEND + f_args_body { result = @builder.def_method(val[0], val[1], - val[3], val[4], val[5]) + *val[3]) @lexer.cmdarg.pop @lexer.cond.pop @@ -1255,10 +1255,10 @@ rule @context.push(:defs) @current_arg_stack.push(nil) } - f_arglist bodystmt kEND + f_args_body { result = @builder.def_singleton(val[0], val[1], val[2], - val[4], val[6], val[7], val[8]) + val[4], *val[6]) @lexer.cmdarg.pop @lexer.cond.pop @@ -2515,6 +2515,24 @@ keyword_variable: kNIL result = nil } + f_args_body: f_arglist bodystmt kEND + { + result = [ val[0], val[1], val[2] ] + } + | opt_f_arglist tEQL arg + { + result = [ val[0], val[2], nil ] + } + + opt_f_arglist: f_arglist + { + result = [ val[0] ] + } + | # nothing + { + result = [@builder.args(nil, [], nil)] + } + f_arglist: tLPAREN2 f_args rparen { result = @builder.args(val[0], val[1], val[2]) diff --git a/test/test_lexer.rb b/test/test_lexer.rb index f51373718..05f0cdc09 100644 --- a/test/test_lexer.rb +++ b/test/test_lexer.rb @@ -3569,6 +3569,16 @@ def test_ambiguous_integer_re :tIDENTIFIER, 're', [1, 3]) end + def test_endless_method + setup_lexer(28) + + assert_scanned('def foo = 42', + :kDEF, "def", [0, 3], + :tIDENTIFIER, 'foo', [4, 7], + :tEQL, "=", [8, 9], + :tINTEGER, 42, [10, 12]) + end + def lex_numbered_parameter(input) @lex.max_numparam_stack.push diff --git a/test/test_parser.rb b/test/test_parser.rb index 5fc1e5111..cc12f1864 100644 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -9428,4 +9428,54 @@ def test_parser_bug_645 %{}, SINCE_1_9) end + + def test_endless_method + assert_parses( + s(:def_e, :foo, + s(:args), + s(:int, 42)), + %q{def foo = 42}, + %q{~~~ keyword + | ~~~ name + |~~~~~~~~~~~~ expression}, + SINCE_2_8) + + assert_parses( + s(:def_e, :inc, + s(:args, s(:arg, :x)), + s(:send, + s(:lvar, :x), :+, + s(:int, 1))), + %q{def inc(x) = x + 1}, + %q{~~~ keyword + | ~~~ name + | ~~~ args + |~~~~~~~~~~~~~~~~~~ expression}, + SINCE_2_8) + + assert_parses( + s(:defs_e, s(:send, nil, :obj), :foo, + s(:args), + s(:int, 42)), + %q{def obj.foo = 42}, + %q{~~~ keyword + | ^ operator + | ~~~ name + |~~~~~~~~~~~~~~~~ expression}, + SINCE_2_8) + + assert_parses( + s(:defs_e, s(:send, nil, :obj), :inc, + s(:args, s(:arg, :x)), + s(:send, + s(:lvar, :x), :+, + s(:int, 1))), + %q{def obj.inc(x) = x + 1}, + %q{~~~ keyword + | ~~~ name + | ^ operator + | ~~~ args + |~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_2_8) + end end