summaryrefslogtreecommitdiff
path: root/vm_args.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-04-07 18:45:26 -0700
committerJeremy Evans <code@jeremyevans.net>2019-08-30 12:39:31 -0700
commit9c2e165f7dbb63dd1921eacae58c4305d3e9e5b6 (patch)
tree16ea05751be006d7cb947fe3057effaad629a793 /vm_args.c
parente0b4599bba6bea744e0e90b7640dcc21d695c434 (diff)
Only promote last hash to keyword if all keys are symbols
If all keys are not symbols, then the non-symbol keys would not be treated as keywords in previous versions. It doesn't make sense to treat these hashes as keywords to break compatibility and warn about behavior changes in Ruby 2.7 when the Ruby 3.0 behavior will be the same as the Ruby 2.6 for these hashes.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/2395
Diffstat (limited to 'vm_args.c')
-rw-r--r--vm_args.c35
1 files changed, 29 insertions, 6 deletions
diff --git a/vm_args.c b/vm_args.c
index 35c435181a..9d5626ce97 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -184,11 +184,34 @@ args_rest_array(struct args_info *args)
}
static int
-keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr)
+keyword_hash_symbol_p(st_data_t key, st_data_t val, st_data_t arg)
+{
+ if (SYMBOL_P((VALUE)key)) {
+ return ST_CONTINUE;
+ }
+
+ *(VALUE*)arg = (VALUE)0;
+ return ST_STOP;
+}
+
+static int
+keyword_hash_only_symbol_p(VALUE hash)
+{
+ VALUE all_symbols = (VALUE)(1);
+ rb_hash_stlike_foreach(hash, keyword_hash_symbol_p, (st_data_t)(&all_symbols));
+ return (int)all_symbols;
+}
+
+static int
+keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, int check_only_symbol)
{
*rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
if (!NIL_P(*rest_hash_ptr)) {
+ if (check_only_symbol && !keyword_hash_only_symbol_p(*rest_hash_ptr)) {
+ *kw_hash_ptr = Qnil;
+ return FALSE;
+ }
*kw_hash_ptr = *rest_hash_ptr;
*rest_hash_ptr = Qfalse;
return TRUE;
@@ -200,7 +223,7 @@ keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr)
}
static VALUE
-args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr)
+args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, int check_only_symbol)
{
VALUE rest_hash;
@@ -209,7 +232,7 @@ args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr)
VM_ASSERT(args->argc > 0);
*kw_hash_ptr = args->argv[args->argc-1];
- if (keyword_hash_p(kw_hash_ptr, &rest_hash)) {
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash, check_only_symbol)) {
if (rest_hash) {
args->argv[args->argc-1] = rest_hash;
}
@@ -225,7 +248,7 @@ args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr)
if (len > 0) {
*kw_hash_ptr = RARRAY_AREF(args->rest, len - 1);
- if (keyword_hash_p(kw_hash_ptr, &rest_hash)) {
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash, check_only_symbol)) {
if (rest_hash) {
RARRAY_ASET(args->rest, len - 1, rest_hash);
}
@@ -672,11 +695,11 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
(kw_splat && given_argc > max_argc)) &&
args->kw_argv == NULL) {
if (((kw_flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT)) || !ec->cfp->iseq /* called from C */)) {
- if (args_pop_keyword_hash(args, &keyword_hash)) {
+ if (args_pop_keyword_hash(args, &keyword_hash, 0)) {
given_argc--;
}
}
- else if (args_pop_keyword_hash(args, &keyword_hash)) {
+ else if (args_pop_keyword_hash(args, &keyword_hash, 1)) {
/* Warn the following:
* def foo(k:1) p [k]; end
* foo({k:42}) #=> 42