1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Dup hash with keyword flag when converted to keywords

When ruby2_keywords is used on a method, keywords passed to the method
are flagged.  When the hash is passed as the last element of an
argument splat to another method, the hash should be treated as a
keyword splat.  When keyword splatting a hash, a duplicate of the
hash is made.  So when auto-splatting the hash with the keyword
flag, a duplicate of the hash should also be made.

This fixes cases where the hash is later passed to another method
and would be treated as keywords there:

  class Object
    ruby2_keywords def foo(*a) bar(*a) end
    def bar(*a) baz(*a) end
    def baz(*a, **kw) [a, kw] end
  end
  foo(:a=>1)

Previously, this would pass the :a=>1 as keywords to bar and also as
keywords to baz. Now it only passes :a=>1 as keywords to bar, but bar
passes :a=>1 as a positional hash to baz (which in this case
generates a warning in 2.7).
This commit is contained in:
Jeremy Evans 2019-10-15 12:46:24 -07:00
parent 259601ab57
commit 6081ddd6e6
2 changed files with 10 additions and 0 deletions

View file

@ -2664,6 +2664,10 @@ class TestKeywordArguments < Test::Unit::TestCase
baz(*args) baz(*args)
end end
def pass_bar(*args)
bar(*args)
end
def bar(*args, **kw) def bar(*args, **kw)
[args, kw] [args, kw]
end end
@ -2907,6 +2911,10 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.baz(1, h1)) assert_equal([1, h1], o.baz(1, h1))
assert_equal([h1], o.baz(h1, **{})) assert_equal([h1], o.baz(h1, **{}))
assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
assert_equal([[1], h1], o.foo(:pass_bar, 1, :a=>1))
end
assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do
assert_nil(c.send(:ruby2_keywords, :bar)) assert_nil(c.send(:ruby2_keywords, :bar))
end end

View file

@ -737,6 +737,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
if (!kw_flag && len > 0) { if (!kw_flag && len > 0) {
if (RB_TYPE_P(rest_last, T_HASH) && if (RB_TYPE_P(rest_last, T_HASH) &&
(((struct RHash *)rest_last)->basic.flags & RHASH_PASS_AS_KEYWORDS)) { (((struct RHash *)rest_last)->basic.flags & RHASH_PASS_AS_KEYWORDS)) {
rest_last = rb_hash_dup(rest_last);
RARRAY_ASET(args->rest, len - 1, rest_last);
kw_flag |= VM_CALL_KW_SPLAT; kw_flag |= VM_CALL_KW_SPLAT;
} }
else { else {