diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2023-09-29 01:58:07 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2023-09-29 03:14:17 +0900 |
commit | eaa0fbf9b956fa25e73c3d55e2eba8887324e233 (patch) | |
tree | 0b07d87c32f8be0cd18401d13c6ee3897aeef948 | |
parent | 5a376f0f71e8ecc8a6cc0b9f35e63a8367275988 (diff) |
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.
-rw-r--r-- | parse.y | 33 | ||||
-rw-r--r-- | test/ruby/test_ast.rb | 21 |
2 files changed, 44 insertions, 10 deletions
@@ -1572,6 +1572,12 @@ 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) { /* See: def_name action */ @@ -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; } ; diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 76fd2b3783..f2c746aafa 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -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 |