Project

General

Profile

Feature #16891 ยป keyword-hash-integration.diff

jeremyevans0 (Jeremy Evans), 05/14/2020 08:11 PM

View differences:

vm_args.c
187 187
    return ary;
188 188
}
189 189

  
190
static int
191
keyword_hash_has_symbol_p_iter(st_data_t key, st_data_t val, st_data_t arg)
192
{
193
    if (SYMBOL_P((VALUE)key)) {
194
        *(int*)arg |= 1;
195
        return ST_STOP;
196
    }
197

  
198
    return ST_CONTINUE;
199
}
200

  
201
static int
202
keyword_hash_has_symbol_p(VALUE kw_hash) {
203
    int has_symbol = 0;
204
    rb_hash_stlike_foreach(kw_hash, keyword_hash_has_symbol_p_iter, (st_data_t)(&has_symbol));
205
    return has_symbol;
206
}
207

  
208
static int
209
keyword_hash_p(VALUE *kw_hash_ptr, int check_symbol)
210
{
211
    *kw_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
212

  
213
    if (NIL_P(*kw_hash_ptr)) {
214
        return FALSE;
215
    }
216

  
217
    if (!check_symbol || RHASH_EMPTY_P(*kw_hash_ptr) ||
218
            keyword_hash_has_symbol_p(*kw_hash_ptr)) {
219
        return TRUE;
220
    }
221

  
222
    *kw_hash_ptr = Qnil;
223
    return FALSE;
224
}
225

  
226
static VALUE
227
args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, int check_symbol)
228
{
229
    if (args->rest == Qfalse) {
230
      from_argv:
231
        VM_ASSERT(args->argc > 0);
232
        *kw_hash_ptr = args->argv[args->argc-1];
233

  
234
        if (keyword_hash_p(kw_hash_ptr, check_symbol)) {
235
            args->argc--;
236
            return TRUE;
237
        }
238
    }
239
    else {
240
        long len = RARRAY_LEN(args->rest);
241

  
242
        if (len > 0) {
243
            *kw_hash_ptr = RARRAY_AREF(args->rest, len - 1);
244

  
245
            if (keyword_hash_p(kw_hash_ptr, check_symbol)) {
246
                arg_rest_dup(args);
247
                rb_ary_pop(args->rest);
248
                return TRUE;
249
            }
250
        }
251
        else {
252
            goto from_argv;
253
        }
254
    }
255

  
256
    return FALSE;
257
}
258

  
190 259
static int
191 260
args_kw_argv_to_hash(struct args_info *args)
192 261
{
......
452 521
           RHASH_EMPTY_P(keyword_hash);
453 522
}
454 523

  
524
VALUE rb_iseq_location(const rb_iseq_t *iseq);
525

  
526
static st_table *caller_to_callees = 0;
527

  
528
static VALUE
529
rb_warn_check(const rb_execution_context_t * const ec, const rb_iseq_t *const iseq)
530
{
531
    if (!rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) return 1;
532

  
533
    if (!iseq) return 0;
534

  
535
    const st_data_t callee = (st_data_t)(iseq->body->iseq_unique_id * 2);
536

  
537
    const rb_control_frame_t * const cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
538

  
539
    if (!cfp) return 0;
540

  
541
    const st_data_t caller = (st_data_t)cfp->pc;
542

  
543
    if (!caller_to_callees) {
544
        caller_to_callees = st_init_numtable();
545
    }
546

  
547
    st_data_t val;
548
    if (st_lookup(caller_to_callees, caller, &val)) {
549
        st_table *callees;
550

  
551
        if (val & 1) {
552
            val &= ~(st_data_t)1;
553
            if (val == callee) return 1; /* already warned */
554

  
555
            callees = st_init_numtable();
556
            st_insert(callees, val, 1);
557
        }
558
        else {
559
            callees = (st_table *) val;
560
            if (st_is_member(callees, callee)) return 1; /* already warned */
561
        }
562
        st_insert(callees, callee, 1);
563
        st_insert(caller_to_callees, caller, (st_data_t) callees);
564
    }
565
    else {
566
        st_insert(caller_to_callees, caller, callee | 1);
567
    }
568

  
569
    return 0; /* not warned yet for the pair of caller and callee */
570
}
571

  
572
static inline void
573
rb_warn_last_hash_to_keyword(rb_execution_context_t * const ec,
574
                            struct rb_calling_info * const calling,
575
                            const struct rb_callinfo *ci,
576
                            const rb_iseq_t * const iseq)
577
{
578
    if (rb_warn_check(ec, iseq)) return;
579

  
580
    VALUE name, loc;
581
    name = rb_id2str(vm_ci_mid(ci));
582
    loc = rb_iseq_location(iseq);
583
    if (NIL_P(loc)) {
584
        rb_warning("Using the last argument for `%"PRIsVALUE"' as keyword parameters is deprecated; maybe ** should be added to the call",
585
                name);
586
    }
587
    else {
588
        rb_warning("Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call");
589
        if (calling->recv != Qundef) {
590
            rb_compile_warning(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
591
                            "The called method `%"PRIsVALUE"' is defined here", name);
592
        }
593
        else {
594
            rb_compile_warning(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
595
                            "The called method is defined here");
596
        }
597
    }
598
}
599

  
455 600
static int
456 601
setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
457 602
			 struct rb_calling_info *const calling,
......
626 771
        }
627 772
    }
628 773

  
774
    if (!kw_flag && args->kw_argv == NULL &&
775
            (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
776
            given_argc > min_argc &&
777
            args_pop_keyword_hash(args, &keyword_hash, !iseq->body->param.flags.has_kwrest)) {
778
        rb_warn_last_hash_to_keyword(ec, calling, ci, iseq);
779
        given_argc--;
780
    }
781

  
629 782
    if (given_argc > max_argc && max_argc != UNLIMITED_ARGUMENTS) {
630 783
	if (arg_setup_type == arg_setup_block) {
631 784
	    /* truncate */