Do not autosplat when calling proc with empty keyword splat

With the removal of the splatted argument when using an empty
keyword splat, the autosplat code considered an empty keyword
splat the same as no argument at all.  However, that results
in autosplat behavior changing dependent on the content of
the splatted hash, which is not what anyone would expect or
want.  This change always skips an autosplat if keywords were
provided.

Fixes [Bug #16560]
This commit is contained in:
Jeremy Evans 2020-01-24 12:13:41 -08:00
Родитель 9af7d048b6
Коммит c1d8829ef5
2 изменённых файлов: 22 добавлений и 1 удалений

Просмотреть файл

@ -1087,6 +1087,26 @@ class TestProc < Test::Unit::TestCase
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
end
def test_proc_autosplat
def self.a(arg, kw)
yield arg
yield arg, **kw
yield arg, kw
end
arr = []
a([1,2,3], {}) do |arg1, arg2=0|
arr << [arg1, arg2]
end
assert_equal([[1, 2], [[1, 2, 3], 0], [[1, 2, 3], {}]], arr)
arr = []
a([1,2,3], a: 1) do |arg1, arg2=0|
arr << [arg1, arg2]
end
assert_equal([[1, 2], [[1, 2, 3], {a: 1}], [[1, 2, 3], {a: 1}]], arr)
end
def test_proc_single_arg_with_keywords_accepted_and_yielded
def self.a
yield [], **{a: 1}

Просмотреть файл

@ -445,9 +445,9 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
{
const int min_argc = iseq->body->param.lead_num + iseq->body->param.post_num;
const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS;
int opt_pc = 0;
int given_argc;
unsigned int kw_flag = ci->flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT);
int opt_pc = 0, allow_autosplat = !kw_flag;
struct args_info args_body, *args;
VALUE keyword_hash = Qnil;
VALUE * const orig_sp = ec->cfp->sp;
@ -573,6 +573,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
break; /* do nothing special */
case arg_setup_block:
if (given_argc == (keyword_hash == Qnil ? 1 : 2) &&
allow_autosplat &&
(min_argc > 0 || iseq->body->param.opt_num > 1 ||
iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
!iseq->body->param.flags.ambiguous_param0 &&