summaryrefslogtreecommitdiff
path: root/class.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-10-04 12:51:57 -0700
committerJeremy Evans <code@jeremyevans.net>2020-01-02 18:40:45 -0800
commitbeae6cbf0fd8b6619e5212552de98022d4c4d4d4 (patch)
treea926f619ed39569df4e93ec31157acdb78796379 /class.c
parent8ba261c7546e3cd12ff6870bb41dd0a0bee32ba8 (diff)
Fully separate positional arguments and keyword arguments
This removes the warnings added in 2.7, and changes the behavior so that a final positional hash is not treated as keywords or vice-versa. To handle the arg_setup_block splat case correctly with keyword arguments, we need to check if we are taking a keyword hash. That case didn't have a test, but it affects real-world code, so add a test for it. This removes rb_empty_keyword_given_p() and related code, as that is not needed in Ruby 3. The empty keyword case is the same as the no keyword case in Ruby 3. This changes rb_scan_args to implement keyword argument separation for C functions when the : character is used. For backwards compatibility, it returns a duped hash. This is a bad idea for performance, but not duping the hash breaks at least Enumerator::ArithmeticSequence#inspect. Instead of having RB_PASS_CALLED_KEYWORDS be a number, simplify the code by just making it be rb_keyword_given_p().
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/2794
Diffstat (limited to 'class.c')
-rw-r--r--class.c70
1 files changed, 5 insertions, 65 deletions
diff --git a/class.c b/class.c
index bc64ff7..e42ee08 100644
--- a/class.c
+++ b/class.c
@@ -1970,7 +1970,6 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
const char *p = fmt;
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));
@@ -1979,16 +1978,11 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
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();
- }
+ keyword_given = rb_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;
@@ -2023,67 +2017,13 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
}
arg->n_mand = arg->n_lead + arg->n_trail;
- /* capture an option hash - phase 1: pop */
- /* Ignore final positional hash if empty keywords given */
- if (argc > 0 && !(arg->f_hash && empty_keyword_given)) {
+ if (arg->f_hash && argc > 0) {
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("Using the last argument as keyword parameters is deprecated");
- }
- 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;
- }
+ if (keyword_given || (last_hash_keyword && RB_TYPE_P(last, T_HASH))) {
+ arg->hash = rb_hash_dup(last);
+ argc--;
}
- 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("Passing the keyword argument as the last hash parameter is deprecated");
- }
- }
- 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("Passing the keyword argument as the last hash parameter is deprecated");
}
arg->argc = argc;