зеркало из https://github.com/github/ruby.git
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:
Родитель
e033c9d7db
Коммит
8d099aa040
7
NEWS.md
7
NEWS.md
|
@ -28,6 +28,12 @@ Outstanding ones only.
|
|||
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.
|
|||
## 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
|
||||
|
|
22
struct.c
22
struct.c
|
@ -554,7 +554,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
|
|||
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)
|
|||
}
|
||||
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
|
|||
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)
|
|||
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) {
|
||||
|
|
|
@ -350,6 +350,18 @@ module TestStruct
|
|||
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]')
|
||||
|
|
Загрузка…
Ссылка в новой задаче