[前][次][番号順一覧][スレッド一覧]

ruby-changes:61593

From: Jeremy <ko1@a...>
Date: Sun, 7 Jun 2020 09:07:32 +0900 (JST)
Subject: [ruby-changes:61593] f8b4340fa2 (master): Add leading arguments support to arguments forwarding

https://git.ruby-lang.org/ruby.git/commit/?id=f8b4340fa2

From f8b4340fa2c254cd093ebc3bc70d2d0c46ea9997 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 28 May 2020 11:09:44 -0700
Subject: Add leading arguments support to arguments forwarding

The idFWD_KWREST sections may be wrong. However, the existing
idFWD_KWREST sections for ... without leading arguments are already
broken.

Implements [Feature #16378]

diff --git a/parse.y b/parse.y
index d2099e2..1848dc5 100644
--- a/parse.y
+++ b/parse.y
@@ -2568,6 +2568,32 @@ paren_args	: '(' opt_call_args rparen https://github.com/ruby/ruby/blob/trunk/parse.y#L2568
 		    /*% %*/
 		    /*% ripper: arg_paren!(escape_Qundef($2)) %*/
 		    }
+		| '(' args ',' args_forward rparen
+		    {
+			if (!local_id(p, idFWD_REST) ||
+#if idFWD_KWREST
+			    !local_id(p, idFWD_KWREST) ||
+#endif
+			    !local_id(p, idFWD_BLOCK)) {
+			    compile_error(p, "unexpected ...");
+			    $$ = Qnone;
+			}
+			else {
+			/*%%%*/
+			    NODE *splat = NEW_SPLAT(NEW_LVAR(idFWD_REST, &@4), &@4);
+#if idFWD_KWREST
+			    NODE *kwrest = list_append(p, NEW_LIST(0, &@4), NEW_LVAR(idFWD_KWREST, &@4));
+#endif
+			    NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, &@4), &@4);
+			    $$ = rest_arg_append(p, $2, splat, &@$);
+#if idFWD_KWREST
+			    $$ = arg_append(p, $$, new_hash(p, kwrest, &@4), &@4);
+#endif
+			    $$ = arg_blk_pass($$, block);
+			/*% %*/
+			/*% ripper: arg_paren!(args_add!($2, $4)) %*/
+			}
+		    }
 		| '(' args_forward rparen
 		    {
 			if (!local_id(p, idFWD_REST) ||
@@ -4914,6 +4940,21 @@ f_paren_args	: '(' f_args rparen https://github.com/ruby/ruby/blob/trunk/parse.y#L4940
 			SET_LEX_STATE(EXPR_BEG);
 			p->command_start = TRUE;
 		    }
+		| '(' f_arg ',' args_forward rparen
+		    {
+			arg_var(p, idFWD_REST);
+#if idFWD_KWREST
+			arg_var(p, idFWD_KWREST);
+#endif
+			arg_var(p, idFWD_BLOCK);
+		    /*%%%*/
+			$$ = new_args_tail(p, Qnone, idFWD_KWREST, idFWD_BLOCK, &@4);
+			$$ = new_args(p, $2, Qnone, idFWD_REST, Qnone, $$, &@4);
+		    /*% %*/
+		    /*% ripper: paren!(params_new($2, Qnone, $4, Qnone, Qnone, Qnone, Qnone)) %*/
+			SET_LEX_STATE(EXPR_BEG);
+			p->command_start = TRUE;
+		    }
 		| '(' args_forward rparen
 		    {
 			arg_var(p, idFWD_REST);
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index b6eeb77..544896b 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -132,9 +132,17 @@ class TestRipper::ParserEvents < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ripper/test_parser_events.rb#L132
   end
 
   def test_args_forward
-    thru_args_forward = false
-    parse('def m(...) n(...) end', :on_args_forward) {thru_args_forward = true}
-    assert_equal true, thru_args_forward
+    [
+      'def m(...) n(...) end',
+      'def m(...) end',
+      'def m(a, ...) n(1, ...) end',
+      'def m(...) n(1, ...) end',
+      'def m(a, ...) n(...) end'
+    ].each do |code|
+      thru_args_forward = false
+      parse(code, :on_args_forward) {thru_args_forward = true}
+      assert_equal true, thru_args_forward, "no args_forward for: #{code}"
+    end
   end
 
   def test_arg_paren
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index d419fc8..cf0a93e 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1581,6 +1581,119 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1581
     end
   end
 
+  def test_argument_forwarding_with_leading_arguments
+    obj = Object.new
+    def obj.bar(*args, **kws, &block)
+      if block
+        block.call(args, kws)
+      else
+        [args, kws]
+      end
+    end
+    obj.instance_eval('def foo(_a, ...) bar(...) end', __FILE__, __LINE__)
+    assert_equal [[], {}], obj.foo(1)
+    assert_equal [[2], {}], obj.foo(1, 2)
+    assert_equal [[2, 3], {}], obj.foo(1, 2, 3)
+    assert_equal [[], {a: 1}], obj.foo(1, a: 1)
+    assert_equal [[2], {a: 1}], obj.foo(1, 2, a: 1)
+    assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+    assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(...) bar(1, ...) end', __FILE__, __LINE__)
+    assert_equal [[1], {}], obj.foo
+    assert_equal [[1, 1], {}], obj.foo(1)
+    assert_equal [[1, 1, 2], {}], obj.foo(1, 2)
+    assert_equal [[1, 1, 2, 3], {}], obj.foo(1, 2, 3)
+    assert_equal [[1], {a: 1}], obj.foo(a: 1)
+    assert_equal [[1, 1], {a: 1}], obj.foo(1, a: 1)
+    assert_equal [[1, 1, 2], {a: 1}], obj.foo(1, 2, a: 1)
+    assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+    assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 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]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(_a, ...) bar(1, ...) end', __FILE__, __LINE__)
+    assert_equal [[1], {}], obj.foo(4)
+    assert_equal [[1, 2], {}], obj.foo(4, 2)
+    assert_equal [[1, 2, 3], {}], obj.foo(4, 2, 3)
+    assert_equal [[1], {a: 1}], obj.foo(4, a: 1)
+    assert_equal [[1, 2], {a: 1}], obj.foo(4, 2, a: 1)
+    assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+    assert_equal [[1, 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, _b, ...) bar(...) end', __FILE__, __LINE__)
+    assert_equal [[], {}], obj.foo(4, 5)
+    assert_equal [[2], {}], obj.foo(4, 5, 2)
+    assert_equal [[2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(_a, _b, ...) bar(1, ...) end', __FILE__, __LINE__)
+    assert_equal [[1], {}], obj.foo(4, 5)
+    assert_equal [[1, 2], {}], obj.foo(4, 5, 2)
+    assert_equal [[1, 2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[1], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[1, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(_a, ...) bar(1, 2, ...) end', __FILE__, __LINE__)
+    assert_equal [[1, 2], {}], obj.foo(5)
+    assert_equal [[1, 2, 5], {}], obj.foo(4, 5)
+    assert_equal [[1, 2, 5, 2], {}], obj.foo(4, 5, 2)
+    assert_equal [[1, 2, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[1, 2, 5], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[1, 2, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(a, b, ...) bar(b, a, ...) end', __FILE__, __LINE__)
+    assert_equal [[5, 4], {}], obj.foo(4, 5)
+    assert_equal [[5, 4, 2], {}], obj.foo(4, 5, 2)
+    assert_equal [[5, 4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[5, 4], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[5, 4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(a, _b, ...) bar(a, ...) end', __FILE__, __LINE__)
+    assert_equal [[4], {}], obj.foo(4, 5)
+    assert_equal [[4, 2], {}], obj.foo(4, 5, 2)
+    assert_equal [[4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[4], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+    obj.singleton_class.send(:remove_method, :foo)
+    obj.instance_eval('def foo(a, ...) bar(a, 1, ...) end', __FILE__, __LINE__)
+    assert_equal [[4, 1], {}], obj.foo(4)
+    assert_equal [[4, 1, 5], {}], obj.foo(4, 5)
+    assert_equal [[4, 1, 5, 2], {}], obj.foo(4, 5, 2)
+    assert_equal [[4, 1, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+    assert_equal [[4, 1, 5], {a: 1}], obj.foo(4, 5, a: 1)
+    assert_equal [[4, 1, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+    assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+    assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+  end
+
   def test_rightward_assign
     assert_equal(1, eval("1 => a"))
     assert_equal([2,3], eval("13.divmod(5) => a,b; [a, b]"))
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]