diff options
-rw-r--r-- | class.c | 246 | ||||
-rw-r--r-- | dir.c | 2 | ||||
-rw-r--r-- | error.c | 4 | ||||
-rw-r--r-- | ext/-test-/scan_args/scan_args.c | 31 | ||||
-rw-r--r-- | ext/json/lib/json/common.rb | 4 | ||||
-rw-r--r-- | ext/json/parser/parser.c | 2 | ||||
-rw-r--r-- | ext/pathname/pathname.c | 10 | ||||
-rw-r--r-- | ext/stringio/stringio.c | 4 | ||||
-rw-r--r-- | ext/zlib/zlib.c | 4 | ||||
-rw-r--r-- | include/ruby/ruby.h | 106 | ||||
-rw-r--r-- | internal.h | 3 | ||||
-rw-r--r-- | io.c | 8 | ||||
-rw-r--r-- | lib/csv.rb | 2 | ||||
-rw-r--r-- | lib/fileutils.rb | 4 | ||||
-rw-r--r-- | lib/open-uri.rb | 6 | ||||
-rw-r--r-- | lib/pstore.rb | 6 | ||||
-rwxr-xr-x | lib/rubygems/core_ext/kernel_warn.rb | 9 | ||||
-rw-r--r-- | lib/rubygems/package.rb | 6 | ||||
-rw-r--r-- | lib/tempfile.rb | 6 | ||||
-rw-r--r-- | rational.c | 2 | ||||
-rw-r--r-- | test/-ext-/test_scan_args.rb | 137 | ||||
-rw-r--r-- | test/pathname/test_pathname.rb | 2 | ||||
-rw-r--r-- | test/reline/helper.rb | 2 | ||||
-rw-r--r-- | test/ruby/test_dir_m17n.rb | 36 | ||||
-rw-r--r-- | test/ruby/test_econv.rb | 7 | ||||
-rw-r--r-- | test/ruby/test_exception.rb | 8 | ||||
-rw-r--r-- | test/ruby/test_io.rb | 12 | ||||
-rw-r--r-- | test/ruby/test_io_m17n.rb | 3 | ||||
-rw-r--r-- | test/ruby/test_literal.rb | 4 | ||||
-rw-r--r-- | test/ruby/test_numeric.rb | 7 | ||||
-rw-r--r-- | test/ruby/test_string.rb | 4 |
31 files changed, 508 insertions, 179 deletions
@@ -1943,85 +1943,172 @@ rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, V #undef extract_kwarg } -#undef rb_scan_args -int -rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) +struct rb_scan_args_t { + int argc; + const VALUE *argv; + va_list vargs; + int f_var; + int f_hash; + int f_block; + int n_lead; + int n_opt; + int n_trail; + int n_mand; + int argi; + int last_idx; + VALUE hash; + VALUE last_hash; + VALUE *tmp_buffer; +}; + +static void +rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, struct rb_scan_args_t *arg) { - int i; const char *p = fmt; - VALUE *var; - va_list vargs; - int f_var = 0, f_hash = 0, f_block = 0; - int n_lead = 0, n_opt = 0, n_trail = 0, n_mand; - int argi = 0, last_idx = -1; - VALUE hash = Qnil, last_hash = 0; + VALUE *tmp_buffer = arg->tmp_buffer; + int keyword_given = 0; + int empty_keyword_given = 0; + int last_hash_keyword = 0; + + memset(arg, 0, sizeof(*arg)); + arg->last_idx = -1; + arg->hash = Qnil; + + switch(kw_flag) { + case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS: + if(!(keyword_given = rb_keyword_given_p())) { + empty_keyword_given = rb_empty_keyword_given_p(); + } + break; + case RB_SCAN_ARGS_KEYWORDS: + keyword_given = 1; + break; + case RB_SCAN_ARGS_EMPTY_KEYWORDS: + empty_keyword_given = 1; + break; + case RB_SCAN_ARGS_LAST_HASH_KEYWORDS: + last_hash_keyword = 1; + break; + } if (ISDIGIT(*p)) { - n_lead = *p - '0'; + arg->n_lead = *p - '0'; p++; if (ISDIGIT(*p)) { - n_opt = *p - '0'; + arg->n_opt = *p - '0'; p++; } } if (*p == '*') { - f_var = 1; + arg->f_var = 1; p++; } if (ISDIGIT(*p)) { - n_trail = *p - '0'; + arg->n_trail = *p - '0'; p++; } if (*p == ':') { - f_hash = 1; + arg->f_hash = 1; p++; } if (*p == '&') { - f_block = 1; + arg->f_block = 1; p++; } if (*p != '\0') { rb_fatal("bad scan arg format: %s", fmt); } - n_mand = n_lead + n_trail; + arg->n_mand = arg->n_lead + arg->n_trail; - if (argc < n_mand) - goto argc_error; + /* capture an option hash - phase 1: pop */ + /* Ignore final positional hash if empty keywords given */ + if (argc > 0 && !(arg->f_hash && empty_keyword_given)) { + VALUE last = argv[argc - 1]; + + if (arg->f_hash && arg->n_mand < argc) { + if (keyword_given) { + if (!RB_TYPE_P(last, T_HASH)) { + rb_warn("Keyword flag set when calling rb_scan_args, but last entry is not a hash"); + } + else { + arg->hash = last; + } + } + else if (NIL_P(last)) { + /* For backwards compatibility, nil is taken as an empty + option hash only if it is not ambiguous; i.e. '*' is + not specified and arguments are given more than sufficient. + This will be removed in Ruby 3. */ + if (!arg->f_var && arg->n_mand + arg->n_opt < argc) { + rb_warn("The last argument is nil, treating as empty keywords"); + argc--; + } + } + else { + arg->hash = rb_check_hash_type(last); + } + + /* Ruby 3: Remove if branch, as it will not attempt to split hashes */ + if (!NIL_P(arg->hash)) { + VALUE opts = rb_extract_keywords(&arg->hash); + + if (!(arg->last_hash = arg->hash)) { + if (!keyword_given && !last_hash_keyword) { + /* Warn if treating positional as keyword, as in Ruby 3, + this will be an error */ + rb_warn("The last argument is used as the keyword parameter"); + } + argc--; + } + else { + /* Warn if splitting either positional hash to keywords or keywords + to positional hash, as in Ruby 3, no splitting will be done */ + rb_warn("The last argument is split into positional and keyword parameters"); + arg->last_idx = argc - 1; + } + arg->hash = opts ? opts : Qnil; + } + } + else if (arg->f_hash && keyword_given && arg->n_mand == argc) { + /* Warn if treating keywords as positional, as in Ruby 3, this will be an error */ + rb_warn("The keyword argument is passed as the last hash parameter"); + } + } + if (arg->f_hash && arg->n_mand == argc+1 && empty_keyword_given) { + VALUE *ptr = rb_alloc_tmp_buffer2(tmp_buffer, argc+1, sizeof(VALUE)); + memcpy(ptr, argv, sizeof(VALUE)*argc); + ptr[argc] = rb_hash_new(); + argc++; + *(&argv) = ptr; + rb_warn("The keyword argument is passed as the last hash parameter"); + } - va_start(vargs, fmt); + arg->argc = argc; + arg->argv = argv; +} - /* capture an option hash - phase 1: pop */ - if (f_hash && n_mand < argc) { - VALUE last = argv[argc - 1]; - - if (NIL_P(last)) { - /* nil is taken as an empty option hash only if it is not - ambiguous; i.e. '*' is not specified and arguments are - given more than sufficient */ - if (!f_var && n_mand + n_opt < argc) - argc--; - } - else { - hash = rb_check_hash_type(last); - if (!NIL_P(hash)) { - VALUE opts = rb_extract_keywords(&hash); - if (!(last_hash = hash)) argc--; - else last_idx = argc - 1; - hash = opts ? opts : Qnil; - } - } +static int +rb_scan_args_assign(struct rb_scan_args_t *arg, va_list vargs) +{ + int argi = 0; + int i; + VALUE *var; + + if (arg->argc < arg->n_mand) { + return 1; } + /* capture leading mandatory arguments */ - for (i = n_lead; i-- > 0; ) { + for (i = arg->n_lead; i-- > 0; ) { var = va_arg(vargs, VALUE *); - if (var) *var = (argi == last_idx) ? last_hash : argv[argi]; + if (var) *var = (argi == arg->last_idx) ? arg->last_hash : arg->argv[argi]; argi++; } /* capture optional arguments */ - for (i = n_opt; i-- > 0; ) { + for (i = arg->n_opt; i-- > 0; ) { var = va_arg(vargs, VALUE *); - if (argi < argc - n_trail) { - if (var) *var = (argi == last_idx) ? last_hash : argv[argi]; + if (argi < arg->argc - arg->n_trail) { + if (var) *var = (argi == arg->last_idx) ? arg->last_hash : arg->argv[argi]; argi++; } else { @@ -2029,15 +2116,15 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) } } /* capture variable length arguments */ - if (f_var) { - int n_var = argc - argi - n_trail; + if (arg->f_var) { + int n_var = arg->argc - argi - arg->n_trail; var = va_arg(vargs, VALUE *); if (0 < n_var) { if (var) { - int f_last = (last_idx + 1 == argc - n_trail); - *var = rb_ary_new4(n_var-f_last, &argv[argi]); - if (f_last) rb_ary_push(*var, last_hash); + int f_last = (arg->last_idx + 1 == arg->argc - arg->n_trail); + *var = rb_ary_new4(n_var - f_last, &arg->argv[argi]); + if (f_last) rb_ary_push(*var, arg->last_hash); } argi += n_var; } @@ -2046,18 +2133,18 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) } } /* capture trailing mandatory arguments */ - for (i = n_trail; i-- > 0; ) { + for (i = arg->n_trail; i-- > 0; ) { var = va_arg(vargs, VALUE *); - if (var) *var = (argi == last_idx) ? last_hash : argv[argi]; + if (var) *var = (argi == arg->last_idx) ? arg->last_hash : arg->argv[argi]; argi++; } /* capture an option hash - phase 2: assignment */ - if (f_hash) { + if (arg->f_hash) { var = va_arg(vargs, VALUE *); - if (var) *var = hash; + if (var) *var = arg->hash; } /* capture iterator block */ - if (f_block) { + if (arg->f_block) { var = va_arg(vargs, VALUE *); if (rb_block_given_p()) { *var = rb_block_proc(); @@ -2066,14 +2153,53 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) *var = Qnil; } } - va_end(vargs); - if (argi < argc) { - argc_error: - rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt); + if (argi < arg->argc) return 1; + + return 0; +} + +#undef rb_scan_args +int +rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) +{ + int error; + va_list vargs; + VALUE tmp_buffer = 0; + struct rb_scan_args_t arg; + arg.tmp_buffer = &tmp_buffer; + rb_scan_args_parse(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, argc, argv, fmt, &arg); + va_start(vargs,fmt); + error = rb_scan_args_assign(&arg, vargs); + va_end(vargs); + if (tmp_buffer) { + rb_free_tmp_buffer(&tmp_buffer); } + if (error) { + rb_error_arity(arg.argc, arg.n_mand, arg.f_var ? UNLIMITED_ARGUMENTS : arg.n_mand + arg.n_opt); + } + return arg.argc; +} - return argc; +int +rb_scan_args_kw(int kw_flag, int argc, const VALUE *argv, const char *fmt, ...) +{ + int error; + va_list vargs; + VALUE tmp_buffer = 0; + struct rb_scan_args_t arg; + arg.tmp_buffer = &tmp_buffer; + rb_scan_args_parse(kw_flag, argc, argv, fmt, &arg); + va_start(vargs,fmt); + error = rb_scan_args_assign(&arg, vargs); + va_end(vargs); + if (tmp_buffer) { + rb_free_tmp_buffer(&tmp_buffer); + } + if (error) { + rb_error_arity(arg.argc, arg.n_mand, arg.f_var ? UNLIMITED_ARGUMENTS : arg.n_mand + arg.n_opt); + } + return arg.argc; } int @@ -2900,7 +2900,7 @@ dir_s_glob(int argc, VALUE *argv, VALUE obj) static VALUE dir_open_dir(int argc, VALUE *argv) { - VALUE dir = rb_funcallv(rb_cDir, rb_intern("open"), argc, argv); + VALUE dir = rb_funcallv_kw(rb_cDir, rb_intern("open"), argc, argv, RB_PASS_CALLED_KEYWORDS); rb_check_typeddata(dir, &dir_data_type); return dir; @@ -341,7 +341,7 @@ rb_warn_m(int argc, VALUE *argv, VALUE exc) VALUE opts, location = Qnil; if (!NIL_P(ruby_verbose) && argc > 0 && - (argc = rb_scan_args(argc, argv, "*:", NULL, &opts)) > 0) { + (argc = rb_scan_args(argc, argv, "*:", NULL, &opts)) > 0) { VALUE str = argv[0], uplevel = Qnil; if (!NIL_P(opts)) { static ID kwds[1]; @@ -1595,7 +1595,7 @@ nometh_err_initialize(int argc, VALUE *argv, VALUE self) priv = (argc > 3) && (--argc, RTEST(argv[argc])); args = (argc > 2) ? argv[--argc] : Qnil; if (!NIL_P(options)) argv[argc++] = options; - rb_call_super(argc, argv); + rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS); return nometh_err_init_attr(self, args, priv); } diff --git a/ext/-test-/scan_args/scan_args.c b/ext/-test-/scan_args/scan_args.c index dca353f643..9c374da66f 100644 --- a/ext/-test-/scan_args/scan_args.c +++ b/ext/-test-/scan_args/scan_args.c @@ -250,6 +250,33 @@ scan_args_lead_opt_var_trail_hash(int argc, VALUE *argv, VALUE self) return rb_ary_new_from_values(numberof(args), args); } +static VALUE +scan_args_k_lead_opt_hash(int argc, VALUE *argv, VALUE self) +{ + VALUE args[4]; + int n = rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, "11:", args+1, args+2, args+3); + args[0] = INT2NUM(n); + return rb_ary_new_from_values(numberof(args), args); +} + +static VALUE +scan_args_e_lead_opt_hash(int argc, VALUE *argv, VALUE self) +{ + VALUE args[4]; + int n = rb_scan_args_kw(RB_SCAN_ARGS_EMPTY_KEYWORDS, argc, argv, "11:", args+1, args+2, args+3); + args[0] = INT2NUM(n); + return rb_ary_new_from_values(numberof(args), args); +} + +static VALUE +scan_args_n_lead_opt_hash(int argc, VALUE *argv, VALUE self) +{ + VALUE args[4]; + int n = rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "11:", args+1, args+2, args+3); + args[0] = INT2NUM(n); + return rb_ary_new_from_values(numberof(args), args); +} + void Init_scan_args(void) { @@ -282,5 +309,7 @@ Init_scan_args(void) rb_define_singleton_method(module, "lead_var_trail_hash", scan_args_lead_var_trail_hash, -1); rb_define_singleton_method(module, "opt_var_trail_hash", scan_args_opt_var_trail_hash, -1); rb_define_singleton_method(module, "lead_opt_var_trail_hash", scan_args_lead_opt_var_trail_hash, -1); + rb_define_singleton_method(module, "k_lead_opt_hash", scan_args_k_lead_opt_hash, -1); + rb_define_singleton_method(module, "e_lead_opt_hash", scan_args_e_lead_opt_hash, -1); + rb_define_singleton_method(module, "n_lead_opt_hash", scan_args_n_lead_opt_hash, -1); } - diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 7cc852916c..3be9fd8dc5 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -153,7 +153,7 @@ module JSON # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array def parse(source, opts = {}) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Parse the JSON document _source_ into a Ruby data structure and return it. @@ -176,7 +176,7 @@ module JSON :max_nesting => false, :allow_nan => true }.merge(opts) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Generate a JSON document from the Ruby data structure _obj_ and return diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 6f0d31c2eb..2d13bccf7e 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -1835,7 +1835,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->max_nesting = 100; json->allow_nan = 0; - json->create_additions = 1; + json->create_additions = 0; json->create_id = rb_funcall(mJSON, i_create_id, 0); json->object_class = Qnil; json->array_class = Qnil; diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c index 1cf0f90b9f..022a87699b 100644 --- a/ext/pathname/pathname.c +++ b/ext/pathname/pathname.c @@ -393,7 +393,7 @@ path_read(int argc, VALUE *argv, VALUE self) args[0] = get_strpath(self); n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]); - return rb_funcallv(rb_cFile, id_read, 1+n, args); + return rb_funcallv_kw(rb_cFile, id_read, 1+n, args, RB_PASS_CALLED_KEYWORDS); } /* @@ -1097,12 +1097,12 @@ path_s_glob(int argc, VALUE *argv, VALUE klass) n = rb_scan_args(argc, argv, "12", &args[0], &args[1], &args[2]); if (rb_block_given_p()) { - return rb_block_call(rb_cDir, id_glob, n, args, s_glob_i, klass); + return rb_block_call_kw(rb_cDir, id_glob, n, args, s_glob_i, klass, RB_PASS_CALLED_KEYWORDS); } else { VALUE ary; long i; - ary = rb_funcallv(rb_cDir, id_glob, n, args); + ary = rb_funcallv_kw(rb_cDir, id_glob, n, args, RB_PASS_CALLED_KEYWORDS); ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); for (i = 0; i < RARRAY_LEN(ary); i++) { VALUE elt = RARRAY_AREF(ary, i); @@ -1145,12 +1145,12 @@ path_glob(int argc, VALUE *argv, VALUE self) n = 3; if (rb_block_given_p()) { - return rb_block_call(rb_cDir, id_glob, n, args, glob_i, self); + return rb_block_call_kw(rb_cDir, id_glob, n, args, glob_i, self, RB_PASS_KEYWORDS); } else { VALUE ary; long i; - ary = rb_funcallv(rb_cDir, id_glob, n, args); + ary = rb_funcallv_kw(rb_cDir, id_glob, n, args, RB_PASS_KEYWORDS); ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); for (i = 0; i < RARRAY_LEN(ary); i++) { VALUE elt = RARRAY_AREF(ary, i); diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 4a81e84183..ab533a03fb 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -385,7 +385,7 @@ strio_finalize(VALUE self) static VALUE strio_s_open(int argc, VALUE *argv, VALUE klass) { - VALUE obj = rb_class_new_instance(argc, argv, klass); + VALUE obj = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); if (!rb_block_given_p()) return obj; return rb_ensure(rb_yield, obj, strio_finalize, obj); } @@ -400,7 +400,7 @@ strio_s_new(int argc, VALUE *argv, VALUE klass) rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead", cname, cname); } - return rb_class_new_instance(argc, argv, klass); + return rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); } /* diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index afd761f1c1..22b4d0fb8d 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -3045,7 +3045,7 @@ static VALUE new_wrap(VALUE tmp) { new_wrap_arg_t *arg = (new_wrap_arg_t *)tmp; - return rb_class_new_instance(arg->argc, arg->argv, arg->klass); + return rb_class_new_instance_kw(arg->argc, arg->argv, arg->klass, RB_PASS_CALLED_KEYWORDS); } static VALUE @@ -3078,7 +3078,7 @@ gzfile_wrap(int argc, VALUE *argv, VALUE klass, int close_io_on_error) } } else { - obj = rb_class_new_instance(argc, argv, klass); + obj = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); } if (rb_block_given_p()) { diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 71a1d2e83d..6f7708009a 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1899,6 +1899,11 @@ VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*); VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE); VALUE rb_funcall_with_block_kw(VALUE, ID, int, const VALUE*, VALUE, int); int rb_scan_args(int, const VALUE*, const char*, ...); +#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0 +#define RB_SCAN_ARGS_KEYWORDS 1 +#define RB_SCAN_ARGS_EMPTY_KEYWORDS 2 +#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3 +int rb_scan_args_kw(int, int, const VALUE*, const char*, ...); VALUE rb_call_super(int, const VALUE*); VALUE rb_call_super_kw(int, const VALUE*, int); VALUE rb_current_receiver(void); @@ -2324,6 +2329,9 @@ unsigned long ruby_strtoul(const char *str, char **endptr, int base); PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4); int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap); +/* -- Remove In 3.0, Only public for rb_scan_args optimized version -- */ +int rb_empty_keyword_given_p(void); + #if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__) # define rb_scan_args(argc,argvp,fmt,...) \ __builtin_choose_expr(__builtin_constant_p(fmt), \ @@ -2519,30 +2527,81 @@ rb_scan_args_set(int argc, const VALUE *argv, int i, argi = 0, vari = 0, last_idx = -1; VALUE *var, hash = Qnil, last_hash = 0; const int n_mand = n_lead + n_trail; + int keyword_given = rb_keyword_given_p(); + int empty_keyword_given = 0; + VALUE tmp_buffer = 0; + + if (!keyword_given) { + empty_keyword_given = rb_empty_keyword_given_p(); + } /* capture an option hash - phase 1: pop */ - if (f_hash && n_mand < argc) { - VALUE last = argv[argc - 1]; - - if (RB_NIL_P(last)) { - /* nil is taken as an empty option hash only if it is not - ambiguous; i.e. '*' is not specified and arguments are - given more than sufficient */ - if (!f_var && n_mand + n_opt < argc) - argc--; - } - else { - hash = rb_check_hash_type(last); - if (!RB_NIL_P(hash)) { - VALUE opts = rb_extract_keywords(&hash); - if (!(last_hash = hash)) argc--; - else last_idx = argc - 1; - hash = opts ? opts : Qnil; - } - } + /* Ignore final positional hash if empty keywords given */ + if (argc > 0 && !(f_hash && empty_keyword_given)) { + VALUE last = argv[argc - 1]; + + if (f_hash && n_mand < argc) { + if (keyword_given) { + if (!RB_TYPE_P(last, T_HASH)) { + rb_warn("Keyword flag set when calling rb_scan_args, but last entry is not a hash"); + } + else { + hash = last; + } + } + else if (NIL_P(last)) { + /* For backwards compatibility, nil is taken as an empty + option hash only if it is not ambiguous; i.e. '*' is + not specified and arguments are given more than sufficient. + This will be removed in Ruby 3. */ + if (!f_var && n_mand + n_opt < argc) { + rb_warn("The last argument is nil, treating as empty keywords"); + argc--; + } + } + else { + hash = rb_check_hash_type(last); + } + + /* Ruby 3: Remove if branch, as it will not attempt to split hashes */ + if (!NIL_P(hash)) { + VALUE opts = rb_extract_keywords(&hash); + + if (!(last_hash = hash)) { + if (!keyword_given) { + /* Warn if treating positional as keyword, as in Ruby 3, + this will be an error */ + rb_warn("The last argument is used as the keyword parameter"); + } + argc--; + } + else { + /* Warn if splitting either positional hash to keywords or keywords + to positional hash, as in Ruby 3, no splitting will be done */ + rb_warn("The last argument is split into positional and keyword parameters"); + last_idx = argc - 1; + } + hash = opts ? opts : Qnil; + } + } + else if (f_hash && keyword_given && n_mand == argc) { + /* Warn if treating keywords as positional, as in Ruby 3, this will be an error */ + rb_warn("The keyword argument is passed as the last hash parameter"); + } + } + if (f_hash && n_mand == argc+1 && empty_keyword_given) { + VALUE *ptr = (VALUE *)rb_alloc_tmp_buffer2(&tmp_buffer, argc+1, sizeof(VALUE)); + memcpy(ptr, argv, sizeof(VALUE)*argc); + ptr[argc] = rb_hash_new(); + argc++; + *(&argv) = ptr; + rb_warn("The keyword argument is passed as the last hash parameter"); } - rb_check_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt); + + if (argc < n_mand) { + goto argc_error; + } /* capture leading mandatory arguments */ for (i = n_lead; i-- > 0; ) { @@ -2600,6 +2659,13 @@ rb_scan_args_set(int argc, const VALUE *argv, } } + if (argi < argc) { + argc_error: + if(tmp_buffer) rb_free_tmp_buffer(&tmp_buffer); + rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt); + } + + if(tmp_buffer) rb_free_tmp_buffer(&tmp_buffer); return argc; } #endif diff --git a/internal.h b/internal.h index b000feee29..bb298d2bfb 100644 --- a/internal.h +++ b/internal.h @@ -1552,9 +1552,6 @@ void rb_class_modify_check(VALUE); #define id_status ruby_static_id_status NORETURN(VALUE rb_f_raise(int argc, VALUE *argv)); -/* -- Remove In 3.0 -- */ -int rb_empty_keyword_given_p(void); - /* eval_error.c */ VALUE rb_get_backtrace(VALUE info); @@ -7022,7 +7022,7 @@ rb_open_file(int argc, const VALUE *argv, VALUE io) static VALUE rb_io_s_open(int argc, VALUE *argv, VALUE klass) { - VALUE io = rb_class_new_instance(argc, argv, klass); + VALUE io = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); if (rb_block_given_p()) { return rb_ensure(rb_yield, io, io_close, io); @@ -7209,7 +7209,7 @@ rb_f_open(int argc, VALUE *argv, VALUE _) } } if (redirect) { - VALUE io = rb_funcallv(argv[0], to_open, argc-1, argv+1); + VALUE io = rb_funcallv_kw(argv[0], to_open, argc-1, argv+1, RB_PASS_CALLED_KEYWORDS); if (rb_block_given_p()) { return rb_ensure(rb_yield, io, io_close, io); @@ -8390,7 +8390,7 @@ rb_io_s_new(int argc, VALUE *argv, VALUE klass) rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead", cname, cname); } - return rb_class_new_instance(argc, argv, klass); + return rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); } @@ -10373,7 +10373,7 @@ open_key_args(VALUE klass, int argc, VALUE *argv, VALUE opt, struct foreach_arg v = rb_to_array_type(v); n = RARRAY_LENINT(v); rb_check_arity(n, 0, 3); /* rb_io_open */ - rb_scan_args(n, RARRAY_CONST_PTR(v), "02:", &vmode, &vperm, &opt); + rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, n, RARRAY_CONST_PTR(v), "02:", &vmode, &vperm, &opt); } arg->io = rb_io_open(klass, path, vmode, vperm, opt); } diff --git a/lib/csv.rb b/lib/csv.rb index 61702137d0..60dbbcc230 100644 --- a/lib/csv.rb +++ b/lib/csv.rb @@ -637,7 +637,7 @@ class CSV file_opts = {universal_newline: false}.merge(options) begin - f = File.open(filename, mode, file_opts) + f = File.open(filename, mode, **file_opts) rescue ArgumentError => e raise unless /needs binmode/.match?(e.message) and mode == "r" mode = "rb" diff --git a/lib/fileutils.rb b/lib/fileutils.rb index cfc6ef1ec8..11321763a5 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -1276,9 +1276,9 @@ module FileUtils opts[:encoding] = ::Encoding::UTF_8 if fu_windows? files = if Dir.respond_to?(:children) - Dir.children(path, opts) + Dir.children(path, **opts) else - Dir.entries(path(), opts) + Dir.entries(path(), **opts) .reject {|n| n == '.' or n == '..' } end diff --git a/lib/open-uri.rb b/lib/open-uri.rb index d9517e4b9e..f4cb536006 100644 --- a/lib/open-uri.rb +++ b/lib/open-uri.rb @@ -10,15 +10,15 @@ module Kernel alias open_uri_original_open open # :nodoc: end - def open(name, *rest, &block) # :nodoc: + def open(name, *rest, **kw, &block) # :nodoc: if (name.respond_to?(:open) && !name.respond_to?(:to_path)) || (name.respond_to?(:to_str) && %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name && (uri = URI.parse(name)).respond_to?(:open)) warn('calling URI.open via Kernel#open is deprecated, call URI.open directly', uplevel: 1) - URI.open(name, *rest, &block) + URI.open(name, *rest, **kw, &block) else - open_uri_original_open(name, *rest, &block) + open_uri_original_open(name, *rest, **kw, &block) end end module_function :open diff --git a/lib/pstore.rb b/lib/pstore.rb index 4daa2e003f..1180fd50a0 100644 --- a/lib/pstore.rb +++ b/lib/pstore.rb @@ -374,7 +374,7 @@ class PStore def open_and_lock_file(filename, read_only) if read_only begin - file = File.new(filename, RD_ACCESS) + file = File.new(filename, **RD_ACCESS) begin file.flock(File::LOCK_SH) return file @@ -386,7 +386,7 @@ class PStore return nil end else - file = File.new(filename, RDWR_ACCESS) + file = File.new(filename, **RDWR_ACCESS) file.flock(File::LOCK_EX) return file end @@ -449,7 +449,7 @@ class PStore def save_data_with_atomic_file_rename_strategy(data, file) temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}" - temp_file = File.new(temp_filename, WR_ACCESS) + temp_file = File.new(temp_filename, **WR_ACCESS) begin temp_file.flock(File::LOCK_EX) temp_file.write(data) diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb index 3e531441ed..6c5b387e88 100755 --- a/lib/rubygems/core_ext/kernel_warn.rb +++ b/lib/rubygems/core_ext/kernel_warn.rb @@ -12,9 +12,9 @@ if RUBY_VERSION >= "2.5" original_warn = method(:warn) - module_function define_method(:warn) {|*messages, uplevel: nil| - unless uplevel - return original_warn.call(*messages) + module_function define_method(:warn) {|*messages, **kw| + unless uplevel = kw[:uplevel] + return original_warn.call(*messages, **kw) end # Ensure `uplevel` fits a `long` @@ -39,7 +39,8 @@ if RUBY_VERSION >= "2.5" end uplevel = start end - original_warn.call(*messages, uplevel: uplevel) + kw[:uplevel] = uplevel + original_warn.call(*messages, **kw) } end end diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index b5f83205b3..49cdf98b31 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -529,11 +529,11 @@ EOM when 'metadata' then @spec = Gem::Specification.from_yaml entry.read when 'metadata.gz' then - args = [entry] - args << { :external_encoding => Encoding::UTF_8 } if + opts = {} + opts[:external_encoding] = Encoding::UTF_8 if Zlib::GzipReader.method(:wrap).arity != 1 - Zlib::GzipReader.wrap(*args) do |gzio| + Zlib::GzipReader.wrap(entry, **opts) do |gzio| @spec = Gem::Specification.from_yaml gzio.read end end diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 2bd124abea..f7caf65f0c 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -130,7 +130,7 @@ class Tempfile < DelegateClass(File) @mode = mode|File::RDWR|File::CREAT|File::EXCL ::Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts| opts[:perm] = 0600 - @tmpfile = File.open(tmpname, @mode, opts) + @tmpfile = File.open(tmpname, @mode, **opts) @opts = opts.freeze end ObjectSpace.define_finalizer(self, Remover.new(@tmpfile)) @@ -142,7 +142,7 @@ class Tempfile < DelegateClass(File) def open _close mode = @mode & ~(File::CREAT|File::EXCL) - @tmpfile = File.open(@tmpfile.path, mode, @opts) + @tmpfile = File.open(@tmpfile.path, mode, **@opts) __setobj__(@tmpfile) end @@ -329,7 +329,7 @@ def Tempfile.create(basename="", tmpdir=nil, mode: 0, **options) Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts| mode |= File::RDWR|File::CREAT|File::EXCL opts[:perm] = 0600 - tmpfile = File.open(tmpname, mode, opts) + tmpfile = File.open(tmpname, mode, **opts) end if block_given? begin diff --git a/rational.c b/rational.c index 0dfc8abe12..bd71c215ee 100644 --- a/rational.c +++ b/rational.c @@ -1543,7 +1543,7 @@ nurat_round_n(int argc, VALUE *argv, VALUE self) { VALUE opt; enum ruby_num_rounding_mode mode = ( - argc = rb_scan_args(argc, argv, "*:", NULL, &opt), + argc = rb_scan_args(argc, argv, "*:", NULL, &opt), rb_num_get_rounding_option(opt)); VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round); return f_round_common(argc, argv, self, round_func); diff --git a/test/-ext-/test_scan_args.rb b/test/-ext-/test_scan_args.rb index cb2dab5760..949495dd0d 100644 --- a/test/-ext-/test_scan_args.rb +++ b/test/-ext-/test_scan_args.rb @@ -93,7 +93,14 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([1, "a", nil], Bug::ScanArgs.lead_hash("a")) assert_raise(ArgumentError) {Bug::ScanArgs.lead_hash("a", "b")} assert_equal([1, "a", {b: 1}], Bug::ScanArgs.lead_hash("a", b: 1)) - assert_equal([1, {b: 1}, nil], Bug::ScanArgs.lead_hash(b: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {b: 1}, nil], Bug::ScanArgs.lead_hash(b: 1)) + end + assert_equal([1, {"a"=>0, b: 1}, nil], Bug::ScanArgs.lead_hash({"a"=>0, b: 1}, **{})) + assert_raise(ArgumentError) {Bug::ScanArgs.lead_hash(1, {"a"=>0, b: 1}, **{})} + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {}, nil], Bug::ScanArgs.lead_hash(**{})) + end end def test_opt_hash @@ -102,7 +109,10 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([0, nil, {b: 1}], Bug::ScanArgs.opt_hash(b: 1)) assert_equal([1, "a", {b: 1}], Bug::ScanArgs.opt_hash("a", b: 1)) assert_raise(ArgumentError) {Bug::ScanArgs.opt_hash("a", "b")} - assert_equal([1, {"a"=>0}, {b: 1}], Bug::ScanArgs.opt_hash("a"=>0, b: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([1, {"a"=>0}, {b: 1}], Bug::ScanArgs.opt_hash("a"=>0, b: 1)) + end + assert_equal([1, {"a"=>0, b: 1}, nil], Bug::ScanArgs.opt_hash({"a"=>0, b: 1}, **{})) end def test_lead_opt_hash @@ -110,9 +120,13 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([2, "a", "b", nil], Bug::ScanArgs.lead_opt_hash("a", "b")) assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.lead_opt_hash("a", c: 1)) assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.lead_opt_hash("a", "b", c: 1)) - assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.lead_opt_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.lead_opt_hash(c: 1)) + end assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_hash("a", "b", "c")} - assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_hash("a", "b"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_hash("a", "b"=>0, c: 1)) + end end def test_var_hash @@ -120,7 +134,9 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([1, ["a"], nil], Bug::ScanArgs.var_hash("a")) assert_equal([1, ["a"], {b: 1}], Bug::ScanArgs.var_hash("a", b: 1)) assert_equal([0, [], {b: 1}], Bug::ScanArgs.var_hash(b: 1)) - assert_equal([1, [{"a"=>0}], {b: 1}], Bug::ScanArgs.var_hash("a"=>0, b: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([1, [{"a"=>0}], {b: 1}], Bug::ScanArgs.var_hash("a"=>0, b: 1)) + end end def test_lead_var_hash @@ -129,9 +145,13 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([2, "a", ["b"], nil], Bug::ScanArgs.lead_var_hash("a", "b")) assert_equal([2, "a", ["b"], {c: 1}], Bug::ScanArgs.lead_var_hash("a", "b", c: 1)) assert_equal([1, "a", [], {c: 1}], Bug::ScanArgs.lead_var_hash("a", c: 1)) - assert_equal([1, {c: 1}, [], nil], Bug::ScanArgs.lead_var_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {c: 1}, [], nil], Bug::ScanArgs.lead_var_hash(c: 1)) + end assert_equal([3, "a", ["b", "c"], nil], Bug::ScanArgs.lead_var_hash("a", "b", "c")) - assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.lead_var_hash("a", "b"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.lead_var_hash("a", "b"=>0, c: 1)) + end end def test_opt_var_hash @@ -142,7 +162,9 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([1, "a", [], {c: 1}], Bug::ScanArgs.opt_var_hash("a", c: 1)) assert_equal([0, nil, [], {c: 1}], Bug::ScanArgs.opt_var_hash(c: 1)) assert_equal([3, "a", ["b", "c"], nil], Bug::ScanArgs.opt_var_hash("a", "b", "c")) - assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.opt_var_hash("a", "b"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.opt_var_hash("a", "b"=>0, c: 1)) + end end def test_lead_opt_var_hash @@ -151,10 +173,14 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([2, "a", "b", [], nil], Bug::ScanArgs.lead_opt_var_hash("a", "b")) assert_equal([2, "a", "b", [], {c: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", c: 1)) assert_equal([1, "a", nil, [], {c: 1}], Bug::ScanArgs.lead_opt_var_hash("a", c: 1)) - assert_equal([1, {c: 1}, nil, [], nil], Bug::ScanArgs.lead_opt_var_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {c: 1}, nil, [], nil], Bug::ScanArgs.lead_opt_var_hash(c: 1)) + end assert_equal([3, "a", "b", ["c"], nil], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c")) assert_equal([3, "a", "b", ["c"], {d: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c", d: 1)) - assert_equal([3, "a", "b", [{"c"=>0}], {d: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c"=>0, d: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([3, "a", "b", [{"c"=>0}], {d: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c"=>0, d: 1)) + end end def test_opt_trail_hash @@ -163,9 +189,13 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([2, "a", "b", nil], Bug::ScanArgs.opt_trail_hash("a", "b")) assert_equal([1, nil, "a", {c: 1}], Bug::ScanArgs.opt_trail_hash("a", c: 1)) assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.opt_trail_hash("a", "b", c: 1)) - assert_equal([1, nil, {c: 1}, nil], Bug::ScanArgs.opt_trail_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, nil, {c: 1}, nil], Bug::ScanArgs.opt_trail_hash(c: 1)) + end assert_raise(ArgumentError) {Bug::ScanArgs.opt_trail_hash("a", "b", "c")} - assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.opt_trail_hash("a", "b"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.opt_trail_hash("a", "b"=>0, c: 1)) + end end def test_lead_opt_trail_hash @@ -173,12 +203,16 @@ class TestScanArgs < Test::Unit::TestCase assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash("a")} assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash(c: 1)} assert_equal([2, "a", nil, "b", nil], Bug::ScanArgs.lead_opt_trail_hash("a", "b")) - assert_equal([2, "a", nil, {c: 1}, nil], Bug::ScanArgs.lead_opt_trail_hash("a", c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([2, "a", nil, {c: 1}, nil], Bug::ScanArgs.lead_opt_trail_hash("a", c: 1)) + end assert_equal([2, "a", nil, "b", {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", c: 1)) assert_equal([3, "a", "b", "c", nil], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c")) assert_equal([3, "a", "b", "c", {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c", c: 1)) assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c", "d")} - assert_equal([3, "a", "b", {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([3, "a", "b", {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c"=>0, c: 1)) + end end def test_var_trail_hash @@ -187,45 +221,104 @@ class TestScanArgs < Test::Unit::TestCase assert_equal([2, ["a"], "b", nil], Bug::ScanArgs.var_trail_hash("a", "b")) assert_equal([1, [], "a", {c: 1}], Bug::ScanArgs.var_trail_hash("a", c: 1)) assert_equal([2, ["a"], "b", {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", c: 1)) - assert_equal([1, [], {c: 1}, nil], Bug::ScanArgs.var_trail_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, [], {c: 1}, nil], Bug::ScanArgs.var_trail_hash(c: 1)) + end assert_equal([3, ["a", "b"], "c", nil], Bug::ScanArgs.var_trail_hash("a", "b", "c")) assert_equal([3, ["a", "b"], "c", {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", "c", c: 1)) - assert_equal([3, ["a", "b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", "c"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([3, ["a", "b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", "c"=>0, c: 1)) + end end def test_lead_var_trail_hash assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash()} assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash("a")} assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash(c: 1)} - assert_equal([2, "a", [], {c: 1}, nil], Bug::ScanArgs.lead_var_trail_hash("a", c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([2, "a", [], {c: 1}, nil], Bug::ScanArgs.lead_var_trail_hash("a", c: 1)) + end assert_equal([2, "a", [], "b", nil], Bug::ScanArgs.lead_var_trail_hash("a", "b")) assert_equal([2, "a", [], "b", {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", c: 1)) assert_equal([3, "a", ["b"], "c", nil], Bug::ScanArgs.lead_var_trail_hash("a", "b", "c")) assert_equal([3, "a", ["b"], "c", {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", "c", c: 1)) - assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", c: 1, "c"=>0)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", c: 1, "c"=>0)) + end end def test_opt_var_trail_hash assert_raise(ArgumentError) {Bug::ScanArgs.opt_var_trail_hash()} assert_equal([1, nil, [], "a", nil], Bug::ScanArgs.opt_var_trail_hash("a")) - assert_equal([1, nil, [], {c: 1}, nil], Bug::ScanArgs.opt_var_trail_hash(c: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, nil, [], {c: 1}, nil], Bug::ScanArgs.opt_var_trail_hash(c: 1)) + end assert_equal([1, nil, [], "a", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", c: 1)) assert_equal([2, "a", [], "b", nil], Bug::ScanArgs.opt_var_trail_hash("a", "b")) assert_equal([2, "a", [], "b", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", c: 1)) assert_equal([3, "a", ["b"], "c", nil], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c")) assert_equal([3, "a", ["b"], "c", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c", c: 1)) - assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c"=>0, c: 1)) + end end def test_lead_opt_var_trail_hash assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_trail_hash()} assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_trail_hash("a")} - assert_equal([2, "a", nil, [], {b: 1}, nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", b: 1)) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([2, "a", nil, [], {b: 1}, nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", b: 1)) + end assert_equal([2, "a", nil, [], "b", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b")) assert_equal([2, "a", nil, [], "b", {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", c: 1)) assert_equal([3, "a", "b", [], "c", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c")) assert_equal([3, "a", "b", [], "c", {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", c: 1)) assert_equal([4, "a", "b", ["c"], "d", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", "d")) - assert_equal([4, "a", "b", ["c"], {"d"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", "d"=>0, c: 1)) + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([4, "a", "b", ["c"], {"d"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", "d"=>0, c: 1)) + end + end + + def test_k_lead_opt_hash + assert_raise(ArgumentError) {Bug::ScanArgs.k_lead_opt_hash} + assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.k_lead_opt_hash("a", c: 1)) + assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.k_lead_opt_hash("a", {c: 1})) + assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.k_lead_opt_hash("a", "b", c: 1)) + assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.k_lead_opt_hash("a", "b", {c: 1})) + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.k_lead_opt_hash(c: 1)) + end + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.k_lead_opt_hash("a", "b"=>0, c: 1)) + end + end + + def test_e_lead_opt_hash + assert_warn(/The keyword argument is passed as the last hash parameter/) do + assert_equal([1, {}, nil, nil], Bug::ScanArgs.e_lead_opt_hash) + end + assert_equal([1, "a", nil, nil], Bug::ScanArgs.e_lead_opt_hash("a")) + assert_equal([2, "a", "b", nil], Bug::ScanArgs.e_lead_opt_hash("a", "b")) + assert_equal([2, "a", {c: 1}, nil], Bug::ScanArgs.e_lead_opt_hash("a", c: 1)) + assert_raise(ArgumentError) {Bug::ScanArgs.e_lead_opt_hash("a", "b", c: 1)} + assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.e_lead_opt_hash(c: 1)) + assert_raise(ArgumentError) {Bug::ScanArgs.e_lead_opt_hash("a", "b", "c")} + assert_equal([2, "a", {"b"=>0, c: 1}, nil], Bug::ScanArgs.e_lead_opt_hash("a", "b"=>0, c: 1)) + end + + def test_n_lead_opt_hash + assert_raise(ArgumentError) {Bug::ScanArgs.n_lead_opt_hash} + assert_equal([1, "a", nil, nil], Bug::ScanArgs.n_lead_opt_hash("a")) + assert_equal([2, "a", "b", nil], Bug::ScanArgs.n_lead_opt_hash("a", "b")) + assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.n_lead_opt_hash("a", c: 1)) + assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.n_lead_opt_hash("a", {c: 1})) + assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.n_lead_opt_hash("a", "b", c: 1)) + assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.n_lead_opt_hash("a", "b", {c: 1})) + assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.n_lead_opt_hash(c: 1)) + assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.n_lead_opt_hash({c: 1})) + assert_raise(ArgumentError) {Bug::ScanArgs.n_lead_opt_hash("a", "b", "c")} + assert_warn(/The last argument is split into positional and keyword parameters/) do + assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.n_lead_opt_hash("a", "b"=>0, c: 1)) + end end end diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index af523f9f98..053c728247 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -933,7 +933,7 @@ class TestPathname < Test::Unit::TestCase assert_equal(0444 & ~File.umask, File.stat("b").mode & 0777) assert_equal("def", File.read("b")) - Pathname("c").open("w", 0444, {}) {|f| f.write "ghi" } + Pathname("c").open("w", 0444, **{}) {|f| f.write "ghi" } assert_equal(0444 & ~File.umask, File.stat("c").mode & 0777) assert_equal("ghi", File.read("c")) diff --git a/test/reline/helper.rb b/test/reline/helper.rb index ca0001258d..b1759f1d80 100644 --- a/test/reline/helper.rb +++ b/test/reline/helper.rb @@ -31,7 +31,7 @@ class Reline::TestCase < Test::Unit::TestCase if Reline::Unicode::EscapedChars.include?(c.ord) c else - c.encode(@line_editor.instance_variable_get(:@encoding), Encoding::UTF_8, options) + c.encode(@line_editor.instance_variable_get(:@encoding), Encoding::UTF_8, **options) end }.join rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb index 7584074c7e..c2c0c4999e 100644 --- a/test/ruby/test_dir_m17n.rb +++ b/test/ruby/test_dir_m17n.rb @@ -18,7 +18,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = #{code}.chr('UTF-8').force_encoding("#{encoding}") File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename) EOS @@ -26,7 +26,7 @@ class TestDir_M17N < Test::Unit::TestCase assert_separately(%w[-EASCII-8BIT], <<-EOS, :chdir=>dir) filename = #{code}.chr('UTF-8').force_encoding("ASCII-8BIT") opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) expected_filename = #{code}.chr('UTF-8').encode(Encoding.find("filesystem")) rescue expected_filename = "?" expected_filename = expected_filename.force_encoding("ASCII-8BIT") if /mswin|mingw/ =~ RUBY_PLATFORM @@ -35,7 +35,7 @@ class TestDir_M17N < Test::Unit::TestCase when ents.include?(expected_filename) filename = expected_filename else - ents = Dir.entries(".", {:encoding => Encoding.find("filesystem")}) + ents = Dir.entries(".", :encoding => Encoding.find("filesystem")) filename = expected_filename end end @@ -52,7 +52,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\u3042" File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename) EOS } @@ -67,7 +67,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8 File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF") assert_include(ents, filename) EOS @@ -75,7 +75,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xff".force_encoding("UTF-8") # invalid byte sequence as UTF-8 File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF") assert_include(ents, filename) EOS @@ -88,7 +88,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xc2\xa1".force_encoding("utf-8") File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename) EOS assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d) @@ -125,13 +125,13 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\u3042" File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename) EOS assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d) filename = "\xA4\xA2".force_encoding("euc-jp") opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename) EOS assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d) @@ -151,7 +151,7 @@ class TestDir_M17N < Test::Unit::TestCase File.open(filename1, "w") {} File.open(filename2, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename1) assert_include(ents, filename2) EOS @@ -159,7 +159,7 @@ class TestDir_M17N < Test::Unit::TestCase filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP filename2 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) assert_include(ents, filename1) assert_include(ents, filename2) EOS @@ -183,7 +183,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xA4\xA2".force_encoding("euc-jp") File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) ents.each {|e| e.force_encoding("ASCII-8BIT") } if /darwin/ =~ RUBY_PLATFORM filename = filename.encode("utf-8") @@ -200,7 +200,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xA4\xA2".force_encoding("euc-jp") File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) if /darwin/ =~ RUBY_PLATFORM filename = filename.encode("utf-8").force_encoding("euc-jp") end @@ -210,14 +210,14 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xA4\xA2".force_encoding('ASCII-8BIT') win_expected_filename = filename.encode(Encoding.find("filesystem"), "euc-jp") rescue "?" opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) unless ents.include?(filename) case RUBY_PLATFORM when /darwin/ filename = filename.encode("utf-8", "euc-jp").b when /mswin|mingw/ if ents.include?(win_expected_filename.b) - ents = Dir.entries(".", {:encoding => Encoding.find("filesystem")}) + ents = Dir.entries(".", :encoding => Encoding.find("filesystem")) filename = win_expected_filename end end @@ -246,7 +246,7 @@ class TestDir_M17N < Test::Unit::TestCase filename = "\xA4\xA2".force_encoding("euc-jp") File.open(filename, "w") {} opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) if /darwin/ =~ RUBY_PLATFORM filename = filename.encode("utf-8", "euc-jp").force_encoding("euc-jp") end @@ -255,7 +255,7 @@ class TestDir_M17N < Test::Unit::TestCase assert_separately(%w[-EEUC-JP:UTF-8], <<-'EOS', :chdir=>d) filename = "\u3042" opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM - ents = Dir.entries(".", opts) + ents = Dir.entries(".", **(opts||{})) if /darwin/ =~ RUBY_PLATFORM filename = filename.force_encoding("euc-jp") end @@ -420,7 +420,7 @@ class TestDir_M17N < Test::Unit::TestCase else orig.each {|o| o.force_encoding(enc) } end - ents = Dir.entries(".", opts).reject {|n| /\A\./ =~ n} + ents = Dir.entries(".", **(opts||{})).reject {|n| /\A\./ =~ n} ents.sort! PP.assert_equal(orig, ents, bug7267) } diff --git a/test/ruby/test_econv.rb b/test/ruby/test_econv.rb index 6f098db454..115ff73ea8 100644 --- a/test/ruby/test_econv.rb +++ b/test/ruby/test_econv.rb @@ -3,7 +3,12 @@ require 'test/unit' class TestEncodingConverter < Test::Unit::TestCase def check_ec(edst, esrc, eres, dst, src, ec, off, len, opts=nil) - res = ec.primitive_convert(src, dst, off, len, opts) + case opts + when Hash + res = ec.primitive_convert(src, dst, off, len, **opts) + else + res = ec.primitive_convert(src, dst, off, len, opts) + end assert_equal([edst.b, esrc.b, eres], [dst.b, src.b, res]) end diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 9de1b2d82c..e5c38091ac 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -1223,6 +1223,14 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status| assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0]) assert_raise(ArgumentError) {warn("test warning", uplevel: -1)} assert_in_out_err(["-e", "warn 'ok', uplevel: 1"], '', [], /warning:/) + warning = capture_warning_warn {warn("test warning", {uplevel: 0})} + assert_equal("#{__FILE__}:#{__LINE__-1}: warning: The last argument is used as the keyword parameter\n", warning[0]) + assert_match(/warning: for method defined here|warning: test warning/, warning[1]) + warning = capture_warning_warn {warn("test warning", **{uplevel: 0})} + assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0]) + warning = capture_warning_warn {warn("test warning", {uplevel: 0}, **{})} + assert_equal("test warning\n{:uplevel=>0}\n", warning[0]) + assert_raise(ArgumentError) {warn("test warning", foo: 1)} end def test_warning_warn_invalid_argument diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index ed60452b36..a610b608aa 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -423,7 +423,7 @@ class TestIO < Test::Unit::TestCase path = t.path t.close! assert_raise(Errno::ENOENT, "[ruby-dev:33072]") do - File.read(path, nil, nil, {}) + File.read(path, nil, nil, **{}) end end @@ -2489,15 +2489,15 @@ class TestIO < Test::Unit::TestCase assert_equal(["foo\n", "bar\n", "baz\n"], a) a = [] - IO.foreach(t.path, {:mode => "r" }) {|x| a << x } + IO.foreach(t.path, :mode => "r") {|x| a << x } assert_equal(["foo\n", "bar\n", "baz\n"], a) a = [] - IO.foreach(t.path, {:open_args => [] }) {|x| a << x } + IO.foreach(t.path, :open_args => []) {|x| a << x } assert_equal(["foo\n", "bar\n", "baz\n"], a) a = [] - IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x } + IO.foreach(t.path, :open_args => ["r"]) {|x| a << x } assert_equal(["foo\n", "bar\n", "baz\n"], a) a = [] @@ -3119,7 +3119,9 @@ __END__ assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8")) assert_equal("ff", File.read(path)) assert_raise(TypeError) { - File.write(path, "foo", Object.new => Object.new) + assert_warn(/The last argument is split into positional and keyword parameters/) do + File.write(path, "foo", Object.new => Object.new) + end } end end diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index 8101bfb62f..022ff330b5 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -23,7 +23,8 @@ class TestIO_M17N < Test::Unit::TestCase def pipe(*args, wp, rp) re, we = nil, nil - r, w = IO.pipe(*args) + kw = args.last.is_a?(Hash) ? args.pop : {} + r, w = IO.pipe(*args, **kw) rt = Thread.new do begin rp.call(r) diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb index 3f87f860b1..7f4a329c4a 100644 --- a/test/ruby/test_literal.rb +++ b/test/ruby/test_literal.rb @@ -189,7 +189,7 @@ class TestRubyLiteral < Test::Unit::TestCase def test_debug_frozen_string src = 'n = 1; _="foo#{n ? "-#{n}" : ""}"'; f = "test.rb"; n = 1 opt = {frozen_string_literal: true, debug_frozen_string_literal: true} - str = RubyVM::InstructionSequence.compile(src, f, f, n, opt).eval + str = RubyVM::InstructionSequence.compile(src, f, f, n, **opt).eval assert_equal("foo-1", str) assert_predicate(str, :frozen?) assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) { @@ -200,7 +200,7 @@ class TestRubyLiteral < Test::Unit::TestCase def test_debug_frozen_string_in_array_literal src = '["foo"]'; f = "test.rb"; n = 1 opt = {frozen_string_literal: true, debug_frozen_string_literal: true} - ary = RubyVM::InstructionSequence.compile(src, f, f, n, opt).eval + ary = RubyVM::InstructionSequence.compile(src, f, f, n, **opt).eval assert_equal("foo", ary.first) assert_predicate(ary.first, :frozen?) assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) { diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb index 6073ec1ee5..f87abbb4a8 100644 --- a/test/ruby/test_numeric.rb +++ b/test/ruby/test_numeric.rb @@ -230,7 +230,8 @@ class TestNumeric < Test::Unit::TestCase end def assert_step(expected, (from, *args), inf: false) - enum = from.step(*args) + kw = args.last.is_a?(Hash) ? args.pop : {} + enum = from.step(*args, **kw) size = enum.size xsize = expected.size @@ -239,7 +240,7 @@ class TestNumeric < Test::Unit::TestCase assert_send [size, :>, 0], "step size: +infinity" a = [] - from.step(*args) { |x| a << x; break if a.size == xsize } + from.step(*args, **kw) { |x| a << x; break if a.size == xsize } assert_equal expected, a, "step" a = [] @@ -249,7 +250,7 @@ class TestNumeric < Test::Unit::TestCase assert_equal expected.size, size, "step size" a = [] - from.step(*args) { |x| a << x } + from.step(*args, **kw) { |x| a << x } assert_equal expected, a, "step" a = [] diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 36d246f9e0..dd403bf9d9 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -17,8 +17,8 @@ class TestString < Test::Unit::TestCase super end - def S(*args) - @cls.new(*args) + def S(*args, **kw) + @cls.new(*args, **kw) end def test_s_new |