1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Support **nil syntax for specifying a method does not accept keyword arguments

This syntax means the method should be treated as a method that
uses keyword arguments, but no specific keyword arguments are
supported, and therefore calling the method with keyword arguments
will raise an ArgumentError.  It is still allowed to double splat
an empty hash when calling the method, as that does not pass
any keyword arguments.
This commit is contained in:
Jeremy Evans 2019-04-19 22:19:41 +09:00
parent afae8555da
commit 6a9ce1fea8
Notes: git 2019-08-31 04:40:15 +09:00
7 changed files with 67 additions and 0 deletions

View file

@ -1700,6 +1700,9 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
body->param.keyword = keyword;
body->param.flags.has_kwrest = TRUE;
}
else if (args->no_kwarg) {
body->param.flags.accepts_no_kwarg = TRUE;
}
if (block_id) {
body->param.block_start = arg_size++;

1
node.h
View file

@ -450,6 +450,7 @@ struct rb_args_info {
NODE *kw_rest_arg;
NODE *opt_args;
int no_kwarg;
};
struct rb_ary_pattern_info {

14
parse.y
View file

@ -3245,6 +3245,10 @@ block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
{
$$ = new_args_tail(p, Qnone, $1, $2, &@1);
}
| f_no_kwarg opt_f_block_arg
{
$$ = new_args_tail(p, Qnone, rb_intern("nil"), $2, &@1);
}
| f_block_arg
{
$$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
@ -4712,6 +4716,10 @@ args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
{
$$ = new_args_tail(p, Qnone, $1, $2, &@1);
}
| f_no_kwarg opt_f_block_arg
{
$$ = new_args_tail(p, Qnone, rb_intern("nil"), $2, &@1);
}
| f_block_arg
{
$$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
@ -4968,6 +4976,9 @@ kwrest_mark : tPOW
| tDSTAR
;
f_no_kwarg : kwrest_mark keyword_nil
;
f_kwrest : kwrest_mark tIDENTIFIER
{
arg_var(p, shadowing_lvar(p, get_id($2)));
@ -11125,6 +11136,9 @@ new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block,
args->kw_rest_arg = NEW_DVAR(kw_rest_arg, loc);
args->kw_rest_arg->nd_cflag = kw_bits;
}
else if (kw_rest_arg == rb_intern("nil")) {
args->no_kwarg = 1;
}
else if (kw_rest_arg) {
args->kw_rest_arg = NEW_DVAR(kw_rest_arg, loc);
}

View file

@ -126,6 +126,34 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(1, f10(b: 42))
end
def f11(**nil)
local_variables
end
def test_f11
h = {}
assert_equal([], f11)
assert_equal([], f11(**{}))
assert_equal([], f11(**h))
end
def f12(**nil, &b)
[b, local_variables]
end
def test_f12
h = {}
b = proc{}
assert_equal([nil, [:b]], f12)
assert_equal([nil, [:b]], f12(**{}))
assert_equal([nil, [:b]], f12(**h))
assert_equal([b, [:b]], f12(&b))
assert_equal([b, [:b]], f12(**{}, &b))
assert_equal([b, [:b]], f12(**h, &b))
end
def test_method_parameters
assert_equal([[:key, :str], [:key, :num]], method(:f1).parameters);
assert_equal([[:req, :x], [:key, :str], [:key, :num]], method(:f2).parameters);

View file

@ -249,6 +249,22 @@ class TestSyntax < Test::Unit::TestCase
assert_syntax_error('def o.foo(@@foo: a) end', /class variable/)
end
def test_keywords_specified_and_not_accepted
assert_syntax_error('def o.foo(a:, **nil) end', /unexpected/)
assert_syntax_error('def o.foo(a:, **nil, &b) end', /unexpected/)
assert_syntax_error('def o.foo(**a, **nil) end', /unexpected/)
assert_syntax_error('def o.foo(**a, **nil, &b) end', /unexpected/)
assert_syntax_error('def o.foo(**nil, **a) end', /unexpected/)
assert_syntax_error('def o.foo(**nil, **a, &b) end', /unexpected/)
assert_syntax_error('proc do |a:, **nil| end', /unexpected/)
assert_syntax_error('proc do |a:, **nil, &b| end', /unexpected/)
assert_syntax_error('proc do |**a, **nil| end', /unexpected/)
assert_syntax_error('proc do |**a, **nil, &b| end', /unexpected/)
assert_syntax_error('proc do |**nil, **a| end', /unexpected/)
assert_syntax_error('proc do |**nil, **a, &b| end', /unexpected/)
end
def test_optional_self_reference
bug9593 = '[ruby-core:61299] [Bug #9593]'
o = Object.new

View file

@ -702,6 +702,10 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args->rest = Qfalse;
}
if (kw_flag && iseq->body->param.flags.accepts_no_kwarg) {
rb_raise(rb_eArgError, "no keywords accepted");
}
switch (arg_setup_type) {
case arg_setup_method:
break; /* do nothing special */

View file

@ -385,6 +385,7 @@ struct rb_iseq_constant_body {
unsigned int has_block : 1;
unsigned int ambiguous_param0 : 1; /* {|a|} */
unsigned int accepts_no_kwarg : 1;
} flags;
unsigned int size;