зеркало из https://github.com/github/ruby.git
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:
Родитель
5a376f0f71
Коммит
eaa0fbf9b9
33
parse.y
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче