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

ruby-changes:57968

From: Jeremy <ko1@a...>
Date: Fri, 27 Sep 2019 07:31:11 +0900 (JST)
Subject: [ruby-changes:57968] 37f9213f89 (master): Fix keyword argument separation issues in Enumerator::Generator#each

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

From 37f9213f8957e0c6dffee7d8803890907f97bdbb Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 26 Sep 2019 09:10:42 -0700
Subject: Fix keyword argument separation issues in Enumerator::Generator#each

This requires adding rb_proc_call_kw to pass the keyword flag.

diff --git a/enumerator.c b/enumerator.c
index af9dc0f..6332219 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -1506,7 +1506,7 @@ generator_each(int argc, VALUE *argv, VALUE obj) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1506
 	rb_ary_cat(args, argv, argc);
     }
 
-    return rb_proc_call(ptr->proc, args);
+    return rb_proc_call_kw(ptr->proc, args, RB_PASS_CALLED_KEYWORDS);
 }
 
 /* Lazy Enumerator methods */
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 4af254d..14543a9 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -454,6 +454,7 @@ VALUE rb_block_lambda(void); https://github.com/ruby/ruby/blob/trunk/include/ruby/intern.h#L454
 VALUE rb_proc_new(rb_block_call_func_t, VALUE);
 VALUE rb_obj_is_proc(VALUE);
 VALUE rb_proc_call(VALUE, VALUE);
+VALUE rb_proc_call_kw(VALUE, VALUE, int);
 VALUE rb_proc_call_with_block(VALUE, int argc, const VALUE *argv, VALUE);
 VALUE rb_proc_call_with_block_kw(VALUE, int argc, const VALUE *argv, VALUE, int);
 int rb_proc_arity(VALUE);
diff --git a/proc.c b/proc.c
index f03e470..7c82cc6 100644
--- a/proc.c
+++ b/proc.c
@@ -935,6 +935,24 @@ check_argc(long argc) https://github.com/ruby/ruby/blob/trunk/proc.c#L935
 #endif
 
 VALUE
+rb_proc_call_kw(VALUE self, VALUE args, int kw_splat)
+{
+    VALUE vret;
+    rb_proc_t *proc;
+    VALUE v;
+    int argc = check_argc(RARRAY_LEN(args));
+    const VALUE *argv = RARRAY_CONST_PTR(args);
+    GetProcPtr(self, proc);
+    v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
+    vret = rb_vm_invoke_proc(GET_EC(), proc, argc, argv,
+                             kw_splat, VM_BLOCK_HANDLER_NONE);
+    rb_free_tmp_buffer(&v);
+    RB_GC_GUARD(self);
+    RB_GC_GUARD(args);
+    return vret;
+}
+
+VALUE
 rb_proc_call(VALUE self, VALUE args)
 {
     VALUE vret;
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 4bda9d5..da502ac 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -841,6 +841,83 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L841
     assert_equal([1, h3], t.new(&f).resume(a: 1, **h2))
   end
 
+  def test_Enumerator_Generator_each_kwsplat
+    kw = {}
+    h = {:a=>1}
+    h2 = {'a'=>1}
+    h3 = {'a'=>1, :a=>1}
+
+    g = Enumerator::Generator
+    f = ->(_) { true }
+    assert_equal(true, g.new(&f).each(**{}))
+    assert_equal(true, g.new(&f).each(**kw))
+    assert_raise(ArgumentError) { g.new(&f).each(**h) }
+    assert_raise(ArgumentError) { g.new(&f).each(a: 1) }
+    assert_raise(ArgumentError) { g.new(&f).each(**h2) }
+    assert_raise(ArgumentError) { g.new(&f).each(**h3) }
+
+    f = ->(_, a) { a }
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal(kw, g.new(&f).each(**{}))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal(kw, g.new(&f).each(**kw))
+    end
+    assert_equal(h, g.new(&f).each(**h))
+    assert_equal(h, g.new(&f).each(a: 1))
+    assert_equal(h2, g.new(&f).each(**h2))
+    assert_equal(h3, g.new(&f).each(**h3))
+    assert_equal(h3, g.new(&f).each(a: 1, **h2))
+
+    f = ->(_, **x) { x }
+    assert_equal(kw, g.new(&f).each(**{}))
+    assert_equal(kw, g.new(&f).each(**kw))
+    assert_equal(h, g.new(&f).each(**h))
+    assert_equal(h, g.new(&f).each(a: 1))
+    assert_equal(h2, g.new(&f).each(**h2))
+    assert_equal(h3, g.new(&f).each(**h3))
+    assert_equal(h3, g.new(&f).each(a: 1, **h2))
+    assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
+      assert_equal(h, g.new(&f).each(h))
+    end
+    assert_raise(ArgumentError) { g.new(&f).each(h2) }
+    assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
+      assert_raise(ArgumentError) { g.new(&f).each(h3) }
+    end
+
+    f = ->(_, a, **x) { [a,x] }
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([{}, {}], g.new(&f).each(**{}))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([{}, {}], g.new(&f).each(**kw))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h, {}], g.new(&f).each(**h))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h, {}], g.new(&f).each(a: 1))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h2, {}], g.new(&f).each(**h2))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h3, {}], g.new(&f).each(**h3))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h3, {}], g.new(&f).each(a: 1, **h2))
+    end
+
+    f = ->(_, a=1, **x) { [a, x] }
+    assert_equal([1, kw], g.new(&f).each(**{}))
+    assert_equal([1, kw], g.new(&f).each(**kw))
+    assert_equal([1, h], g.new(&f).each(**h))
+    assert_equal([1, h], g.new(&f).each(a: 1))
+    assert_equal([1, h2], g.new(&f).each(**h2))
+    assert_equal([1, h3], g.new(&f).each(**h3))
+    assert_equal([1, h3], g.new(&f).each(a: 1, **h2))
+  end
+
   def test_Class_new_kwsplat_call
     kw = {}
     h = {:a=>1}
-- 
cgit v0.10.2


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

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