Feature #7701 ยป 0001-required-keyword-arguments.patch
compile.c | ||
---|---|---|
if (args->kw_args) {
|
||
NODE *node = args->kw_args;
|
||
VALUE keywords = rb_ary_tmp_new(1);
|
||
int i = 0, j;
|
||
VALUE required = 0;
|
||
int i = 0, j, r = 0;
|
||
iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
|
||
COMPILE(optargs, "kwarg", args->kw_rest_arg);
|
||
while (node) {
|
||
rb_ary_push(keywords, INT2FIX(node->nd_body->nd_vid));
|
||
VALUE list = keywords;
|
||
if (node->nd_body->nd_value == (NODE *)-1) {
|
||
++r;
|
||
if (!required) required = rb_ary_tmp_new(1);
|
||
list = required;
|
||
}
|
||
rb_ary_push(list, INT2FIX(node->nd_body->nd_vid));
|
||
COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
|
||
node = node->nd_next;
|
||
i += 1;
|
||
}
|
||
iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
|
||
iseq->arg_keywords = i;
|
||
iseq->arg_keyword_required = r;
|
||
iseq->arg_keyword_table = ALLOC_N(ID, i);
|
||
if (r) {
|
||
rb_ary_concat(required, keywords);
|
||
keywords = required;
|
||
}
|
||
for (j = 0; j < i; j++) {
|
||
iseq->arg_keyword_table[j] = FIX2INT(RARRAY_PTR(keywords)[j]);
|
||
}
|
||
... | ... | |
}
|
||
case NODE_KW_ARG:{
|
||
LABEL *default_label = NEW_LABEL(nd_line(node));
|
||
LABEL *end_label = NEW_LABEL(nd_line(node));
|
||
LABEL *end_label = 0;
|
||
int idx, lv, ls;
|
||
ID id = node->nd_body->nd_vid;
|
||
... | ... | |
default:
|
||
rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body)));
|
||
}
|
||
ADD_INSNL(ret, nd_line(node), jump, end_label);
|
||
if (node->nd_body->nd_value != (NODE *)-1) {
|
||
end_label = NEW_LABEL(nd_line(node));
|
||
ADD_INSNL(ret, nd_line(node), jump, end_label);
|
||
}
|
||
ADD_LABEL(ret, default_label);
|
||
COMPILE_POPED(ret, "keyword default argument", node->nd_body);
|
||
ADD_LABEL(ret, end_label);
|
||
if (node->nd_body->nd_value != (NODE *)-1) {
|
||
COMPILE_POPED(ret, "keyword default argument", node->nd_body);
|
||
ADD_LABEL(ret, end_label);
|
||
}
|
||
break;
|
||
}
|
||
case NODE_DSYM:{
|
parse.y | ||
---|---|---|
$$ = rb_assoc_new($$, $2);
|
||
%*/
|
||
}
|
||
| tLABEL
|
||
{
|
||
arg_var(formal_argument(get_id($1)));
|
||
$$ = assignable($1, (NODE *)-1);
|
||
/*%%%*/
|
||
$$ = NEW_KW_ARG(0, $$);
|
||
/*%
|
||
$$ = rb_assoc_new($$, 0);
|
||
%*/
|
||
}
|
||
;
|
||
f_block_kw : tLABEL primary_value
|
||
... | ... | |
$$ = rb_assoc_new($$, $2);
|
||
%*/
|
||
}
|
||
| tLABEL
|
||
{
|
||
arg_var(formal_argument(get_id($1)));
|
||
$$ = assignable($1, (NODE *)-1);
|
||
/*%%%*/
|
||
$$ = NEW_KW_ARG(0, $$);
|
||
/*%
|
||
$$ = rb_assoc_new($$, 0);
|
||
%*/
|
||
}
|
||
;
|
||
f_block_kwarg : f_block_kw
|
test/ruby/test_keyword.rb | ||
---|---|---|
assert_valid_syntax("def bug7662(*, **) end")
|
||
assert_valid_syntax("def bug7662(a, **) end")
|
||
end
|
||
def test_required_keyword
|
||
feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
|
||
o = Object.new
|
||
assert_nothing_raised(SyntaxError, feature7701) do
|
||
eval("def o.foo(a:) a; end")
|
||
end
|
||
assert_raise(ArgumentError, feature7701) {o.foo}
|
||
assert_equal(42, o.foo(a: 42), feature7701)
|
||
end
|
||
def test_block_required_keyword
|
||
feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
|
||
b = assert_nothing_raised(SyntaxError, feature7701) do
|
||
break eval("proc {|a:| a}")
|
||
end
|
||
assert_raise(ArgumentError, feature7701) {b.call}
|
||
assert_equal(42, b.call(a: 42), feature7701)
|
||
end
|
||
end
|
vm_core.h | ||
---|---|---|
int arg_keyword;
|
||
int arg_keyword_check; /* if this is true, raise an ArgumentError when unknown keyword argument is passed */
|
||
int arg_keywords;
|
||
int arg_keyword_required;
|
||
ID *arg_keyword_table;
|
||
size_t stack_max; /* for stack overflow check */
|
vm_insnhelper.c | ||
---|---|---|
rb_exc_raise(exc);
|
||
}
|
||
NORETURN(static void keyword_error(const char *error, VALUE keys));
|
||
static void
|
||
keyword_error(const char *error, VALUE keys)
|
||
{
|
||
const char *msg = RARRAY_LEN(keys) == 1 ? "" : "s";
|
||
keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
|
||
rb_raise(rb_eArgError, "%s keyword%s: %"PRIsVALUE, error, msg, keys);
|
||
}
|
||
NORETURN(static void unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash));
|
||
static void
|
||
unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash)
|
||
{
|
||
VALUE sep = rb_usascii_str_new2(", "), keys;
|
||
const char *msg;
|
||
VALUE keys;
|
||
int i;
|
||
for (i = 0; i < iseq->arg_keywords; i++) {
|
||
rb_hash_delete(hash, ID2SYM(iseq->arg_keyword_table[i]));
|
||
}
|
||
keys = rb_funcall(hash, rb_intern("keys"), 0, 0);
|
||
if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
|
||
msg = RARRAY_LEN(keys) == 1 ? "" : "s";
|
||
keys = rb_funcall(keys, rb_intern("join"), 1, sep);
|
||
rb_raise(rb_eArgError, "unknown keyword%s: %"PRIsVALUE, msg, keys);
|
||
keyword_error("unknown", keys);
|
||
}
|
||
void
|
||
... | ... | |
argc--;
|
||
keyword_hash = rb_hash_dup(keyword_hash);
|
||
if (iseq->arg_keyword_check) {
|
||
for (i = j = 0; i < iseq->arg_keywords; i++) {
|
||
VALUE missing = Qnil;
|
||
for (i = 0; i < iseq->arg_keyword_required; i++) {
|
||
if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0))
|
||
continue;
|
||
if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
|
||
rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
|
||
}
|
||
if (!NIL_P(missing)) {
|
||
keyword_error("missing", missing);
|
||
}
|
||
for (j = i; i < iseq->arg_keywords; i++) {
|
||
if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0)) j++;
|
||
}
|
||
if (RHASH_TBL(keyword_hash)->num_entries > (unsigned int) j) {
|
||
... | ... | |
}
|
||
}
|
||
}
|
||
else if (iseq->arg_keyword_check && iseq->arg_keyword_required) {
|
||
VALUE missing = rb_ary_tmp_new(iseq->arg_keyword_required);
|
||
for (i = 0; i < iseq->arg_keyword_required; i++) {
|
||
rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
|
||
}
|
||
keyword_error("missing", missing);
|
||
}
|
||
else {
|
||
keyword_hash = rb_hash_new();
|
||
}
|