Raise a compile error for break/next/redo inside eval in cases where it is optimized away
In cases where break/next/redo are not valid syntax, they should raise a SyntaxError even if inside a conditional block that is optimized away. Fixes [Bug #20597] Co-authored-by: Kevin Newton <kddnewton@gmail.com>
This commit is contained in:
parent
984a791d58
commit
268c72377b
Notes:
git
2024-09-18 23:55:14 +00:00
Merged: https://github.com/ruby/ruby/pull/11099 Merged-By: jeremyevans <code@jeremyevans.net>
@ -217,7 +217,7 @@ assert_equal %q{[10, main]}, %q{
|
|||||||
}
|
}
|
||||||
|
|
||||||
%w[break next redo].each do |keyword|
|
%w[break next redo].each do |keyword|
|
||||||
assert_match %r"Can't escape from eval with #{keyword}\b", %{
|
assert_match %r"Invalid #{keyword}\b", %{
|
||||||
$stderr = STDOUT
|
$stderr = STDOUT
|
||||||
begin
|
begin
|
||||||
eval "0 rescue #{keyword}"
|
eval "0 rescue #{keyword}"
|
||||||
|
2
parse.y
2
parse.y
@ -1836,7 +1836,7 @@ clear_block_exit(struct parser_params *p, bool error)
|
|||||||
{
|
{
|
||||||
rb_node_exits_t *exits = p->exits;
|
rb_node_exits_t *exits = p->exits;
|
||||||
if (!exits) return;
|
if (!exits) return;
|
||||||
if (error && !compile_for_eval) {
|
if (error) {
|
||||||
for (NODE *e = RNODE(exits); (e = RNODE_EXITS(e)->nd_chain) != 0; ) {
|
for (NODE *e = RNODE(exits); (e = RNODE_EXITS(e)->nd_chain) != 0; ) {
|
||||||
switch (nd_type(e)) {
|
switch (nd_type(e)) {
|
||||||
case NODE_BREAK:
|
case NODE_BREAK:
|
||||||
|
@ -18852,12 +18852,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|||||||
switch (keyword.type) {
|
switch (keyword.type) {
|
||||||
case PM_TOKEN_KEYWORD_BREAK: {
|
case PM_TOKEN_KEYWORD_BREAK: {
|
||||||
pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
|
pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
|
||||||
if (!parser->parsing_eval) parse_block_exit(parser, node);
|
parse_block_exit(parser, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
case PM_TOKEN_KEYWORD_NEXT: {
|
case PM_TOKEN_KEYWORD_NEXT: {
|
||||||
pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
|
pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
|
||||||
if (!parser->parsing_eval) parse_block_exit(parser, node);
|
parse_block_exit(parser, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
case PM_TOKEN_KEYWORD_RETURN: {
|
case PM_TOKEN_KEYWORD_RETURN: {
|
||||||
@ -19574,7 +19574,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|||||||
parser_lex(parser);
|
parser_lex(parser);
|
||||||
|
|
||||||
pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
|
pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
|
||||||
if (!parser->parsing_eval) parse_block_exit(parser, node);
|
parse_block_exit(parser, node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -7275,7 +7275,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
|
|||||||
throw_flag = 0;
|
throw_flag = 0;
|
||||||
}
|
}
|
||||||
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
||||||
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with break");
|
COMPILE_ERROR(iseq, location.line, "Invalid break");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -9047,7 +9047,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
||||||
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with next");
|
COMPILE_ERROR(iseq, location.line, "Invalid next");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9300,7 +9300,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
|
||||||
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with redo");
|
COMPILE_ERROR(iseq, location.line, "Invalid redo");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +535,12 @@ class TestEval < Test::Unit::TestCase
|
|||||||
assert_equal(fname, eval("__FILE__", nil, fname, 1))
|
assert_equal(fname, eval("__FILE__", nil, fname, 1))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_eval_invalid_block_exit_bug_20597
|
||||||
|
assert_raise(SyntaxError){eval("break if false")}
|
||||||
|
assert_raise(SyntaxError){eval("next if false")}
|
||||||
|
assert_raise(SyntaxError){eval("redo if false")}
|
||||||
|
end
|
||||||
|
|
||||||
def test_eval_location_fstring
|
def test_eval_location_fstring
|
||||||
o = Object.new
|
o = Object.new
|
||||||
o.instance_eval "def foo() end", "generated code"
|
o.instance_eval "def foo() end", "generated code"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user