aboutsummaryrefslogtreecommitdiffstats
path: root/vm_args.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-08-17 22:43:26 -0700
committerJeremy Evans <code@jeremyevans.net>2019-08-30 12:39:31 -0700
commit5c507db467f20a6981ce6b282a607d4d975bcb07 (patch)
treed2954279ebf275863d34c83987a99999de3712b0 /vm_args.c
parent8399609e5a27d3d993c9fbb08f9d88051f26d455 (diff)
downloadruby-5c507db467f20a6981ce6b282a607d4d975bcb07.tar.gz
Implement keyword argument to last positional hash emulation
For methods that accept keyword arguments but do not accept a keyword splat, if a keyword splat is passed, or keywords are used with a non-symbol key, check the hash. If the hash contains all symbols, keep the same behavior as before. If the hash contains all non-symbols, move the hash to the last positional hash and warn. If the hash contains symbols and non-Symbols, split the hash and use the symbol keys for the keyword hash and non-symbol keys for the positional hash and warn.
Diffstat (limited to 'vm_args.c')
-rw-r--r--vm_args.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/vm_args.c b/vm_args.c
index b276c36ccd..a91db40cf9 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -585,6 +585,20 @@ get_loc(struct rb_calling_info *calling, const struct rb_call_info *ci)
}
static inline void
+rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_call_info *ci)
+{
+ if (calling->recv == Qundef) return;
+ VALUE loc = get_loc(calling, ci);
+ if (NIL_P(loc)) {
+ rb_warn("The keyword argument for `%s' is passed as the last hash parameter", rb_id2name(ci->mid));
+ }
+ else {
+ rb_warn("The keyword argument for `%s' (defined at %s:%d) is passed as the last hash parameter",
+ rb_id2name(ci->mid), RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)));
+ }
+}
+
+static inline void
rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci)
{
if (calling->recv == Qundef) return;
@@ -746,9 +760,21 @@ 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, 0)) {
+ int check_only_symbol = (kw_flag & VM_CALL_KW_SPLAT) &&
+ iseq->body->param.flags.has_kw &&
+ !iseq->body->param.flags.has_kwrest;
+
+ if (args_pop_keyword_hash(args, &keyword_hash, check_only_symbol)) {
given_argc--;
}
+ else if (check_only_symbol) {
+ if (keyword_hash != Qnil) {
+ rb_warn_split_last_hash_to_keyword(calling, ci);
+ }
+ else {
+ rb_warn_keyword_to_last_hash(calling, ci);
+ }
+ }
}
else if (args_pop_keyword_hash(args, &keyword_hash, 1)) {
/* Warn the following: