[Bug #20647] Disallow return directly within a singleton class

This commit is contained in:
Nobuyoshi Nakada 2024-07-24 11:15:25 +09:00
parent f2f9d6ce49
commit e642ddf7ae
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2024-07-24 06:32:02 +00:00
4 changed files with 25 additions and 5 deletions

12
parse.y
View File

@ -318,6 +318,7 @@ struct lex_context {
unsigned int in_class: 1; unsigned int in_class: 1;
BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2); BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2);
BITFIELD(enum rescue_context, in_rescue, 2); BITFIELD(enum rescue_context, in_rescue, 2);
unsigned int cant_return: 1;
}; };
typedef struct RNode_DEF_TEMP rb_node_def_temp_t; typedef struct RNode_DEF_TEMP rb_node_def_temp_t;
@ -1696,12 +1697,17 @@ endless_method_name(struct parser_params *p, ID mid, const YYLTYPE *loc)
#define begin_definition(k, loc_beg, loc_end) \ #define begin_definition(k, loc_beg, loc_end) \
do { \ do { \
if (!(p->ctxt.in_class = (k)[0] != 0)) { \ if (!(p->ctxt.in_class = (k)[0] != 0)) { \
/* singleton class */ \
p->ctxt.cant_return = !p->ctxt.in_def; \
p->ctxt.in_def = 0; \ p->ctxt.in_def = 0; \
} \ } \
else if (p->ctxt.in_def) { \ else if (p->ctxt.in_def) { \
YYLTYPE loc = code_loc_gen(loc_beg, loc_end); \ YYLTYPE loc = code_loc_gen(loc_beg, loc_end); \
yyerror1(&loc, k " definition in method body"); \ yyerror1(&loc, k " definition in method body"); \
} \ } \
else { \
p->ctxt.cant_return = 1; \
} \
local_push(p, 0); \ local_push(p, 0); \
} while (0) } while (0)
@ -3400,6 +3406,7 @@ def_name : fname
local_push(p, 0); local_push(p, 0);
p->ctxt.in_def = 1; p->ctxt.in_def = 1;
p->ctxt.in_rescue = before_rescue; p->ctxt.in_rescue = before_rescue;
p->ctxt.cant_return = 0;
$$ = $1; $$ = $1;
} }
; ;
@ -4628,6 +4635,7 @@ primary : literal
/*% ripper: class!($:cpath, $:superclass, $:bodystmt) %*/ /*% ripper: class!($:cpath, $:superclass, $:bodystmt) %*/
local_pop(p); local_pop(p);
p->ctxt.in_class = $k_class.in_class; p->ctxt.in_class = $k_class.in_class;
p->ctxt.cant_return = $k_class.cant_return;
p->ctxt.shareable_constant_value = $k_class.shareable_constant_value; p->ctxt.shareable_constant_value = $k_class.shareable_constant_value;
} }
| k_class tLSHFT expr_value | k_class tLSHFT expr_value
@ -4646,6 +4654,7 @@ primary : literal
local_pop(p); local_pop(p);
p->ctxt.in_def = $k_class.in_def; p->ctxt.in_def = $k_class.in_def;
p->ctxt.in_class = $k_class.in_class; p->ctxt.in_class = $k_class.in_class;
p->ctxt.cant_return = $k_class.cant_return;
p->ctxt.shareable_constant_value = $k_class.shareable_constant_value; p->ctxt.shareable_constant_value = $k_class.shareable_constant_value;
} }
| k_module cpath | k_module cpath
@ -4662,6 +4671,7 @@ primary : literal
/*% ripper: module!($:cpath, $:bodystmt) %*/ /*% ripper: module!($:cpath, $:bodystmt) %*/
local_pop(p); local_pop(p);
p->ctxt.in_class = $k_module.in_class; p->ctxt.in_class = $k_module.in_class;
p->ctxt.cant_return = $k_module.cant_return;
p->ctxt.shareable_constant_value = $k_module.shareable_constant_value; p->ctxt.shareable_constant_value = $k_module.shareable_constant_value;
} }
| defn_head[head] | defn_head[head]
@ -4890,7 +4900,7 @@ k_end : keyword_end
k_return : keyword_return k_return : keyword_return
{ {
if (p->ctxt.in_class && !p->ctxt.in_def && !dyna_in_block(p)) if (p->ctxt.cant_return && !dyna_in_block(p))
yyerror1(&@1, "Invalid return in class/module body"); yyerror1(&@1, "Invalid return in class/module body");
} }
; ;

View File

@ -15342,6 +15342,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
*/ */
static void static void
parse_return(pm_parser_t *parser, pm_node_t *node) { parse_return(pm_parser_t *parser, pm_node_t *node) {
bool in_sclass = false;
for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
switch (context_node->context) { switch (context_node->context) {
case PM_CONTEXT_BEGIN_ELSE: case PM_CONTEXT_BEGIN_ELSE:
@ -15366,10 +15367,6 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
case PM_CONTEXT_PREDICATE: case PM_CONTEXT_PREDICATE:
case PM_CONTEXT_PREEXE: case PM_CONTEXT_PREEXE:
case PM_CONTEXT_RESCUE_MODIFIER: case PM_CONTEXT_RESCUE_MODIFIER:
case PM_CONTEXT_SCLASS_ELSE:
case PM_CONTEXT_SCLASS_ENSURE:
case PM_CONTEXT_SCLASS_RESCUE:
case PM_CONTEXT_SCLASS:
case PM_CONTEXT_TERNARY: case PM_CONTEXT_TERNARY:
case PM_CONTEXT_UNLESS: case PM_CONTEXT_UNLESS:
case PM_CONTEXT_UNTIL: case PM_CONTEXT_UNTIL:
@ -15377,6 +15374,12 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
// Keep iterating up the lists of contexts, because returns can // Keep iterating up the lists of contexts, because returns can
// see through these. // see through these.
continue; continue;
case PM_CONTEXT_SCLASS_ELSE:
case PM_CONTEXT_SCLASS_ENSURE:
case PM_CONTEXT_SCLASS_RESCUE:
case PM_CONTEXT_SCLASS:
in_sclass = true;
continue;
case PM_CONTEXT_CLASS_ELSE: case PM_CONTEXT_CLASS_ELSE:
case PM_CONTEXT_CLASS_ENSURE: case PM_CONTEXT_CLASS_ENSURE:
case PM_CONTEXT_CLASS_RESCUE: case PM_CONTEXT_CLASS_RESCUE:
@ -15411,6 +15414,9 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
break; break;
} }
} }
if (in_sclass) {
pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
}
} }
/** /**

View File

@ -0,0 +1,3 @@
class << A; return; end
^~~~~~ Invalid return in class/module body

View File

@ -316,6 +316,7 @@ class TestClass < Test::Unit::TestCase
def test_invalid_return_from_class_definition def test_invalid_return_from_class_definition
assert_syntax_error("class C; return; end", /Invalid return/) assert_syntax_error("class C; return; end", /Invalid return/)
assert_syntax_error("class << Object; return; end", /Invalid return/)
end end
def test_invalid_yield_from_class_definition def test_invalid_yield_from_class_definition