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/