summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-08-30 23:50:50 -0700
committerJeremy Evans <code@jeremyevans.net>2019-08-30 23:50:50 -0700
commit1f18b578ce300a3ba71a9525e680037122bb81d3 (patch)
tree49265f5fdb8ade2db3b8bb56719907fb8122b090
parent4868ad7e5b3065f9d94cc7e70889c9d31ebe88cc (diff)
Don't pass an empty keyword hash when double splatting empty hash
-rw-r--r--test/ruby/test_keyword.rb49
-rw-r--r--vm_insnhelper.c6
-rw-r--r--vm_insnhelper.h1
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. */