mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Arguments forwarding [Feature #16253]
This commit is contained in:
parent
35f90bf1b9
commit
62d4382877
Notes:
git
2019-10-22 02:36:12 +09:00
4 changed files with 99 additions and 3 deletions
9
NEWS
9
NEWS
|
@ -186,6 +186,15 @@ sufficient information, see the ChangeLog file or Redmine
|
|||
|
||||
* +yield+ in singleton class syntax is warned and will be deprecated later [Feature #15575].
|
||||
|
||||
* Argument forwarding by <code>...</code> is introduced. [Feature #16253]
|
||||
|
||||
def foo(...)
|
||||
bar(...)
|
||||
end
|
||||
|
||||
All arguments to +foo+ are forwarded to +bar+, including keyword and
|
||||
block arguments.
|
||||
|
||||
=== Core classes updates (outstanding ones only)
|
||||
|
||||
Array::
|
||||
|
|
47
parse.y
47
parse.y
|
@ -597,6 +597,10 @@ static void numparam_pop(struct parser_params *p, NODE *prev_inner);
|
|||
# define METHOD_NOT '!'
|
||||
#endif
|
||||
|
||||
#define idFWD_REST '*'
|
||||
#define idFWD_KWREST idPow /* Use simple "**", as tDSTAR is "**arg" */
|
||||
#define idFWD_BLOCK '&'
|
||||
|
||||
#define RE_OPTION_ONCE (1<<16)
|
||||
#define RE_OPTION_ENCODING_SHIFT 8
|
||||
#define RE_OPTION_ENCODING(e) (((e)&0xff)<<RE_OPTION_ENCODING_SHIFT)
|
||||
|
@ -1063,7 +1067,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in
|
|||
%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg
|
||||
%type <id> f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon
|
||||
%type <id> p_kwrest p_kwnorest
|
||||
%type <id> f_no_kwarg
|
||||
%type <id> f_no_kwarg args_forward
|
||||
%token END_OF_INPUT 0 "end-of-input"
|
||||
%token <id> '.'
|
||||
/* escaped chars, should be ignored otherwise */
|
||||
|
@ -2406,6 +2410,23 @@ paren_args : '(' opt_call_args rparen
|
|||
/*% %*/
|
||||
/*% ripper: arg_paren!(escape_Qundef($2)) %*/
|
||||
}
|
||||
| '(' args_forward rparen
|
||||
{
|
||||
if (!local_id(p, idFWD_REST) || !local_id(p, idFWD_KWREST) || !local_id(p, idFWD_BLOCK)) {
|
||||
compile_error(p, "unexpected ...");
|
||||
$$ = Qnone;
|
||||
}
|
||||
else {
|
||||
/*%%%*/
|
||||
NODE *splat = NEW_SPLAT(NEW_LVAR(idFWD_REST, &@2), &@2);
|
||||
NODE *kwrest = list_append(p, NEW_LIST(0, &@2), NEW_LVAR(idFWD_KWREST, &@2));
|
||||
NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, &@2), &@2);
|
||||
$$ = arg_append(p, splat, new_hash(p, kwrest, &@2), &@2);
|
||||
$$ = arg_blk_pass($$, block);
|
||||
/*% %*/
|
||||
/*% ripper: arg_paren!($2) %*/
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
opt_paren_args : none
|
||||
|
@ -3396,7 +3417,7 @@ block_param_def : '|' opt_bv_decl '|'
|
|||
/*%%%*/
|
||||
$$ = 0;
|
||||
/*% %*/
|
||||
/*% ripper: block_var!(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), escape_Qundef($2)) %*/
|
||||
/*% ripper: block_var!(params!(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), escape_Qundef($2)) %*/
|
||||
}
|
||||
| tOROP
|
||||
{
|
||||
|
@ -3404,7 +3425,7 @@ block_param_def : '|' opt_bv_decl '|'
|
|||
/*%%%*/
|
||||
$$ = 0;
|
||||
/*% %*/
|
||||
/*% ripper: block_var!(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), Qnil) %*/
|
||||
/*% ripper: block_var!(params!(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), Qnil) %*/
|
||||
}
|
||||
| '|' block_param opt_bv_decl '|'
|
||||
{
|
||||
|
@ -4862,6 +4883,17 @@ f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
|
|||
{
|
||||
$$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$);
|
||||
}
|
||||
| args_forward
|
||||
{
|
||||
arg_var(p, idFWD_REST);
|
||||
arg_var(p, idFWD_KWREST);
|
||||
arg_var(p, idFWD_BLOCK);
|
||||
/*%%%*/
|
||||
$$ = new_args_tail(p, Qnone, idFWD_KWREST, idFWD_BLOCK, &@1);
|
||||
$$ = new_args(p, Qnone, Qnone, idFWD_REST, Qnone, $$, &@$);
|
||||
/*% %*/
|
||||
/*% ripper: params_new(Qnone, Qnone, $1, Qnone, Qnone, Qnone, Qnone) %*/
|
||||
}
|
||||
| /* none */
|
||||
{
|
||||
$$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
|
||||
|
@ -4869,6 +4901,15 @@ f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
|
|||
}
|
||||
;
|
||||
|
||||
args_forward : tBDOT3
|
||||
{
|
||||
/*%%%*/
|
||||
$$ = idDot3;
|
||||
/*% %*/
|
||||
/*% ripper: args_forward! %*/
|
||||
}
|
||||
;
|
||||
|
||||
f_bad_arg : tCONSTANT
|
||||
{
|
||||
/*%%%*/
|
||||
|
|
|
@ -131,6 +131,12 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
|
|||
assert_equal true, thru_args_new
|
||||
end
|
||||
|
||||
def test_args_forward
|
||||
thru_args_forward = false
|
||||
parse('def m(...) n(...) end', :on_args_forward) {thru_args_forward = true}
|
||||
assert_equal true, thru_args_forward
|
||||
end
|
||||
|
||||
def test_arg_paren
|
||||
# FIXME
|
||||
end
|
||||
|
|
|
@ -1471,6 +1471,46 @@ eom
|
|||
assert_valid_syntax("tap {a = (break unless true)}")
|
||||
end
|
||||
|
||||
def test_argument_forwarding
|
||||
assert_valid_syntax('def foo(...) bar(...) end')
|
||||
assert_valid_syntax('def foo(...) end')
|
||||
assert_syntax_error('iter do |...| end', /unexpected/)
|
||||
assert_syntax_error('iter {|...|}', /unexpected/)
|
||||
assert_syntax_error('def foo(x, y, z) bar(...); end', /unexpected/)
|
||||
assert_syntax_error('def foo(x, y, z) super(...); end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) yield(...); end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) return(...); end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) a = (...); end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) [...]; end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) foo[...]; end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) foo[...] = x; end', /unexpected/)
|
||||
assert_syntax_error('def foo(...) foo(...) { }; end', /both block arg and actual block given/)
|
||||
assert_syntax_error('def foo(...) defined?(...); end', /unexpected/)
|
||||
|
||||
obj1 = Object.new
|
||||
def obj1.bar(*args, **kws, &block)
|
||||
block.call(args, kws)
|
||||
end
|
||||
obj1.instance_eval('def foo(...) bar(...) end')
|
||||
|
||||
klass = Class.new {
|
||||
def foo(*args, **kws, &block)
|
||||
block.call(args, kws)
|
||||
end
|
||||
}
|
||||
obj2 = klass.new
|
||||
obj2.instance_eval('def foo(...) super(...) end')
|
||||
|
||||
[obj1, obj2].each do |obj|
|
||||
assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5) {|*x| x})
|
||||
assert_equal(-1, obj.:foo.arity)
|
||||
parameters = obj.:foo.parameters
|
||||
assert_equal(:rest, parameters.dig(0, 0))
|
||||
assert_equal(:keyrest, parameters.dig(1, 0))
|
||||
assert_equal(:block, parameters.dig(2, 0))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def not_label(x) @result = x; @not_label ||= nil end
|
||||
|
|
Loading…
Reference in a new issue