summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c30
-rw-r--r--parse.y10
-rw-r--r--test/ruby/test_keyword.rb12
3 files changed, 41 insertions, 11 deletions
diff --git a/compile.c b/compile.c
index 3315716cc5..aca710e2b0 100644
--- a/compile.c
+++ b/compile.c
@@ -3940,6 +3940,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
else {
int opt_p = 1;
int first = 1, i;
+ int single_kw = 0;
+ int num_kw = 0;
while (node) {
const NODE *start_node = node, *end_node;
@@ -3955,11 +3957,15 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
if (type == COMPILE_ARRAY_TYPE_HASH && !node->nd_head) {
kw = node->nd_next;
+ num_kw++;
node = 0;
if (kw) {
opt_p = 0;
node = kw->nd_next;
kw = kw->nd_head;
+ if (!single_kw && !node) {
+ single_kw = 1;
+ }
}
break;
}
@@ -4053,6 +4059,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
}
case COMPILE_ARRAY_TYPE_HASH:
if (i > 0) {
+ num_kw++;
if (first) {
if (!popped) {
ADD_INSN1(anchor, line, newhash, INT2FIX(i));
@@ -4071,16 +4078,27 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
}
}
if (kw) {
- if (!popped) {
- ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- if (i > 0 || !first) ADD_INSN(ret, line, swap);
- else ADD_INSN1(ret, line, newhash, INT2FIX(0));
+ int empty_kw = nd_type(kw) == NODE_LIT;
+ int first_kw = num_kw == 1;
+ int only_kw = single_kw && first_kw;
+
+ if (!popped && !empty_kw) {
+ ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ if (i > 0 || !first) ADD_INSN(ret, line, swap);
+ else ADD_INSN1(ret, line, newhash, INT2FIX(0));
}
- NO_CHECK(COMPILE(ret, "keyword splat", kw));
+
+ if (empty_kw && first_kw && !only_kw) {
+ ADD_INSN1(ret, line, newhash, INT2FIX(0));
+ }
+ else if (!empty_kw || only_kw) {
+ NO_CHECK(COMPILE(ret, "keyword splat", kw));
+ }
+
if (popped) {
ADD_INSN(ret, line, pop);
}
- else {
+ else if (!empty_kw) {
ADD_SEND(ret, line, id_core_hash_merge_kwd, INT2FIX(2));
}
}
diff --git a/parse.y b/parse.y
index 6729e104be..9003f80d92 100644
--- a/parse.y
+++ b/parse.y
@@ -5209,8 +5209,14 @@ assoc : arg_value tASSOC arg_value
{
/*%%%*/
if (nd_type($2) == NODE_HASH &&
- !($2->nd_head && $2->nd_head->nd_alen))
- $$ = 0;
+ !($2->nd_head && $2->nd_head->nd_alen)) {
+ static VALUE empty_hash;
+ if (!empty_hash) {
+ empty_hash = rb_obj_freeze(rb_hash_new());
+ rb_gc_register_mark_object(empty_hash);
+ }
+ $$ = list_append(p, NEW_LIST(0, &@$), NEW_LIT(empty_hash, &@$));
+ }
else
$$ = list_append(p, NEW_LIST(0, &@$), $2);
/*% %*/
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 1cf905ff01..c3ad7b9a97 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -205,7 +205,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h3, f[**h3])
f = ->(a, **x) { [a,x] }
- assert_raise(ArgumentError) { f[**{}] }
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
+ assert_equal([{}, {}], f[**{}])
+ end
assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
assert_equal([{}, {}], f[**kw])
end
@@ -418,7 +420,9 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(arg, **args)
[arg, args]
end
- assert_raise(ArgumentError) { c.send(:m, **{}) }
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
+ assert_equal([kw, kw], c.send(:m, **{}))
+ end
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
assert_equal([kw, kw], c.send(:m, **kw))
end
@@ -491,7 +495,9 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.method_missing(_, arg, **args)
[arg, args]
end
- assert_raise(ArgumentError) { c.send(:m, **{}) }
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
+ assert_equal([kw, kw], c.send(:m, **{}))
+ end
assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
assert_equal([kw, kw], c.send(:m, **kw))
end