Implement it
(#9199)
[[Feature #18980]](https://bugs.ruby-lang.org/issues/18980) Co-authored-by: Yusuke Endoh <mame@ruby-lang.org>
This commit is contained in:
parent
98eeadc932
commit
44592c4e20
89
parse.y
89
parse.y
@ -393,6 +393,7 @@ struct local_vars {
|
|||||||
struct {
|
struct {
|
||||||
NODE *outer, *inner, *current;
|
NODE *outer, *inner, *current;
|
||||||
} numparam;
|
} numparam;
|
||||||
|
NODE *it;
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -488,6 +489,7 @@ struct parser_params {
|
|||||||
int node_id;
|
int node_id;
|
||||||
|
|
||||||
int max_numparam;
|
int max_numparam;
|
||||||
|
ID it_id;
|
||||||
|
|
||||||
struct lex_context ctxt;
|
struct lex_context ctxt;
|
||||||
|
|
||||||
@ -1223,7 +1225,7 @@ static NODE *new_hash_pattern(struct parser_params *p, NODE *constant, NODE *hsh
|
|||||||
static NODE *new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, const YYLTYPE *loc);
|
static NODE *new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, const YYLTYPE *loc);
|
||||||
|
|
||||||
static rb_node_kw_arg_t *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
|
static rb_node_kw_arg_t *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
|
||||||
static rb_node_args_t *args_with_numbered(struct parser_params*,rb_node_args_t*,int);
|
static rb_node_args_t *args_with_numbered(struct parser_params*,rb_node_args_t*,int,ID);
|
||||||
|
|
||||||
static VALUE negate_lit(struct parser_params*, VALUE);
|
static VALUE negate_lit(struct parser_params*, VALUE);
|
||||||
static NODE *ret_args(struct parser_params*,NODE*);
|
static NODE *ret_args(struct parser_params*,NODE*);
|
||||||
@ -1480,7 +1482,7 @@ new_args_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg, VALUE b
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
args_with_numbered(struct parser_params *p, VALUE args, int max_numparam)
|
args_with_numbered(struct parser_params *p, VALUE args, int max_numparam, ID it_id)
|
||||||
{
|
{
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@ -2099,6 +2101,7 @@ get_nd_args(struct parser_params *p, NODE *node)
|
|||||||
%type <tbl> p_lparen p_lbracket p_pktbl p_pvtbl
|
%type <tbl> p_lparen p_lbracket p_pktbl p_pvtbl
|
||||||
/* ripper */ %type <num> max_numparam
|
/* ripper */ %type <num> max_numparam
|
||||||
/* ripper */ %type <node> numparam
|
/* ripper */ %type <node> numparam
|
||||||
|
/* ripper */ %type <id> it_id
|
||||||
%token END_OF_INPUT 0 "end-of-input"
|
%token END_OF_INPUT 0 "end-of-input"
|
||||||
%token <id> '.'
|
%token <id> '.'
|
||||||
|
|
||||||
@ -4743,6 +4746,12 @@ numparam : {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
it_id : {
|
||||||
|
$$ = p->it_id;
|
||||||
|
p->it_id = 0;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
lambda : tLAMBDA[dyna]
|
lambda : tLAMBDA[dyna]
|
||||||
{
|
{
|
||||||
token_info_push(p, "->", &@1);
|
token_info_push(p, "->", &@1);
|
||||||
@ -4750,7 +4759,7 @@ lambda : tLAMBDA[dyna]
|
|||||||
$<num>$ = p->lex.lpar_beg;
|
$<num>$ = p->lex.lpar_beg;
|
||||||
p->lex.lpar_beg = p->lex.paren_nest;
|
p->lex.lpar_beg = p->lex.paren_nest;
|
||||||
}[lpar]
|
}[lpar]
|
||||||
max_numparam numparam allow_exits
|
max_numparam numparam it_id allow_exits
|
||||||
f_larglist[args]
|
f_larglist[args]
|
||||||
{
|
{
|
||||||
CMDARG_PUSH(0);
|
CMDARG_PUSH(0);
|
||||||
@ -4758,11 +4767,13 @@ lambda : tLAMBDA[dyna]
|
|||||||
lambda_body[body]
|
lambda_body[body]
|
||||||
{
|
{
|
||||||
int max_numparam = p->max_numparam;
|
int max_numparam = p->max_numparam;
|
||||||
|
ID it_id = p->it_id;
|
||||||
p->lex.lpar_beg = $<num>lpar;
|
p->lex.lpar_beg = $<num>lpar;
|
||||||
p->max_numparam = $max_numparam;
|
p->max_numparam = $max_numparam;
|
||||||
|
p->it_id = $it_id;
|
||||||
restore_block_exit(p, $allow_exits);
|
restore_block_exit(p, $allow_exits);
|
||||||
CMDARG_POP();
|
CMDARG_POP();
|
||||||
$args = args_with_numbered(p, $args, max_numparam);
|
$args = args_with_numbered(p, $args, max_numparam, it_id);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
{
|
{
|
||||||
YYLTYPE loc = code_loc_gen(&@args, &@body);
|
YYLTYPE loc = code_loc_gen(&@args, &@body);
|
||||||
@ -4950,12 +4961,14 @@ brace_block : '{' brace_body '}'
|
|||||||
;
|
;
|
||||||
|
|
||||||
brace_body : {$<vars>$ = dyna_push(p);}[dyna]
|
brace_body : {$<vars>$ = dyna_push(p);}[dyna]
|
||||||
max_numparam numparam allow_exits
|
max_numparam numparam it_id allow_exits
|
||||||
opt_block_param[args] compstmt
|
opt_block_param[args] compstmt
|
||||||
{
|
{
|
||||||
int max_numparam = p->max_numparam;
|
int max_numparam = p->max_numparam;
|
||||||
|
ID it_id = p->it_id;
|
||||||
p->max_numparam = $max_numparam;
|
p->max_numparam = $max_numparam;
|
||||||
$args = args_with_numbered(p, $args, max_numparam);
|
p->it_id = $it_id;
|
||||||
|
$args = args_with_numbered(p, $args, max_numparam, it_id);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = NEW_ITER($args, $compstmt, &@$);
|
$$ = NEW_ITER($args, $compstmt, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
@ -4970,12 +4983,14 @@ do_body : {
|
|||||||
$<vars>$ = dyna_push(p);
|
$<vars>$ = dyna_push(p);
|
||||||
CMDARG_PUSH(0);
|
CMDARG_PUSH(0);
|
||||||
}[dyna]
|
}[dyna]
|
||||||
max_numparam numparam allow_exits
|
max_numparam numparam it_id allow_exits
|
||||||
opt_block_param[args] bodystmt
|
opt_block_param[args] bodystmt
|
||||||
{
|
{
|
||||||
int max_numparam = p->max_numparam;
|
int max_numparam = p->max_numparam;
|
||||||
|
ID it_id = p->it_id;
|
||||||
p->max_numparam = $max_numparam;
|
p->max_numparam = $max_numparam;
|
||||||
$args = args_with_numbered(p, $args, max_numparam);
|
p->it_id = $<id>it_id;
|
||||||
|
$args = args_with_numbered(p, $args, max_numparam, it_id);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = NEW_ITER($args, $bodystmt, &@$);
|
$$ = NEW_ITER($args, $bodystmt, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
@ -12715,6 +12730,34 @@ numparam_nested_p(struct parser_params *p)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
numparam_used_p(struct parser_params *p)
|
||||||
|
{
|
||||||
|
NODE *numparam = p->lvtbl->numparam.current;
|
||||||
|
if (numparam) {
|
||||||
|
compile_error(p, "numbered parameter is already used in\n"
|
||||||
|
"%s:%d: current block here",
|
||||||
|
p->ruby_sourcefile, nd_line(numparam));
|
||||||
|
parser_show_error_line(p, &numparam->nd_loc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
it_used_p(struct parser_params *p)
|
||||||
|
{
|
||||||
|
NODE *it = p->lvtbl->it;
|
||||||
|
if (it) {
|
||||||
|
compile_error(p, "`it` is already used in\n"
|
||||||
|
"%s:%d: current block here",
|
||||||
|
p->ruby_sourcefile, nd_line(it));
|
||||||
|
parser_show_error_line(p, &it->nd_loc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static NODE*
|
static NODE*
|
||||||
gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
@ -12751,7 +12794,7 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
|||||||
switch (id_type(id)) {
|
switch (id_type(id)) {
|
||||||
case ID_LOCAL:
|
case ID_LOCAL:
|
||||||
if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
|
if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
|
||||||
if (NUMPARAM_ID_P(id) && numparam_nested_p(p)) return 0;
|
if (NUMPARAM_ID_P(id) && (numparam_nested_p(p) || it_used_p(p))) return 0;
|
||||||
if (id == p->cur_arg) {
|
if (id == p->cur_arg) {
|
||||||
compile_error(p, "circular argument reference - %"PRIsWARN, rb_id2str(id));
|
compile_error(p, "circular argument reference - %"PRIsWARN, rb_id2str(id));
|
||||||
return 0;
|
return 0;
|
||||||
@ -12771,7 +12814,7 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
|||||||
}
|
}
|
||||||
if (dyna_in_block(p) && NUMPARAM_ID_P(id) &&
|
if (dyna_in_block(p) && NUMPARAM_ID_P(id) &&
|
||||||
parser_numbered_param(p, NUMPARAM_ID_TO_IDX(id))) {
|
parser_numbered_param(p, NUMPARAM_ID_TO_IDX(id))) {
|
||||||
if (numparam_nested_p(p)) return 0;
|
if (numparam_nested_p(p) || it_used_p(p)) return 0;
|
||||||
node = NEW_DVAR(id, loc);
|
node = NEW_DVAR(id, loc);
|
||||||
struct local_vars *local = p->lvtbl;
|
struct local_vars *local = p->lvtbl;
|
||||||
if (!local->numparam.current) local->numparam.current = node;
|
if (!local->numparam.current) local->numparam.current = node;
|
||||||
@ -12783,10 +12826,19 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
/* method call without arguments */
|
/* method call without arguments */
|
||||||
if (dyna_in_block(p) && id == rb_intern("it")
|
if (dyna_in_block(p) && id == rb_intern("it") && !(DVARS_TERMINAL_P(p->lvtbl->args) || DVARS_TERMINAL_P(p->lvtbl->args->prev))) {
|
||||||
&& !(DVARS_TERMINAL_P(p->lvtbl->args) || DVARS_TERMINAL_P(p->lvtbl->args->prev))
|
if (numparam_used_p(p)) return 0;
|
||||||
&& p->max_numparam != ORDINAL_PARAM) {
|
if (p->max_numparam == ORDINAL_PARAM) {
|
||||||
rb_warn0("`it` calls without arguments will refer to the first block param in Ruby 3.4; use it() or self.it");
|
compile_error(p, "ordinary parameter is defined");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!p->it_id) {
|
||||||
|
p->it_id = internal_id(p);
|
||||||
|
vtable_add(p->lvtbl->args, p->it_id);
|
||||||
|
}
|
||||||
|
NODE *node = NEW_DVAR(p->it_id, loc);
|
||||||
|
if (!p->lvtbl->it) p->lvtbl->it = node;
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
return NEW_VCALL(id, loc);
|
return NEW_VCALL(id, loc);
|
||||||
case ID_GLOBAL:
|
case ID_GLOBAL:
|
||||||
@ -14441,15 +14493,15 @@ new_args_tail(struct parser_params *p, rb_node_kw_arg_t *kw_args, ID kw_rest_arg
|
|||||||
}
|
}
|
||||||
|
|
||||||
static rb_node_args_t *
|
static rb_node_args_t *
|
||||||
args_with_numbered(struct parser_params *p, rb_node_args_t *args, int max_numparam)
|
args_with_numbered(struct parser_params *p, rb_node_args_t *args, int max_numparam, ID it_id)
|
||||||
{
|
{
|
||||||
if (max_numparam > NO_PARAM) {
|
if (max_numparam > NO_PARAM || it_id) {
|
||||||
if (!args) {
|
if (!args) {
|
||||||
YYLTYPE loc = RUBY_INIT_YYLLOC();
|
YYLTYPE loc = RUBY_INIT_YYLLOC();
|
||||||
args = new_args_tail(p, 0, 0, 0, 0);
|
args = new_args_tail(p, 0, 0, 0, 0);
|
||||||
nd_set_loc(RNODE(args), &loc);
|
nd_set_loc(RNODE(args), &loc);
|
||||||
}
|
}
|
||||||
args->nd_ainfo.pre_args_num = max_numparam;
|
args->nd_ainfo.pre_args_num = it_id ? 1 : max_numparam;
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@ -14848,6 +14900,7 @@ local_push(struct parser_params *p, int toplevel_scope)
|
|||||||
local->numparam.outer = 0;
|
local->numparam.outer = 0;
|
||||||
local->numparam.inner = 0;
|
local->numparam.inner = 0;
|
||||||
local->numparam.current = 0;
|
local->numparam.current = 0;
|
||||||
|
local->it = 0;
|
||||||
#endif
|
#endif
|
||||||
local->used = warn_unused_vars ? vtable_alloc(0) : 0;
|
local->used = warn_unused_vars ? vtable_alloc(0) : 0;
|
||||||
|
|
||||||
@ -15068,6 +15121,7 @@ numparam_push(struct parser_params *p)
|
|||||||
}
|
}
|
||||||
local->numparam.inner = 0;
|
local->numparam.inner = 0;
|
||||||
local->numparam.current = 0;
|
local->numparam.current = 0;
|
||||||
|
local->it = 0;
|
||||||
return inner;
|
return inner;
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
@ -15096,6 +15150,7 @@ numparam_pop(struct parser_params *p, NODE *prev_inner)
|
|||||||
/* no numbered parameter */
|
/* no numbered parameter */
|
||||||
local->numparam.current = 0;
|
local->numparam.current = 0;
|
||||||
}
|
}
|
||||||
|
local->it = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,16 +1778,52 @@ eom
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_it
|
def test_it
|
||||||
assert_no_warning(/`it`/) {eval('if false; it; end')}
|
assert_valid_syntax('proc {it}')
|
||||||
assert_no_warning(/`it`/) {eval('def foo; it; end')}
|
assert_syntax_error('[1,2].then {it+_2}', /`it` is already used/)
|
||||||
assert_warn(/`it`/) {eval('0.times { it }')}
|
assert_syntax_error('[1,2].then {_2+it}', /numbered parameter is already used/)
|
||||||
assert_no_warning(/`it`/) {eval('0.times { || it }')}
|
assert_equal([1, 2], eval('[1,2].then {it}'))
|
||||||
assert_no_warning(/`it`/) {eval('0.times { |_n| it }')}
|
assert_syntax_error('[1,2].then {"#{it}#{_2}"}', /`it` is already used/)
|
||||||
assert_warn(/`it`/) {eval('0.times { it; it = 1; it }')}
|
assert_syntax_error('[1,2].then {"#{_2}#{it}"}', /numbered parameter is already used/)
|
||||||
assert_no_warning(/`it`/) {eval('0.times { it = 1; it }')}
|
assert_syntax_error('->{it+_2}.call(1,2)', /`it` is already used/)
|
||||||
assert_no_warning(/`it`/) {eval('it = 1; 0.times { it }')}
|
assert_syntax_error('->{_2+it}.call(1,2)', /numbered parameter is already used/)
|
||||||
ensure
|
assert_equal(4, eval('->(a=->{it}){a}.call.call(4)'))
|
||||||
self.class.remove_method(:foo)
|
assert_equal(5, eval('-> a: ->{it} {a}.call.call(5)'))
|
||||||
|
assert_syntax_error('proc {|| it}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('proc {|;a| it}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error("proc {|\n| it}", /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('proc {|x| it}', /ordinary parameter is defined/)
|
||||||
|
assert_equal([1, 2], eval('1.then {[it, 2.then {_1}]}'))
|
||||||
|
assert_equal([2, 1], eval('1.then {[2.then {_1}, it]}'))
|
||||||
|
assert_syntax_error('->(){it}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('->(x){it}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('->x{it}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('->x:_1{}', /ordinary parameter is defined/)
|
||||||
|
assert_syntax_error('->x=it{}', /ordinary parameter is defined/)
|
||||||
|
assert_valid_syntax('-> {it; -> {_2}}')
|
||||||
|
assert_valid_syntax('-> {-> {it}; _2}')
|
||||||
|
assert_equal([1, nil], eval('proc {that=it; it=nil; [that, it]}.call(1)'))
|
||||||
|
assert_equal(1, eval('proc {it = 1}.call'))
|
||||||
|
assert_equal(2, eval('a=Object.new; def a.foo; it = 2; end; a.foo'))
|
||||||
|
assert_equal(3, eval('proc {|it| it}.call(3)'))
|
||||||
|
assert_equal(4, eval('a=Object.new; def a.foo(it); it; end; a.foo(4)'))
|
||||||
|
assert_equal(5, eval('a=Object.new; def a.it; 5; end; a.it'))
|
||||||
|
assert_equal(6, eval('a=Class.new; a.class_eval{ def it; 6; end }; a.new.it'))
|
||||||
|
assert_raise_with_message(NameError, /undefined local variable or method `it'/) do
|
||||||
|
eval('it')
|
||||||
|
end
|
||||||
|
['class C', 'class << C', 'module M', 'def m', 'def o.m'].each do |c|
|
||||||
|
assert_valid_syntax("->{#{c};->{it};end;it}\n")
|
||||||
|
assert_valid_syntax("->{it;#{c};->{it};end}\n")
|
||||||
|
end
|
||||||
|
1.times do
|
||||||
|
[
|
||||||
|
assert_equal(0, it),
|
||||||
|
assert_equal([:a], eval('[:a].map{it}')),
|
||||||
|
assert_raise(NameError) {eval('it')},
|
||||||
|
]
|
||||||
|
end
|
||||||
|
assert_valid_syntax('proc {def foo(_);end;it}')
|
||||||
|
assert_syntax_error('p { [it **2] }', /unexpected \*\*arg/)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_value_expr_in_condition
|
def test_value_expr_in_condition
|
||||||
|
Loading…
x
Reference in New Issue
Block a user