mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
e033c9d7db
commit
8d099aa040
Notes:
git
2021-01-17 18:36:20 +09:00
Merged-By: k0kubun <takashikkbn@gmail.com>
3 changed files with 34 additions and 7 deletions
7
NEWS.md
7
NEWS.md
|
@ -28,6 +28,12 @@ Outstanding ones only.
|
||||||
modify the ancestor chain if the receiver has already prepended
|
modify the ancestor chain if the receiver has already prepended
|
||||||
the argument. [[Bug #17423]]
|
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
|
## Stdlib updates
|
||||||
|
|
||||||
Outstanding ones only.
|
Outstanding ones only.
|
||||||
|
@ -55,5 +61,6 @@ Excluding feature bug fixes.
|
||||||
## Miscellaneous changes
|
## Miscellaneous changes
|
||||||
|
|
||||||
|
|
||||||
|
[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
|
||||||
[Feature #17312]: https://bugs.ruby-lang.org/issues/17312
|
[Feature #17312]: https://bugs.ruby-lang.org/issues/17312
|
||||||
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
|
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
|
||||||
|
|
22
struct.c
22
struct.c
|
@ -554,7 +554,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
|
rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
|
||||||
{
|
{
|
||||||
VALUE name, rest, keyword_init = Qfalse;
|
VALUE name, rest, keyword_init = Qnil;
|
||||||
long i;
|
long i;
|
||||||
VALUE st;
|
VALUE st;
|
||||||
st_table *tbl;
|
st_table *tbl;
|
||||||
|
@ -577,7 +577,7 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
|
||||||
}
|
}
|
||||||
rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &keyword_init);
|
rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &keyword_init);
|
||||||
if (keyword_init == Qundef) {
|
if (keyword_init == Qundef) {
|
||||||
keyword_init = Qfalse;
|
keyword_init = Qnil;
|
||||||
}
|
}
|
||||||
--argc;
|
--argc;
|
||||||
}
|
}
|
||||||
|
@ -657,11 +657,15 @@ static VALUE
|
||||||
rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
|
rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
VALUE klass = rb_obj_class(self);
|
VALUE klass = rb_obj_class(self);
|
||||||
long i, n;
|
|
||||||
|
|
||||||
rb_struct_modify(self);
|
rb_struct_modify(self);
|
||||||
n = num_members(klass);
|
long n = num_members(klass);
|
||||||
if (argc > 0 && RTEST(rb_struct_s_keyword_init(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;
|
struct struct_hash_set_arg arg;
|
||||||
if (argc > 1 || !RB_TYPE_P(argv[0], T_HASH)) {
|
if (argc > 1 || !RB_TYPE_P(argv[0], T_HASH)) {
|
||||||
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0)", argc);
|
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)
|
||||||
if (n < argc) {
|
if (n < argc) {
|
||||||
rb_raise(rb_eArgError, "struct size differs");
|
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]);
|
RSTRUCT_SET(self, i, argv[i]);
|
||||||
}
|
}
|
||||||
if (n > argc) {
|
if (n > argc) {
|
||||||
|
|
|
@ -350,6 +350,18 @@ module TestStruct
|
||||||
end
|
end
|
||||||
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
|
def test_nonascii
|
||||||
struct_test = @Struct.new(name = "R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
|
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]')
|
assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
|
||||||
|
|
Loading…
Reference in a new issue