diff options
-rw-r--r-- | compile.c | 21 | ||||
-rw-r--r-- | test/ruby/test_keyword.rb | 2 |
2 files changed, 21 insertions, 2 deletions
@@ -4773,6 +4773,22 @@ add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return) ADD_SEQ(ret, ensure); } +static int +check_keyword(const NODE *node) +{ + /* This check is essentially a code clone of compile_keyword_arg. */ + + if (nd_type(node) == NODE_ARRAY) { + while (node->nd_next) { + node = node->nd_next; + } + node = node->nd_head; + } + + if (nd_type(node) == NODE_HASH && !node->nd_alen) return TRUE; + return FALSE; +} + static VALUE setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, int dup_rest, unsigned int *flag, struct rb_call_info_kw_arg **keywords) @@ -4792,8 +4808,9 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, COMPILE(args, "args (cat: splat)", argn->nd_body); if (flag) { *flag |= VM_CALL_ARGS_SPLAT; - if (nd_type(argn->nd_body) == NODE_HASH) - /* bug: https://bugs.ruby-lang.org/issues/10856#change-77095 */ + /* This is a dirty hack. It traverses the AST twice. + * In a long term, it should be fixed by a redesign of keyword arguments */ + if (check_keyword(argn->nd_body)) *flag |= VM_CALL_KW_SPLAT; } if (nd_type(argn) == NODE_ARGSCAT) { diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 84a5cfec66..eda74276e2 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -518,6 +518,8 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal(:ok, m.f(**o), '[ruby-core:68124] [Bug #10856]') a = [] assert_equal(:ok, m.f(*a, **o), '[ruby-core:83638] [Bug #10856]') + assert_equal(:OK, m.f1(*a, :OK, **o), '[ruby-core:91825] [Bug #10856]') + assert_equal({}, m.f1(*a, o), '[ruby-core:91825] [Bug #10856]') o = {a: 42} assert_warning(/splat keyword/, 'splat to mandatory') do |