summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2023-09-29 01:58:07 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2023-09-29 03:14:17 +0900
commiteaa0fbf9b956fa25e73c3d55e2eba8887324e233 (patch)
tree0b07d87c32f8be0cd18401d13c6ee3897aeef948
parent5a376f0f71e8ecc8a6cc0b9f35e63a8367275988 (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.y33
-rw-r--r--test/ruby/test_ast.rb21
2 files changed, 44 insertions, 10 deletions
diff --git a/parse.y b/parse.y
index e9ebfebf61..b572ab488a 100644
--- a/parse.y
+++ b/parse.y
@@ -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