From 11de89ca1a94899535875ea594962c79713615b1 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 13 May 2026 13:52:14 +0200 Subject: [PATCH] Reject `END { next } ` as well Followup for https://bugs.ruby-lang.org/issues/20409 --- parse.y | 32 ++++--------------- prism/prism.c | 3 -- test/prism/errors/4.1/end_block_exit.txt | 5 +++ .../prism/fixtures/3.3-4.0/end_block_exit.txt | 4 +++ test/prism/fixtures/end_block_exit.txt | 3 -- test/ruby/test_ast.rb | 7 ++-- 6 files changed, 18 insertions(+), 36 deletions(-) delete mode 100644 test/prism/fixtures/end_block_exit.txt diff --git a/parse.y b/parse.y index d163d2a05023fb..ecd26acbbb33fb 100644 --- a/parse.y +++ b/parse.y @@ -1659,22 +1659,7 @@ static NODE *add_block_exit(struct parser_params *p, NODE *node); static rb_node_exits_t *init_block_exit(struct parser_params *p); static rb_node_exits_t *allow_block_exit(struct parser_params *p); static void restore_block_exit(struct parser_params *p, rb_node_exits_t *exits); -static void clear_block_exit(struct parser_params *p, unsigned int error_mask); - -static unsigned int -exits_mask(enum node_type t) -{ - switch (t) { - case NODE_BREAK: - case NODE_NEXT: - case NODE_REDO: - return 1u << (t - NODE_BREAK); - default: - UNREACHABLE_RETURN(0); - } -} - -#define EXITS_MASK_ALL (exits_mask(NODE_BREAK)|exits_mask(NODE_NEXT)|exits_mask(NODE_REDO)) +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) @@ -1692,7 +1677,7 @@ restore_defun(struct parser_params *p, rb_node_def_temp_t *temp) p->ctxt.in_rescue = ctxt.in_rescue; p->max_numparam = temp->save.max_numparam; numparam_pop(p, temp->save.numparam_save); - clear_block_exit(p, EXITS_MASK_ALL); + clear_block_exit(p, true); } static void @@ -1849,23 +1834,20 @@ restore_block_exit(struct parser_params *p, rb_node_exits_t *exits) } static void -clear_block_exit(struct parser_params *p, unsigned int error_mask) +clear_block_exit(struct parser_params *p, bool error) { rb_node_exits_t *exits = p->exits; if (!exits) return; - if (error_mask) { + if (error) { for (NODE *e = RNODE(exits); (e = RNODE_EXITS(e)->nd_chain) != 0; ) { switch (nd_type(e)) { case NODE_BREAK: - if (!(error_mask & exits_mask(NODE_BREAK))) break; yyerror1(&e->nd_loc, "Invalid break"); break; case NODE_NEXT: - if (!(error_mask & exits_mask(NODE_NEXT))) break; yyerror1(&e->nd_loc, "Invalid next"); break; case NODE_REDO: - if (!(error_mask & exits_mask(NODE_REDO))) break; yyerror1(&e->nd_loc, "Invalid redo"); break; default: @@ -3230,7 +3212,7 @@ top_stmts : none top_stmt : stmt { - clear_block_exit(p, EXITS_MASK_ALL); + clear_block_exit(p, true); $$ = $1; } | keyword_BEGIN begin_block @@ -3370,7 +3352,7 @@ stmt : keyword_alias[kw] fitem[new] {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fit } | stmt[body] modifier_while[mod] expr_value[cond_expr] { - clear_block_exit(p, 0); + clear_block_exit(p, false); if ($body && nd_type_p($body, NODE_BEGIN)) { $$ = NEW_WHILE(cond(p, $cond_expr, &@cond_expr), RNODE_BEGIN($body)->nd_body, 0, &@$, &@mod, &NULL_LOC); } @@ -3401,7 +3383,7 @@ stmt : keyword_alias[kw] fitem[new] {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fit } | k_END[k_end] block_open[lbrace] compstmt(stmts)[body] '}'[rbrace] { - clear_block_exit(p, exits_mask(NODE_BREAK) | exits_mask(NODE_REDO)); + clear_block_exit(p, true); restore_block_exit(p, $block_open); p->ctxt = $k_end; { diff --git a/prism/prism.c b/prism/prism.c index 800d1eb1058b3c..df8d0d7f831da3 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15332,9 +15332,6 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) { if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) { return; } - if (PM_NODE_TYPE_P(node, PM_NEXT_NODE)) { - return; - } } PRISM_FALLTHROUGH case PM_CONTEXT_DEF: diff --git a/test/prism/errors/4.1/end_block_exit.txt b/test/prism/errors/4.1/end_block_exit.txt index f8fc59d1a37fba..a4a1e9bc2cacca 100644 --- a/test/prism/errors/4.1/end_block_exit.txt +++ b/test/prism/errors/4.1/end_block_exit.txt @@ -3,3 +3,8 @@ END { ^~~~~ Invalid break } +END { + next + ^~~~ Invalid next +} + diff --git a/test/prism/fixtures/3.3-4.0/end_block_exit.txt b/test/prism/fixtures/3.3-4.0/end_block_exit.txt index 53afa1e2f4fad4..8ebf0d6369dc47 100644 --- a/test/prism/fixtures/3.3-4.0/end_block_exit.txt +++ b/test/prism/fixtures/3.3-4.0/end_block_exit.txt @@ -5,3 +5,7 @@ END { END { break } + +END { + next +} diff --git a/test/prism/fixtures/end_block_exit.txt b/test/prism/fixtures/end_block_exit.txt deleted file mode 100644 index 1f64dcb6ed5115..00000000000000 --- a/test/prism/fixtures/end_block_exit.txt +++ /dev/null @@ -1,3 +0,0 @@ -END { - next -} diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index a59ce277d1c568..8b9a3f615db50c 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -255,11 +255,8 @@ def test_invalid_exit assert_invalid_parse(msg, "#{code}") assert_invalid_parse(msg, "def m; #{code}; end") assert_invalid_parse(msg, "begin; #{code}; end") - if code.start_with?("next") - assert_parse("END {#{code}}") - else - assert_invalid_parse(msg, "END {#{code}}") - end + assert_invalid_parse(msg, "BEGIN {#{code}}") + assert_invalid_parse(msg, "END {#{code}}") assert_parse("!defined?(#{code})") assert_parse("def m; defined?(#{code}); end")