ruby-changes:58263
From: Jeremy <ko1@a...>
Date: Wed, 16 Oct 2019 04:48:14 +0900 (JST)
Subject: [ruby-changes:58263] 6081ddd6e6 (master): Dup hash with keyword flag when converted to keywords
https://git.ruby-lang.org/ruby.git/commit/?id=6081ddd6e6 From 6081ddd6e6f2297862b3c7e898d28a76b8f9240b Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@j...> Date: Tue, 15 Oct 2019 12:46:24 -0700 Subject: 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). diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index e5c6da4..2533771 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -2664,6 +2664,10 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2664 baz(*args) end + def pass_bar(*args) + bar(*args) + end + def bar(*args, **kw) [args, kw] end @@ -2907,6 +2911,10 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2911 assert_equal([1, h1], o.baz(1, 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_nil(c.send(:ruby2_keywords, :bar)) end diff --git a/vm_args.c b/vm_args.c index 92deef5..6fce593 100644 --- a/vm_args.c +++ b/vm_args.c @@ -737,6 +737,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co https://github.com/ruby/ruby/blob/trunk/vm_args.c#L737 if (!kw_flag && len > 0) { if (RB_TYPE_P(rest_last, T_HASH) && (((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; } else { -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/