From 660c7e050f6cb050fd5618f812129c211af29810 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Thu, 26 Sep 2019 17:25:54 -0700 Subject: Fix more keyword separation issues This fixes instance_exec and similar methods. It also fixes Enumerator::Yielder#yield, rb_yield_block, and a couple of cases with Proc#{<<,>>}. This support requires the addition of rb_yield_values_kw, similar to rb_yield_values2, for passing the keyword flag. Unlike earlier attempts at this, this does not modify the rb_block_call_func type or add a separate function type. The functions of type rb_block_call_func are called by Ruby with a separate VM frame, and we can get the keyword flag information from the VM frame flags, so it doesn't need to be passed as a function argument. These changes require the following VM functions accept a keyword flag: * vm_yield_with_cref * vm_yield * vm_yield_with_block --- test/ruby/test_keyword.rb | 648 ++++++++++++++++++++++++++++++++++++++++++++++ test/ruby/test_proc.rb | 12 +- 2 files changed, 651 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index da502ac10f..5992434c97 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -1,6 +1,7 @@ # frozen_string_literal: false require 'test/unit' require '-test-/rb_call_super_kw' +require '-test-/iter' class TestKeywordArguments < Test::Unit::TestCase def f1(str: "foo", num: 424242) @@ -918,6 +919,83 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal([1, h3], g.new(&f).each(a: 1, **h2)) end + def test_Enumerator_Yielder_yield_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + g = Enumerator::Generator + f = -> { true } + assert_equal(true, g.new{|y| y.yield(**{})}.each(&f)) + assert_equal(true, g.new{|y| y.yield(**kw)}.each(&f)) + assert_raise(ArgumentError) { g.new{|y| y.yield(**h)}.each(&f) } + assert_raise(ArgumentError) { g.new{|y| y.yield(a: 1)}.each(&f) } + assert_raise(ArgumentError) { g.new{|y| y.yield(**h2)}.each(&f) } + assert_raise(ArgumentError) { g.new{|y| y.yield(**h3)}.each(&f) } + + f = ->(a) { a } + assert_warn(/The keyword argument is passed as the last hash parameter/m) do + assert_equal(kw, g.new{|y| y.yield(**{})}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/m) do + assert_equal(kw, g.new{|y| y.yield(**kw)}.each(&f)) + end + assert_equal(h, g.new{|y| y.yield(**h)}.each(&f)) + assert_equal(h, g.new{|y| y.yield(a: 1)}.each(&f)) + assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f)) + assert_equal(h3, g.new{|y| y.yield(**h3)}.each(&f)) + assert_equal(h3, g.new{|y| y.yield(a: 1, **h2)}.each(&f)) + + f = ->(**x) { x } + assert_equal(kw, g.new{|y| y.yield(**{})}.each(&f)) + assert_equal(kw, g.new{|y| y.yield(**kw)}.each(&f)) + assert_equal(h, g.new{|y| y.yield(**h)}.each(&f)) + assert_equal(h, g.new{|y| y.yield(a: 1)}.each(&f)) + assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f)) + assert_equal(h3, g.new{|y| y.yield(**h3)}.each(&f)) + assert_equal(h3, g.new{|y| y.yield(a: 1, **h2)}.each(&f)) + assert_warn(/The last argument is used as the keyword parameter.*for method/m) do + assert_equal(h, g.new{|y| y.yield(h)}.each(&f)) + end + assert_raise(ArgumentError) { g.new{|y| y.yield(h2)}.each(&f) } + assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do + assert_raise(ArgumentError) { g.new{|y| y.yield(h3)}.each(&f) } + end + + f = ->(a, **x) { [a,x] } + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([{}, {}], g.new{|y| y.yield(**{})}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([{}, {}], g.new{|y| y.yield(**kw)}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, {}], g.new{|y| y.yield(**h)}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, {}], g.new{|y| y.yield(a: 1)}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h2, {}], g.new{|y| y.yield(**h2)}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, {}], g.new{|y| y.yield(**h3)}.each(&f)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, {}], g.new{|y| y.yield(a: 1, **h2)}.each(&f)) + end + + f = ->(a=1, **x) { [a, x] } + assert_equal([1, kw], g.new{|y| y.yield(**{})}.each(&f)) + assert_equal([1, kw], g.new{|y| y.yield(**kw)}.each(&f)) + assert_equal([1, h], g.new{|y| y.yield(**h)}.each(&f)) + assert_equal([1, h], g.new{|y| y.yield(a: 1)}.each(&f)) + assert_equal([1, h2], g.new{|y| y.yield(**h2)}.each(&f)) + assert_equal([1, h3], g.new{|y| y.yield(**h3)}.each(&f)) + assert_equal([1, h3], g.new{|y| y.yield(a: 1, **h2)}.each(&f)) + end + def test_Class_new_kwsplat_call kw = {} h = {:a=>1} @@ -3131,6 +3209,576 @@ class TestKeywordArguments < Test::Unit::TestCase end end + def test_instance_exec_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + m = ->(*args) { args } + assert_equal([], c.instance_exec(**{}, &m)) + assert_equal([], c.instance_exec(**kw, &m)) + assert_equal([h], c.instance_exec(**h, &m)) + assert_equal([h], c.instance_exec(a: 1, &m)) + assert_equal([h2], c.instance_exec(**h2, &m)) + assert_equal([h3], c.instance_exec(**h3, &m)) + assert_equal([h3], c.instance_exec(a: 1, **h2, &m)) + + m = ->() { nil } + assert_nil(c.instance_exec(**{}, &m)) + assert_nil(c.instance_exec(**kw, &m)) + assert_raise(ArgumentError) { c.instance_exec(**h, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h2, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h3, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) } + + m = ->(args) { args } + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**{}, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**kw, &m)) + end + assert_equal(kw, c.instance_exec(kw, **kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + + m = ->(**args) { args } + assert_equal(kw, c.instance_exec(**{}, &m)) + assert_equal(kw, c.instance_exec(**kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/) do + assert_equal(h, c.instance_exec(h, &m)) + end + assert_raise(ArgumentError) { c.instance_exec(h2, &m) } + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_raise(ArgumentError) { c.instance_exec(h3, &m) } + end + + m = ->(arg, **args) { [arg, args] } + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**{}, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**kw, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(**h, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(a: 1, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h2, kw], c.instance_exec(**h2, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(**h3, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m)) + end + assert_equal([h, kw], c.instance_exec(h, &m)) + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_equal([h3, kw], c.instance_exec(h3, &m)) + + m = ->(arg=1, **args) { [arg, args] } + assert_equal([1, kw], c.instance_exec(**{}, &m)) + assert_equal([1, kw], c.instance_exec(**kw, &m)) + assert_equal([1, h], c.instance_exec(**h, &m)) + assert_equal([1, h], c.instance_exec(a: 1, &m)) + assert_equal([1, h2], c.instance_exec(**h2, &m)) + assert_equal([1, h3], c.instance_exec(**h3, &m)) + assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/m) do + assert_equal([1, h], c.instance_exec(h, &m)) + end + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_warn(/The last argument is split into positional and keyword parameters/m) do + assert_equal([h2, h], c.instance_exec(h3, &m)) + end + end + + def test_instance_exec_method_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + def c.m(*args) + args + end + m = c.method(:m) + assert_equal([], c.instance_exec(**{}, &m)) + assert_equal([], c.instance_exec(**kw, &m)) + assert_equal([h], c.instance_exec(**h, &m)) + assert_equal([h], c.instance_exec(a: 1, &m)) + assert_equal([h2], c.instance_exec(**h2, &m)) + assert_equal([h3], c.instance_exec(**h3, &m)) + assert_equal([h3], c.instance_exec(a: 1, **h2, &m)) + + c.singleton_class.remove_method(:m) + def c.m + end + m = c.method(:m) + assert_nil(c.instance_exec(**{}, &m)) + assert_nil(c.instance_exec(**kw, &m)) + assert_raise(ArgumentError) { c.instance_exec(**h, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h2, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h3, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) } + + c.singleton_class.remove_method(:m) + def c.m(args) + args + end + m = c.method(:m) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**{}, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**kw, &m)) + end + assert_equal(kw, c.instance_exec(kw, **kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + + c.singleton_class.remove_method(:m) + def c.m(**args) + args + end + m = c.method(:m) + assert_equal(kw, c.instance_exec(**{}, &m)) + assert_equal(kw, c.instance_exec(**kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/) do + assert_equal(h, c.instance_exec(h, &m)) + end + assert_raise(ArgumentError) { c.instance_exec(h2, &m) } + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_raise(ArgumentError) { c.instance_exec(h3, &m) } + end + + c.singleton_class.remove_method(:m) + def c.m(arg, **args) + [arg, args] + end + m = c.method(:m) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**{}, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**kw, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(**h, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(a: 1, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h2, kw], c.instance_exec(**h2, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(**h3, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m)) + end + assert_equal([h, kw], c.instance_exec(h, &m)) + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_equal([h3, kw], c.instance_exec(h3, &m)) + + c.singleton_class.remove_method(:m) + def c.m(arg=1, **args) + [arg, args] + end + m = c.method(:m) + assert_equal([1, kw], c.instance_exec(**{}, &m)) + assert_equal([1, kw], c.instance_exec(**kw, &m)) + assert_equal([1, h], c.instance_exec(**h, &m)) + assert_equal([1, h], c.instance_exec(a: 1, &m)) + assert_equal([1, h2], c.instance_exec(**h2, &m)) + assert_equal([1, h3], c.instance_exec(**h3, &m)) + assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/m) do + assert_equal([1, h], c.instance_exec(h, &m)) + end + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([h2, h], c.instance_exec(h3, &m)) + end + end + + def test_instance_exec_define_method_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + c.define_singleton_method(:m) do |*args| + args + end + m = c.method(:m) + assert_equal([], c.instance_exec(**{}, &m)) + assert_equal([], c.instance_exec(**kw, &m)) + assert_equal([h], c.instance_exec(**h, &m)) + assert_equal([h], c.instance_exec(a: 1, &m)) + assert_equal([h2], c.instance_exec(**h2, &m)) + assert_equal([h3], c.instance_exec(**h3, &m)) + assert_equal([h3], c.instance_exec(a: 1, **h2, &m)) + + c.singleton_class.remove_method(:m) + c.define_singleton_method(:m) do + end + m = c.method(:m) + assert_nil(c.instance_exec(**{}, &m)) + assert_nil(c.instance_exec(**kw, &m)) + assert_raise(ArgumentError) { c.instance_exec(**h, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h2, &m) } + assert_raise(ArgumentError) { c.instance_exec(**h3, &m) } + assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) } + + c.singleton_class.remove_method(:m) + c.define_singleton_method(:m) do |args| + args + end + m = c.method(:m) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**{}, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(**kw, &m)) + end + assert_equal(kw, c.instance_exec(kw, **kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + + c.singleton_class.remove_method(:m) + c.define_singleton_method(:m) do |**args| + args + end + m = c.method(:m) + assert_equal(kw, c.instance_exec(**{}, &m)) + assert_equal(kw, c.instance_exec(**kw, &m)) + assert_equal(h, c.instance_exec(**h, &m)) + assert_equal(h, c.instance_exec(a: 1, &m)) + assert_equal(h2, c.instance_exec(**h2, &m)) + assert_equal(h3, c.instance_exec(**h3, &m)) + assert_equal(h3, c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/) do + assert_equal(h, c.instance_exec(h, &m)) + end + assert_raise(ArgumentError) { c.instance_exec(h2, &m) } + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_raise(ArgumentError) { c.instance_exec(h3, &m) } + end + + c.singleton_class.remove_method(:m) + c.define_singleton_method(:m) do |arg, **args| + [arg, args] + end + m = c.method(:m) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**{}, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(**kw, &m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(**h, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(a: 1, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h2, kw], c.instance_exec(**h2, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(**h3, &m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m)) + end + assert_equal([h, kw], c.instance_exec(h, &m)) + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_equal([h3, kw], c.instance_exec(h3, &m)) + + c.singleton_class.remove_method(:m) + c.define_singleton_method(:m) do |arg=1, **args| + [arg, args] + end + m = c.method(:m) + assert_equal([1, kw], c.instance_exec(**{}, &m)) + assert_equal([1, kw], c.instance_exec(**kw, &m)) + assert_equal([1, h], c.instance_exec(**h, &m)) + assert_equal([1, h], c.instance_exec(a: 1, &m)) + assert_equal([1, h2], c.instance_exec(**h2, &m)) + assert_equal([1, h3], c.instance_exec(**h3, &m)) + assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter/m) do + assert_equal([1, h], c.instance_exec(h, &m)) + end + assert_equal([h2, kw], c.instance_exec(h2, &m)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([h2, h], c.instance_exec(h3, &m)) + end + end + + def test_instance_exec_sym_proc_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + def c.m(*args) + args + end + assert_equal([], c.instance_exec(c, **{}, &:m)) + assert_equal([], c.instance_exec(c, **kw, &:m)) + assert_equal([h], c.instance_exec(c, **h, &:m)) + assert_equal([h], c.instance_exec(c, a: 1, &:m)) + assert_equal([h2], c.instance_exec(c, **h2, &:m)) + assert_equal([h3], c.instance_exec(c, **h3, &:m)) + assert_equal([h3], c.instance_exec(c, a: 1, **h2, &:m)) + + c.singleton_class.remove_method(:m) + def c.m + end + assert_nil(c.instance_exec(c, **{}, &:m)) + assert_nil(c.instance_exec(c, **kw, &:m)) + assert_raise(ArgumentError) { c.instance_exec(c, **h, &:m) } + assert_raise(ArgumentError) { c.instance_exec(c, a: 1, &:m) } + assert_raise(ArgumentError) { c.instance_exec(c, **h2, &:m) } + assert_raise(ArgumentError) { c.instance_exec(c, **h3, &:m) } + assert_raise(ArgumentError) { c.instance_exec(c, a: 1, **h2, &:m) } + + c.singleton_class.remove_method(:m) + def c.m(args) + args + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(c, **{}, &:m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal(kw, c.instance_exec(c, **kw, &:m)) + end + assert_equal(kw, c.instance_exec(c, kw, **kw, &:m)) + assert_equal(h, c.instance_exec(c, **h, &:m)) + assert_equal(h, c.instance_exec(c, a: 1, &:m)) + assert_equal(h2, c.instance_exec(c, **h2, &:m)) + assert_equal(h3, c.instance_exec(c, **h3, &:m)) + assert_equal(h3, c.instance_exec(c, a: 1, **h2, &:m)) + + c.singleton_class.remove_method(:m) + def c.m(**args) + args + end + assert_equal(kw, c.instance_exec(c, **{}, &:m)) + assert_equal(kw, c.instance_exec(c, **kw, &:m)) + assert_equal(h, c.instance_exec(c, **h, &:m)) + assert_equal(h, c.instance_exec(c, a: 1, &:m)) + assert_equal(h2, c.instance_exec(c, **h2, &:m)) + assert_equal(h3, c.instance_exec(c, **h3, &:m)) + assert_equal(h3, c.instance_exec(c, a: 1, **h2, &:m)) + assert_warn(/The last argument is used as the keyword parameter/) do + assert_equal(h, c.instance_exec(c, h, &:m)) + end + assert_raise(ArgumentError) { c.instance_exec(c, h2, &:m) } + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_raise(ArgumentError) { c.instance_exec(c, h3, &:m) } + end + + c.singleton_class.remove_method(:m) + def c.m(arg, **args) + [arg, args] + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(c, **{}, &:m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + c.instance_exec(c, **kw, &:m) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(c, **h, &:m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h, kw], c.instance_exec(c, a: 1, &:m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h2, kw], c.instance_exec(c, **h2, &:m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(c, **h3, &:m)) + end + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([h3, kw], c.instance_exec(c, a: 1, **h2, &:m)) + end + assert_equal([h, kw], c.instance_exec(c, h, &:m)) + assert_equal([h2, kw], c.instance_exec(c, h2, &:m)) + assert_equal([h3, kw], c.instance_exec(c, h3, &:m)) + + c.singleton_class.remove_method(:m) + def c.m(arg=1, **args) + [arg, args] + end + assert_equal([1, kw], c.instance_exec(c, **{}, &:m)) + assert_equal([1, kw], c.instance_exec(c, **kw, &:m)) + assert_equal([1, h], c.instance_exec(c, **h, &:m)) + assert_equal([1, h], c.instance_exec(c, a: 1, &:m)) + assert_equal([1, h2], c.instance_exec(c, **h2, &:m)) + assert_equal([1, h3], c.instance_exec(c, **h3, &:m)) + assert_equal([1, h3], c.instance_exec(c, a: 1, **h2, &:m)) + assert_warn(/The last argument is used as the keyword parameter/m) do + assert_equal([1, h], c.instance_exec(c, h, &:m)) + end + assert_equal([h2, kw], c.instance_exec(c, h2, &:m)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([h2, h], c.instance_exec(c, h3, &:m)) + end + end + + def test_rb_yield_block_kwsplat + kw = {} + h = {:a=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + c = Object.new + c.extend Bug::Iter::Yield + class << c + alias m yield_block + end + def c.c(*args) + args + end + assert_equal([], c.m(:c, **{})) + assert_equal([], c.m(:c, **kw)) + assert_equal([h], c.m(:c, **h)) + assert_equal([h], c.m(:c, a: 1)) + assert_equal([h2], c.m(:c, **h2)) + assert_equal([h3], c.m(:c, **h3)) + assert_equal([h3], c.m(:c, a: 1, **h2)) + + c.singleton_class.remove_method(:c) + def c.c; end + assert_nil(c.m(:c, **{})) + assert_nil(c.m(:c, **kw)) + assert_raise(ArgumentError) { c.m(:c, **h) } + assert_raise(ArgumentError) { c.m(:c, a: 1) } + assert_raise(ArgumentError) { c.m(:c, **h2) } + assert_raise(ArgumentError) { c.m(:c, **h3) } + assert_raise(ArgumentError) { c.m(:c, a: 1, **h2) } + + c.singleton_class.remove_method(:c) + def c.c(args) + args + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal(kw, c.m(:c, **{})) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal(kw, c.m(:c, **kw)) + end + assert_equal(kw, c.m(:c, kw, **kw)) + assert_equal(h, c.m(:c, **h)) + assert_equal(h, c.m(:c, a: 1)) + assert_equal(h2, c.m(:c, **h2)) + assert_equal(h3, c.m(:c, **h3)) + assert_equal(h3, c.m(:c, a: 1, **h2)) + + c.singleton_class.remove_method(:c) + def c.c(**args) + [args, yield(**args)] + end + m = ->(**args){ args } + assert_equal([kw, kw], c.m(:c, **{}, &m)) + assert_equal([kw, kw], c.m(:c, **kw, &m)) + assert_equal([h, h], c.m(:c, **h, &m)) + assert_equal([h, h], c.m(:c, a: 1, &m)) + assert_equal([h2, h2], c.m(:c, **h2, &m)) + assert_equal([h3, h3], c.m(:c, **h3, &m)) + assert_equal([h3, h3], c.m(:c, a: 1, **h2, &m)) + assert_warn(/The last argument is used as the keyword parameter.*for `c'/m) do + assert_equal([h, h], c.m(:c, h, &m)) + end + assert_raise(ArgumentError) { c.m(:c, h2, &m) } + assert_warn(/The last argument is split into positional and keyword parameters.*for `c'/m) do + assert_raise(ArgumentError) { c.m(:c, h3, &m) } + end + + c.singleton_class.remove_method(:c) + def c.c(arg, **args) + [arg, args] + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([kw, kw], c.m(:c, **{})) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([kw, kw], c.m(:c, **kw)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([h, kw], c.m(:c, **h)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([h, kw], c.m(:c, a: 1)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([h2, kw], c.m(:c, **h2)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([h3, kw], c.m(:c, **h3)) + end + assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do + assert_equal([h3, kw], c.m(:c, a: 1, **h2)) + end + assert_equal([h, kw], c.m(:c, h)) + assert_equal([h2, kw], c.m(:c, h2)) + assert_equal([h3, kw], c.m(:c, h3)) + + c.singleton_class.remove_method(:c) + def c.c(arg=1, **args) + [arg, args] + end + assert_equal([1, kw], c.m(:c, **{})) + assert_equal([1, kw], c.m(:c, **kw)) + assert_equal([1, h], c.m(:c, **h)) + assert_equal([1, h], c.m(:c, a: 1)) + assert_equal([1, h2], c.m(:c, **h2)) + assert_equal([1, h3], c.m(:c, **h3)) + assert_equal([1, h3], c.m(:c, a: 1, **h2)) + assert_warn(/The last argument is used as the keyword parameter.*for `c'/m) do + assert_equal([1, h], c.m(:c, h)) + end + assert_equal([h2, kw], c.m(:c, h2)) + assert_warn(/The last argument is split into positional and keyword parameters.*for `c'/m) do + assert_equal([h2, h], c.m(:c, h3)) + end + end + def p1 Proc.new do |str: "foo", num: 424242| [str, num] diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index b7d644345d..467bbf5925 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -1552,9 +1552,7 @@ class TestProcKeywords < Test::Unit::TestCase assert_warn(/The last argument is used as the keyword parameter.*for method/m) do assert_equal(1, (f << g).call(a: 3)[:a]) end - assert_warn(/The last argument is used as the keyword parameter.*for method/m) do - assert_equal(2, (f >> g).call(a: 3)[:a]) - end + assert_equal(2, (f >> g).call(a: 3)[:a]) assert_warn(/The last argument is used as the keyword parameter.*for method/m) do assert_equal(1, (f << g).call({a: 3})[:a]) end @@ -1574,9 +1572,7 @@ class TestProcKeywords < Test::Unit::TestCase assert_warn(/The keyword argument is passed as the last hash parameter.*The last argument is used as the keyword parameter.*for method/m) do assert_equal(1, (f << g).call(**{})[:a]) end - assert_warn(/The last argument is used as the keyword parameter.*for method/m) do - assert_equal(2, (f >> g).call(**{})[:a]) - end + assert_equal(2, (f >> g).call(**{})[:a]) end def test_compose_keywords_non_proc @@ -1632,9 +1628,7 @@ class TestProcKeywords < Test::Unit::TestCase assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do assert_equal(2, (g << f).call(a: 3)[:a]) end - assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do - assert_equal(1, (g >> f).call(a: 3)[:a]) - end + assert_equal(1, (g >> f).call(a: 3)[:a]) assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do assert_equal(2, (g << f).call({a: 3})[:a]) end -- cgit v1.2.3