ruby-changes:70213
From: Nobuyoshi <ko1@a...>
Date: Wed, 15 Dec 2021 07:01:40 +0900 (JST)
Subject: [ruby-changes:70213] aa7c4c37d7 (master): Fix arg_forward without parentheses [Bug #18267]
https://git.ruby-lang.org/ruby.git/commit/?id=aa7c4c37d7 From aa7c4c37d7752af4897567dec1b0745873b0457a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Thu, 18 Nov 2021 21:41:46 +0900 Subject: Fix arg_forward without parentheses [Bug #18267] --- parse.y | 40 +++++++++++++++++++++++++++++++--------- test/ruby/test_syntax.rb | 25 ++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/parse.y b/parse.y index 31067947a93..0bf7b9c9aff 100644 --- a/parse.y +++ b/parse.y @@ -64,6 +64,7 @@ enum shareability { https://github.com/ruby/ruby/blob/trunk/parse.y#L64 struct lex_context { unsigned int in_defined: 1; unsigned int in_kwarg: 1; + unsigned int in_argdef: 1; unsigned int in_def: 1; unsigned int in_class: 1; BITFIELD(enum shareability, shareable_constant_value, 2); @@ -1817,7 +1818,12 @@ defn_head : k_def def_name https://github.com/ruby/ruby/blob/trunk/parse.y#L1818 } ; -defs_head : k_def singleton dot_or_colon {SET_LEX_STATE(EXPR_FNAME);} def_name +defs_head : k_def singleton dot_or_colon + { + SET_LEX_STATE(EXPR_FNAME); + p->ctxt.in_argdef = 1; + } + def_name { SET_LEX_STATE(EXPR_ENDFN|EXPR_LABEL); /* force for args */ $$ = $5; @@ -3396,6 +3402,7 @@ k_module : keyword_module https://github.com/ruby/ruby/blob/trunk/parse.y#L3402 k_def : keyword_def { token_info_push(p, "def", &@$); + p->ctxt.in_argdef = 1; } ; @@ -3591,6 +3598,8 @@ f_any_kwrest : f_kwrest https://github.com/ruby/ruby/blob/trunk/parse.y#L3598 | f_no_kwarg {$$ = ID2VAL(idNil);} ; +f_eq : {p->ctxt.in_argdef = 0;} '='; + block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); @@ -3703,6 +3712,7 @@ block_param_def : '|' opt_bv_decl '|' https://github.com/ruby/ruby/blob/trunk/parse.y#L3712 { p->cur_arg = 0; p->max_numparam = ORDINAL_PARAM; + p->ctxt.in_argdef = 0; /*%%%*/ $$ = 0; /*% %*/ @@ -3712,6 +3722,7 @@ block_param_def : '|' opt_bv_decl '|' https://github.com/ruby/ruby/blob/trunk/parse.y#L3722 { p->cur_arg = 0; p->max_numparam = ORDINAL_PARAM; + p->ctxt.in_argdef = 0; /*%%%*/ $$ = $2; /*% %*/ @@ -3792,6 +3803,7 @@ lambda : tLAMBDA https://github.com/ruby/ruby/blob/trunk/parse.y#L3803 f_larglist : '(' f_args opt_bv_decl ')' { + p->ctxt.in_argdef = 0; /*%%%*/ $$ = $2; p->max_numparam = ORDINAL_PARAM; @@ -3800,6 +3812,7 @@ f_larglist : '(' f_args opt_bv_decl ')' https://github.com/ruby/ruby/blob/trunk/parse.y#L3812 } | f_args { + p->ctxt.in_argdef = 0; /*%%%*/ if (!args_info_empty_p($1->nd_ainfo)) p->max_numparam = ORDINAL_PARAM; @@ -5122,6 +5135,7 @@ superclass : '<' https://github.com/ruby/ruby/blob/trunk/parse.y#L5135 f_opt_paren_args: f_paren_args | none { + p->ctxt.in_argdef = 0; $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0); $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0); } @@ -5135,6 +5149,7 @@ f_paren_args : '(' f_args rparen https://github.com/ruby/ruby/blob/trunk/parse.y#L5149 /*% ripper: paren!($2) %*/ SET_LEX_STATE(EXPR_BEG); p->command_start = TRUE; + p->ctxt.in_argdef = 0; } ; @@ -5142,11 +5157,13 @@ f_arglist : f_paren_args https://github.com/ruby/ruby/blob/trunk/parse.y#L5157 | { $<ctxt>$ = p->ctxt; p->ctxt.in_kwarg = 1; + p->ctxt.in_argdef = 1; SET_LEX_STATE(p->lex.state|EXPR_LABEL); /* force for args */ } f_args term { p->ctxt.in_kwarg = $<ctxt>1.in_kwarg; + p->ctxt.in_argdef = 0; $$ = $2; SET_LEX_STATE(EXPR_BEG); p->command_start = TRUE; @@ -5363,6 +5380,7 @@ f_label : tLABEL https://github.com/ruby/ruby/blob/trunk/parse.y#L5380 arg_var(p, formal_argument(p, $1)); p->cur_arg = get_id($1); p->max_numparam = ORDINAL_PARAM; + p->ctxt.in_argdef = 0; $$ = $1; } ; @@ -5370,6 +5388,7 @@ f_label : tLABEL https://github.com/ruby/ruby/blob/trunk/parse.y#L5388 f_kw : f_label arg_value { p->cur_arg = 0; + p->ctxt.in_argdef = 1; /*%%%*/ $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$); /*% %*/ @@ -5378,6 +5397,7 @@ f_kw : f_label arg_value https://github.com/ruby/ruby/blob/trunk/parse.y#L5397 | f_label { p->cur_arg = 0; + p->ctxt.in_argdef = 1; /*%%%*/ $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$); /*% %*/ @@ -5387,6 +5407,7 @@ f_kw : f_label arg_value https://github.com/ruby/ruby/blob/trunk/parse.y#L5407 f_block_kw : f_label primary_value { + p->ctxt.in_argdef = 1; /*%%%*/ $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$); /*% %*/ @@ -5394,6 +5415,7 @@ f_block_kw : f_label primary_value https://github.com/ruby/ruby/blob/trunk/parse.y#L5415 } | f_label { + p->ctxt.in_argdef = 1; /*%%%*/ $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$); /*% %*/ @@ -5464,9 +5486,10 @@ f_kwrest : kwrest_mark tIDENTIFIER https://github.com/ruby/ruby/blob/trunk/parse.y#L5486 } ; -f_opt : f_arg_asgn '=' arg_value +f_opt : f_arg_asgn f_eq arg_value { p->cur_arg = 0; + p->ctxt.in_argdef = 1; /*%%%*/ $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$); /*% %*/ @@ -5474,9 +5497,10 @@ f_opt : f_arg_asgn '=' arg_value https://github.com/ruby/ruby/blob/trunk/parse.y#L5497 } ; -f_block_opt : f_arg_asgn '=' primary_value +f_block_opt : f_arg_asgn f_eq primary_value { p->cur_arg = 0; + p->ctxt.in_argdef = 1; /*%%%*/ $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$); /*% %*/ @@ -9659,13 +9683,11 @@ parser_yylex(struct parser_params *p) https://github.com/ruby/ruby/blob/trunk/parse.y#L9683 SET_LEX_STATE(EXPR_BEG); if ((c = nextc(p)) == '.') { if ((c = nextc(p)) == '.') { + if (p->ctxt.in_argdef) { + SET_LEX_STATE(EXPR_ENDARG); + return tBDOT3; + } if (p->lex.paren_nest == 0 && looking_at_eol_p(p)) { - if (p->ctxt.in_argdef || /* def foo a, ... */ - IS_lex_state_for(last_state, EXPR_ENDFN) || /* def foo ... */ - 0) { - SET_LEX_STATE(EXPR_ENDARG); - return tBDOT3; - } rb_warn0("... at EOL, should be parenthesized?"); } else if (p->lex.lpar_beg >= 0 && p->lex.lpar_beg+1 == p->lex.paren_nest) { diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index ce1e489992e..b71f492f9cc 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1587,7 +1587,15 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1587 def test_argument_forwarding assert_valid_syntax('def foo(...) bar(...) end') assert_valid_syntax('def foo(...) end') + assert_valid_syntax('def foo(a, ...) bar(...) end') assert_valid_syntax("def foo ...\n bar(...)\nend") + assert_valid_syntax("def foo a, ...\n bar(...)\nend") + assert_valid_syntax("def foo b = 1, ...\n bar(...)\nend") + assert_valid_syntax("def foo ...; bar(...); end") + assert_valid_syntax("def foo a, ...; bar(...); end") + assert_valid_syntax("def foo b = 1, ...; bar(...); end") + assert_valid_syntax("(def foo ...\n bar(...)\nend)") + assert_valid_syntax("(def foo ...; bar(...); end)") assert_valid_syntax('def ==(...) end') assert_valid_syntax('def [](...) end') assert_valid_syntax('def nil(...) end') @@ -1618,8 +1626,10 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1626 end end obj4 = obj1.clone + obj5 = obj1.clone obj1.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__) obj4.instance_eval("def foo ...\n bar(...)\n""end", __FILE__, __LINE__) + obj5.instance_eval("def foo ...; bar(...); end", __FILE__, __LINE__) klass = Class.new { def foo(*args, **kws, &block) @@ -1648,7 +1658,7 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1658 end obj3.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__) - [obj1, obj2, obj3, obj4].each do |obj| + [obj1, obj2, obj3, obj4, obj5].each do |obj| assert_warning('') { assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5) {|*x| x}) } @@ -1791,6 +1801,19 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1801 assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1) assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1) assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]} + + obj.singleton_class.send(:remove_method, :foo) + obj.instance_eval("def foo a, ...; bar(a, ...); end", __FILE__, __LINE__) + assert_equal [[4], {}], obj.foo(4) + assert_equal [[4, 2], {}], obj.foo(4, 2) + assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3) + assert_equal [[4], {a: 1}], obj.foo(4, a: 1) + assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1) + assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1) + assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]} + + exp = eval("-> (a: nil) {a...1}") + assert_equal 0...1, exp.call(a: 0) end def test_cdhash -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/