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:
parent
afae8555da
commit
6a9ce1fea8
Notes:
git
2019-08-31 04:40:15 +09:00
7 changed files with 67 additions and 0 deletions
|
@ -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
1
node.h
|
@ -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
14
parse.y
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue