[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]