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

ruby-changes:64917

From: Takashi <ko1@a...>
Date: Sun, 17 Jan 2021 18:36:11 +0900 (JST)
Subject: [ruby-changes:64917] 8d099aa040 (master): Warn Struct#initialize with only keyword args (#4070)

https://git.ruby-lang.org/ruby.git/commit/?id=8d099aa040

From 8d099aa040427aede04e42c3ec9380afd431ffe3 Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
Date: Sun, 17 Jan 2021 01:35:54 -0800
Subject: Warn Struct#initialize with only keyword args (#4070)

* Warn Struct#initialize with only keyword args

A part of [Feature #16806]

* Do not warn if `keyword_init: false`

is explicitly specified

* Add a NEWS entry

* s/in/from/

* Make sure all fields are initialized

diff --git a/NEWS.md b/NEWS.md
index 962a777..8e6aeb4 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -28,6 +28,12 @@ Outstanding ones only. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L28
       modify the ancestor chain if the receiver has already prepended
       the argument. [[Bug #17423]]
 
+* Struct
+
+    * Passing only keyword arguments to Struct#initialize is warned.
+      You need to use a Hash literal to set a Hash to a first member.
+      [[Feature #16806]]
+
 ## Stdlib updates
 
 Outstanding ones only.
@@ -55,5 +61,6 @@ Excluding feature bug fixes. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L61
 ## Miscellaneous changes
 
 
+[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
 [Feature #17312]: https://bugs.ruby-lang.org/issues/17312
 [Bug #17423]: https://bugs.ruby-lang.org/issues/17423
diff --git a/struct.c b/struct.c
index d62b6ca..ceb025f 100644
--- a/struct.c
+++ b/struct.c
@@ -554,7 +554,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...) https://github.com/ruby/ruby/blob/trunk/struct.c#L554
 static VALUE
 rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
 {
-    VALUE name, rest, keyword_init = Qfalse;
+    VALUE name, rest, keyword_init = Qnil;
     long i;
     VALUE st;
     st_table *tbl;
@@ -577,7 +577,7 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/struct.c#L577
 	}
         rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &keyword_init);
         if (keyword_init == Qundef) {
-            keyword_init = Qfalse;
+            keyword_init = Qnil;
         }
 	--argc;
     }
@@ -657,11 +657,15 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/struct.c#L657
 rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
 {
     VALUE klass = rb_obj_class(self);
-    long i, n;
-
     rb_struct_modify(self);
-    n = num_members(klass);
-    if (argc > 0 && RTEST(rb_struct_s_keyword_init(klass))) {
+    long n = num_members(klass);
+    if (argc == 0) {
+        rb_mem_clear((VALUE *)RSTRUCT_CONST_PTR(self), n);
+        return Qnil;
+    }
+
+    VALUE keyword_init = rb_struct_s_keyword_init(klass);
+    if (RTEST(keyword_init)) {
 	struct struct_hash_set_arg arg;
 	if (argc > 1 || !RB_TYPE_P(argv[0], T_HASH)) {
 	    rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0)", argc);
@@ -679,7 +683,11 @@ rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/struct.c#L683
 	if (n < argc) {
 	    rb_raise(rb_eArgError, "struct size differs");
 	}
-	for (i=0; i<argc; i++) {
+        if (keyword_init == Qnil && argc == 1 && RB_TYPE_P(argv[0], T_HASH) && rb_keyword_given_p()) {
+            rb_warn("Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2. "\
+                    "Please use a Hash literal like .new({k: v}) instead of .new(k: v).");
+        }
+        for (long i=0; i<argc; i++) {
 	    RSTRUCT_SET(self, i, argv[i]);
 	}
 	if (n > argc) {
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index c313ab0..dc6a0b9 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -350,6 +350,18 @@ module TestStruct https://github.com/ruby/ruby/blob/trunk/test/ruby/test_struct.rb#L350
     end
   end
 
+  def test_keyword_args_warning
+    warning = /warning: Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3\.2\./
+    assert_match(warning, EnvUtil.verbose_warning { assert_equal({a: 1}, @Struct.new(:a).new(a: 1).a) })
+    assert_match(warning, EnvUtil.verbose_warning { assert_equal({a: 1}, @Struct.new(:a, keyword_init: nil).new(a: 1).a) })
+    assert_warn('') { assert_equal({a: 1}, @Struct.new(:a).new({a: 1}).a) }
+    assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, :b).new(1, a: 1).b) }
+    assert_warn('') { assert_equal(1, @Struct.new(:a, keyword_init: true).new(a: 1).a) }
+    assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: nil).new({a: 1}).a) }
+    assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new(a: 1).a) }
+    assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new({a: 1}).a) }
+  end
+
   def test_nonascii
     struct_test = @Struct.new(name = "R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
     assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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