diff options
author | Jeremy Evans <code@jeremyevans.net> | 2019-09-05 19:25:34 -0700 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2019-09-06 19:41:23 -0700 |
commit | 37a2c660aa4f4aacfd6a56651b10124e3ac01321 (patch) | |
tree | e31ede184f37489e1cda4ae2400e6424a03ffb39 /test/ruby/test_keyword.rb | |
parent | 3fafc549ba909e986917f2b6b96eb14624c26329 (diff) |
Convert keyword argument to required positional hash argument for Class#new, Method#call, UnboundMethod#bind_call
Also add keyword argument separation warnings for Class#new and Method#call.
To allow for keyword argument to required positional hash converstion in
cfuncs, add a vm frame flag indicating the cfunc was called with an empty
keyword hash (which was removed before calling the cfunc). The cfunc can
check this frame flag and add back an empty hash if it is passing its
arguments to another Ruby method. Add rb_empty_keyword_given_p function
for checking if called with an empty keyword hash, and
rb_add_empty_keyword for adding back an empty hash to argv.
All of this empty keyword argument support is only for 2.7. It will be
removed in 3.0 as Ruby 3 will not convert empty keyword arguments to
required positional hash arguments. Comment all of the relevent code
to make it obvious this is expected to be removed.
Add rb_funcallv_kw as an public C-API function, just like rb_funcallv
but with a keyword flag. This is used by rb_obj_call_init (internals
of Class#new). This also required expected call_type enum with
CALL_FCALL_KW, similar to the recent addition of CALL_PUBLIC_KW.
Add rb_vm_call_kw as a internal function, used by call_method_data
(internals of Method#call and UnboundMethod#bind_call). Add tests
for UnboundMethod#bind_call keyword handling.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/2432
Diffstat (limited to 'test/ruby/test_keyword.rb')
-rw-r--r-- | test/ruby/test_keyword.rb | 172 |
1 files changed, 152 insertions, 20 deletions
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 7624e6d386..9c8e60a2f8 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -340,7 +340,7 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal([1, h3], f[a: 1, **h2]) end - def test_cfunc_kwsplat_call + def test_Class_new_kwsplat_call kw = {} h = {:a=>1} h2 = {'a'=>1} @@ -382,8 +382,12 @@ class TestKeywordArguments < Test::Unit::TestCase @args = args end end - assert_raise(ArgumentError) { c[**{}] } - assert_raise(ArgumentError) { c[**kw] } + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal(kw, c[**{}].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal(kw, c[**kw].args) + end assert_equal(h, c[**h].args) assert_equal(h, c[a: 1].args) assert_equal(h2, c[**h2].args) @@ -408,13 +412,27 @@ class TestKeywordArguments < Test::Unit::TestCase @args = [arg, args] end end - assert_raise(ArgumentError) { c[**{}] } - assert_raise(ArgumentError) { c[**kw] } - assert_equal([h, kw], c[**h].args) - assert_equal([h, kw], c[a: 1].args) - assert_equal([h2, kw], c[**h2].args) - assert_equal([h3, kw], c[**h3].args) - assert_equal([h3, kw], c[a: 1, **h2].args) + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([kw, kw], c[**{}].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([kw, kw], c[**kw].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([h, kw], c[**h].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([h, kw], c[a: 1].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([h2, kw], c[**h2].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([h3, kw], c[**h3].args) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do + assert_equal([h3, kw], c[a: 1, **h2].args) + end c = Class.new(sc) do def initialize(arg=1, **args) @@ -430,7 +448,7 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal([1, h3], c[a: 1, **h2].args) end - def test_method_kwsplat_call + def test_Method_call_kwsplat_call kw = {} h = {:a=>1} h2 = {'a'=>1} @@ -462,8 +480,12 @@ class TestKeywordArguments < Test::Unit::TestCase def c.m(args) args end - assert_raise(ArgumentError) { c.method(:m)[**{}] } - assert_raise(ArgumentError) { c.method(:m)[**kw] } + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal(kw, c.method(:m)[**{}]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal(kw, c.method(:m)[**kw]) + end assert_equal(h, c.method(:m)[**h]) assert_equal(h, c.method(:m)[a: 1]) assert_equal(h2, c.method(:m)[**h2]) @@ -486,13 +508,27 @@ class TestKeywordArguments < Test::Unit::TestCase def c.m(arg, **args) [arg, args] end - assert_raise(ArgumentError) { c.method(:m)[**{}] } - assert_raise(ArgumentError) { c.method(:m)[**kw] } - assert_equal([h, kw], c.method(:m)[**h]) - assert_equal([h, kw], c.method(:m)[a: 1]) - assert_equal([h2, kw], c.method(:m)[**h2]) - assert_equal([h3, kw], c.method(:m)[**h3]) - assert_equal([h3, kw], c.method(:m)[a: 1, **h2]) + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([kw, kw], c.method(:m)[**{}]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([kw, kw], c.method(:m)[**kw]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h, kw], c.method(:m)[**h]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h, kw], c.method(:m)[a: 1]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h2, kw], c.method(:m)[**h2]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h3, kw], c.method(:m)[**h3]) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h3, kw], c.method(:m)[a: 1, **h2]) + end c.singleton_class.remove_method(:m) def c.m(arg=1, **args) @@ -507,6 +543,102 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal([1, h3], c.method(:m)[a: 1, **h2]) end + def test_UnboundMethod_bindcall_kwsplat_call + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + sc = c.singleton_class + def c.m(*args) + args + end + assert_equal([], sc.instance_method(:m).bind_call(c, **{})) + assert_equal([], sc.instance_method(:m).bind_call(c, **kw)) + assert_equal([h], sc.instance_method(:m).bind_call(c, **h)) + assert_equal([h], sc.instance_method(:m).bind_call(c, a: 1)) + assert_equal([h2], sc.instance_method(:m).bind_call(c, **h2)) + assert_equal([h3], sc.instance_method(:m).bind_call(c, **h3)) + assert_equal([h3], sc.instance_method(:m).bind_call(c, a: 1, **h2)) + + sc.remove_method(:m) + def c.m; end + assert_nil(sc.instance_method(:m).bind_call(c, **{})) + assert_nil(sc.instance_method(:m).bind_call(c, **kw)) + assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h) } + assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, a: 1) } + assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h2) } + assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h3) } + assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, a: 1, **h2) } + + sc.remove_method(:m) + def c.m(args) + args + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal(kw, sc.instance_method(:m).bind_call(c, **{})) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal(kw, sc.instance_method(:m).bind_call(c, **kw)) + end + assert_equal(h, sc.instance_method(:m).bind_call(c, **h)) + assert_equal(h, sc.instance_method(:m).bind_call(c, a: 1)) + assert_equal(h2, sc.instance_method(:m).bind_call(c, **h2)) + assert_equal(h3, sc.instance_method(:m).bind_call(c, **h3)) + assert_equal(h3, sc.instance_method(:m).bind_call(c, a: 1, **h2)) + + sc.remove_method(:m) + def c.m(**args) + args + end + assert_equal(kw, sc.instance_method(:m).bind_call(c, **{})) + assert_equal(kw, sc.instance_method(:m).bind_call(c, **kw)) + assert_equal(h, sc.instance_method(:m).bind_call(c, **h)) + assert_equal(h, sc.instance_method(:m).bind_call(c, a: 1)) + assert_equal(h2, sc.instance_method(:m).bind_call(c, **h2)) + assert_equal(h3, sc.instance_method(:m).bind_call(c, **h3)) + assert_equal(h3, sc.instance_method(:m).bind_call(c, a: 1, **h2)) + + sc.remove_method(:m) + def c.m(arg, **args) + [arg, args] + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([kw, kw], sc.instance_method(:m).bind_call(c, **{})) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([kw, kw], sc.instance_method(:m).bind_call(c, **kw)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h, kw], sc.instance_method(:m).bind_call(c, **h)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h, kw], sc.instance_method(:m).bind_call(c, a: 1)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h2, kw], sc.instance_method(:m).bind_call(c, **h2)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h3, kw], sc.instance_method(:m).bind_call(c, **h3)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do + assert_equal([h3, kw], sc.instance_method(:m).bind_call(c, a: 1, **h2)) + end + + sc.remove_method(:m) + def c.m(arg=1, **args) + [arg=1, args] + end + assert_equal([1, kw], sc.instance_method(:m).bind_call(c, **{})) + assert_equal([1, kw], sc.instance_method(:m).bind_call(c, **kw)) + assert_equal([1, h], sc.instance_method(:m).bind_call(c, **h)) + assert_equal([1, h], sc.instance_method(:m).bind_call(c, a: 1)) + assert_equal([1, h2], sc.instance_method(:m).bind_call(c, **h2)) + assert_equal([1, h3], sc.instance_method(:m).bind_call(c, **h3)) + assert_equal([1, h3], sc.instance_method(:m).bind_call(c, a: 1, **h2)) + end + def test_send_kwsplat kw = {} h = {:a=>1} |