Fix `retry` in nested `rescue` blocks

Restore `rescue`-context from the outer context.
`retry` targets the next outer block except for between `rescue` and
`else` or `ensure`, otherwise, if there is no enclosing block, it
should be syntax error.
This commit is contained in:
Nobuyoshi Nakada 2023-09-29 01:58:07 +09:00
Родитель 5a376f0f71
Коммит eaa0fbf9b9
2 изменённых файлов: 44 добавлений и 10 удалений

33
parse.y
Просмотреть файл

@ -1571,6 +1571,12 @@ static NODE *allow_block_exit(struct parser_params *p);
static void restore_block_exit(struct parser_params *p, NODE *exits);
static void clear_block_exit(struct parser_params *p, bool error);
static void
next_rescue_context(struct lex_context *next, const struct lex_context *outer, enum rescue_context def)
{
next->in_rescue = outer->in_rescue == after_rescue ? after_rescue : def;
}
static void
restore_defun(struct parser_params *p, NODE *name)
{
@ -2116,29 +2122,37 @@ begin_block : block_open top_compstmt '}'
}
;
bodystmt : compstmt
bodystmt : compstmt[body]
lex_ctxt[ctxt]
opt_rescue
k_else
{
if (!$2) yyerror1(&@3, "else without rescue is useless");
p->ctxt.in_rescue = after_else;
if (!$opt_rescue) yyerror1(&@k_else, "else without rescue is useless");
next_rescue_context(&p->ctxt, &$ctxt, after_else);
}
compstmt[elsebody]
{
next_rescue_context(&p->ctxt, &$ctxt, after_ensure);
}
compstmt
opt_ensure
{
/*%%%*/
$$ = new_bodystmt(p, $1, $2, $5, $6, &@$);
$$ = new_bodystmt(p, $body, $opt_rescue, $elsebody, $opt_ensure, &@$);
/*% %*/
/*% ripper: bodystmt!($1, $2, $5, $6) %*/
/*% ripper: bodystmt!($body, $opt_rescue, $elsebody, $opt_ensure) %*/
}
| compstmt
| compstmt[body]
lex_ctxt[ctxt]
opt_rescue
{
next_rescue_context(&p->ctxt, &$ctxt, after_ensure);
}
opt_ensure
{
/*%%%*/
$$ = new_bodystmt(p, $1, $2, 0, $3, &@$);
$$ = new_bodystmt(p, $body, $opt_rescue, 0, $opt_ensure, &@$);
/*% %*/
/*% ripper: bodystmt!($1, $2, Qnil, $3) %*/
/*% ripper: bodystmt!($body, $opt_rescue, Qnil, $opt_ensure) %*/
}
;
@ -4242,7 +4256,6 @@ k_ensure : keyword_ensure
{
token_info_warn(p, "ensure", p->token_info, 1, &@$);
$$ = p->ctxt;
p->ctxt.in_rescue = after_ensure;
}
;

Просмотреть файл

@ -283,6 +283,27 @@ class TestAst < Test::Unit::TestCase
assert_parse("begin rescue; ensure; defined? retry; end")
assert_parse("END {defined? retry}")
assert_parse("begin rescue; END {defined? retry}; end")
assert_parse("#{<<-"begin;"}\n#{<<-'end;'}")
begin;
def foo
begin
yield
rescue StandardError => e
begin
puts "hi"
retry
rescue
retry unless e
raise e
else
retry
ensure
retry
end
end
end
end;
end
def test_invalid_yield