diff options
author | Jeremy Evans <code@jeremyevans.net> | 2019-08-30 23:50:50 -0700 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2019-08-30 23:50:50 -0700 |
commit | 1f18b578ce300a3ba71a9525e680037122bb81d3 (patch) | |
tree | 49265f5fdb8ade2db3b8bb56719907fb8122b090 | |
parent | 4868ad7e5b3065f9d94cc7e70889c9d31ebe88cc (diff) | |
download | ruby-1f18b578ce300a3ba71a9525e680037122bb81d3.tar.gz |
Don't pass an empty keyword hash when double splatting empty hash
-rw-r--r-- | test/ruby/test_keyword.rb | 49 | ||||
-rw-r--r-- | vm_insnhelper.c | 6 | ||||
-rw-r--r-- | vm_insnhelper.h | 1 |
3 files changed, 56 insertions, 0 deletions
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 6212d582f5..000f55744d 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -177,6 +177,55 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal(["bar", 111111], f[str: "bar", num: 111111]) end + def test_lambda_kwsplat_call + kw = {} + h = {'a'=>1} + h2 = {'a'=>1} + h3 = {'a'=>1, :a=>1} + + f = -> { true } + assert_equal(true, f[**{}]) + assert_equal(true, f[**kw]) + assert_raise(ArgumentError) { f[**h] } + assert_raise(ArgumentError) { f[**h2] } + assert_raise(ArgumentError) { f[**h3] } + + f = ->(a) { a } + assert_raise(ArgumentError) { f[**{}] } + assert_raise(ArgumentError) { f[**kw] } + assert_equal(h, f[**h]) + assert_equal(h2, f[**h2]) + assert_equal(h3, f[**h3]) + + f = ->(**x) { x } + assert_equal(kw, f[**{}]) + assert_equal(kw, f[**kw]) + assert_equal(h, f[**h]) + assert_equal(h2, f[**h2]) + assert_equal(h3, f[**h3]) + + f = ->(a, **x) { [a,x] } + assert_raise(ArgumentError) { f[**{}] } + assert_warn(/The keyword argument for `\[\]' .* is passed as the last hash parameter/) do + assert_equal([{}, {}], f[**kw]) + end + assert_warn(/The keyword argument for `\[\]' .* is passed as the last hash parameter/) do + assert_equal([h, {}], f[**h]) + end + assert_warn(/The keyword argument for `\[\]' .* is passed as the last hash parameter/) do + assert_equal([h2, {}], f[**h2]) + end + assert_warn(/The keyword argument for `\[\]' .* is passed as the last hash parameter/) do + assert_equal([h3, {}], f[**h3]) + end + + f = ->(a=1, **x) { [a, x] } + assert_equal([1, kw], f[**{}]) + assert_equal([1, kw], f[**kw]) + assert_equal([1, h], f[**h]) + assert_equal([1, h2], f[**h2]) + assert_equal([1, h3], f[**h3]) + end def p1 Proc.new do |str: "foo", num: 424242| diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2e86a0d3a2..71c5930b2e 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2912,6 +2912,12 @@ vm_callee_setup_block_arg(rb_execution_context_t *ec, struct rb_calling_info *ca CALLER_SETUP_ARG(cfp, calling, ci, 1); /* splat arg */ + if (UNLIKELY(IS_ARGS_KW_SPLAT(ci))) { + if (RHASH_EMPTY_P(argv[calling->argc-1])) { + calling->argc--; + } + } + if (arg_setup_type == arg_setup_block && calling->argc == 1 && iseq->body->param.flags.has_lead && diff --git a/vm_insnhelper.h b/vm_insnhelper.h index f937af8b59..7709840930 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -240,6 +240,7 @@ THROW_DATA_CONSUMED_SET(struct vm_throw_data *obj) #define IS_ARGS_SPLAT(ci) ((ci)->flag & VM_CALL_ARGS_SPLAT) #define IS_ARGS_KEYWORD(ci) ((ci)->flag & VM_CALL_KWARG) +#define IS_ARGS_KW_SPLAT(ci) ((ci)->flag & VM_CALL_KW_SPLAT) /* If this returns true, an optimized function returned by `vm_call_iseq_setup_func` can be used as a fastpath. */ |