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:
Jeremy Evans 2024-09-18 16:54:56 -07:00 коммит произвёл GitHub
Родитель 984a791d58
Коммит 268c72377b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 14 добавлений и 8 удалений

Просмотреть файл

@ -217,7 +217,7 @@ assert_equal %q{[10, main]}, %q{
}
%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
begin
eval "0 rescue #{keyword}"

Просмотреть файл

@ -1836,7 +1836,7 @@ clear_block_exit(struct parser_params *p, bool error)
{
rb_node_exits_t *exits = p->exits;
if (!exits) return;
if (error && !compile_for_eval) {
if (error) {
for (NODE *e = RNODE(exits); (e = RNODE_EXITS(e)->nd_chain) != 0; ) {
switch (nd_type(e)) {
case NODE_BREAK:

Просмотреть файл

@ -18852,12 +18852,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
switch (keyword.type) {
case PM_TOKEN_KEYWORD_BREAK: {
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;
}
case PM_TOKEN_KEYWORD_NEXT: {
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;
}
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);
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;
}

Просмотреть файл

@ -7275,7 +7275,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
throw_flag = 0;
}
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;
}
else {
@ -9047,7 +9047,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
break;
}
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;
}
@ -9300,7 +9300,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
break;
}
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;
}

Просмотреть файл

@ -535,6 +535,12 @@ class TestEval < Test::Unit::TestCase
assert_equal(fname, eval("__FILE__", nil, fname, 1))
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
o = Object.new
o.instance_eval "def foo() end", "generated code"