@@ -7538,6 +7538,29 @@ pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end
75387538 node->base.location.end = end_keyword->end;
75397539}
75407540
7541+ /**
7542+ * Loop modifiers could potentially modify an expression that contains block
7543+ * exits. In this case we need to loop through them and remove them from the
7544+ * list of block exits so that they do not later get marked as invalid.
7545+ */
7546+ static void
7547+ pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
7548+ assert(parser->current_block_exits != NULL);
7549+
7550+ // All of the block exits that we want to remove should be within the
7551+ // statements, and since we are modifying the statements, we shouldn't have
7552+ // to check the end location.
7553+ const uint8_t *start = statements->base.location.start;
7554+
7555+ for (size_t index = parser->current_block_exits->size; index > 0; index--) {
7556+ pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7557+ if (block_exit->location.start < start) break;
7558+
7559+ // Implicitly remove from the list by lowering the size.
7560+ parser->current_block_exits->size--;
7561+ }
7562+ }
7563+
75417564/**
75427565 * Allocate a new UntilNode node.
75437566 */
@@ -7571,6 +7594,7 @@ static pm_until_node_t *
75717594pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
75727595 pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
75737596 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7597+ pm_loop_modifier_block_exits(parser, statements);
75747598
75757599 *node = (pm_until_node_t) {
75767600 {
@@ -7677,6 +7701,7 @@ static pm_while_node_t *
76777701pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
76787702 pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
76797703 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7704+ pm_loop_modifier_block_exits(parser, statements);
76807705
76817706 *node = (pm_while_node_t) {
76827707 {
@@ -15052,11 +15077,8 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
1505215077 * context. If it isn't, add an error to the parser.
1505315078 */
1505415079static void
15055- parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
15056- pm_context_node_t *context_node = parser->current_context;
15057- bool through_expression = false;
15058-
15059- while (context_node != NULL) {
15080+ parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
15081+ for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
1506015082 switch (context_node->context) {
1506115083 case PM_CONTEXT_BLOCK_BRACES:
1506215084 case PM_CONTEXT_BLOCK_KEYWORDS:
@@ -15090,65 +15112,49 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
1509015112 case PM_CONTEXT_SCLASS_RESCUE:
1509115113 // These are the bad cases. We're not allowed to have a block
1509215114 // exit in these contexts.
15093-
15094- if (through_expression) {
15095- // If we get here, then we're about to mark this block exit
15096- // as invalid. However, it could later _become_ valid if we
15097- // find a trailing while/until on the expression. In this
15098- // case instead of adding the error here, we'll add the
15099- // block exit to the list of exits for the expression, and
15100- // the node parsing will handle validating it instead.
15101- assert(parser->current_block_exits != NULL);
15102- pm_node_list_append(parser->current_block_exits, node);
15103- } else {
15104- // Otherwise, if we haven't gone through an expression
15105- // context, then this is just invalid and we'll add the
15106- // error here.
15107- PM_PARSER_ERR_NODE_FORMAT(parser, node, PM_ERR_INVALID_BLOCK_EXIT, type);
15108- }
15109-
15115+ //
15116+ // If we get here, then we're about to mark this block exit
15117+ // as invalid. However, it could later _become_ valid if we
15118+ // find a trailing while/until on the expression. In this
15119+ // case instead of adding the error here, we'll add the
15120+ // block exit to the list of exits for the expression, and
15121+ // the node parsing will handle validating it instead.
15122+ assert(parser->current_block_exits != NULL);
15123+ pm_node_list_append(parser->current_block_exits, node);
1511015124 return;
15111- case PM_CONTEXT_NONE:
15112- // This case should never happen.
15113- assert(false && "unreachable");
15114- break;
15115- case PM_CONTEXT_BEGIN:
1511615125 case PM_CONTEXT_BEGIN_ELSE:
1511715126 case PM_CONTEXT_BEGIN_ENSURE:
1511815127 case PM_CONTEXT_BEGIN_RESCUE:
15128+ case PM_CONTEXT_BEGIN:
1511915129 case PM_CONTEXT_CASE_IN:
1512015130 case PM_CONTEXT_CASE_WHEN:
15121- case PM_CONTEXT_CLASS:
1512215131 case PM_CONTEXT_CLASS_ELSE:
1512315132 case PM_CONTEXT_CLASS_ENSURE:
1512415133 case PM_CONTEXT_CLASS_RESCUE:
15134+ case PM_CONTEXT_CLASS:
15135+ case PM_CONTEXT_DEFAULT_PARAMS:
1512515136 case PM_CONTEXT_ELSE:
1512615137 case PM_CONTEXT_ELSIF:
15138+ case PM_CONTEXT_EMBEXPR:
15139+ case PM_CONTEXT_FOR_INDEX:
1512715140 case PM_CONTEXT_IF:
15128- case PM_CONTEXT_MODULE:
1512915141 case PM_CONTEXT_MODULE_ELSE:
1513015142 case PM_CONTEXT_MODULE_ENSURE:
1513115143 case PM_CONTEXT_MODULE_RESCUE:
15144+ case PM_CONTEXT_MODULE:
1513215145 case PM_CONTEXT_PARENS:
15146+ case PM_CONTEXT_PREDICATE:
1513315147 case PM_CONTEXT_RESCUE_MODIFIER:
1513415148 case PM_CONTEXT_TERNARY:
1513515149 case PM_CONTEXT_UNLESS:
15136- // If we got to an expression that could be modified by a
15137- // trailing while/until, then we'll track that we have gotten
15138- // here because we need to know it if this block exit is later
15139- // marked as invalid.
15140- through_expression = true;
15141- break;
15142- case PM_CONTEXT_EMBEXPR:
15143- case PM_CONTEXT_DEFAULT_PARAMS:
15144- case PM_CONTEXT_FOR_INDEX:
15145- case PM_CONTEXT_PREDICATE:
1514615150 // In these contexts we should continue walking up the list of
1514715151 // contexts.
1514815152 break;
15153+ case PM_CONTEXT_NONE:
15154+ // This case should never happen.
15155+ assert(false && "unreachable");
15156+ break;
1514915157 }
15150-
15151- context_node = context_node->prev;
1515215158 }
1515315159}
1515415160
@@ -15163,6 +15169,30 @@ push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
1516315169 return previous_block_exits;
1516415170}
1516515171
15172+ /**
15173+ * If we did not match a trailing while/until and this was the last chance to do
15174+ * so, then all of the block exits in the list are invalid and we need to add an
15175+ * error for each of them.
15176+ */
15177+ static void
15178+ flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15179+ pm_node_t *block_exit;
15180+ PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15181+ const char *type;
15182+
15183+ switch (PM_NODE_TYPE(block_exit)) {
15184+ case PM_BREAK_NODE: type = "break"; break;
15185+ case PM_NEXT_NODE: type = "next"; break;
15186+ case PM_REDO_NODE: type = "redo"; break;
15187+ default: assert(false && "unreachable"); type = ""; break;
15188+ }
15189+
15190+ PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15191+ }
15192+
15193+ parser->current_block_exits = previous_block_exits;
15194+ }
15195+
1516615196/**
1516715197 * Pop the current level of block exits from the parser, and add errors to the
1516815198 * parser if any of them are deemed to be invalid.
@@ -15173,33 +15203,21 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
1517315203 // If we matched a trailing while/until, then all of the block exits in
1517415204 // the contained list are valid. In this case we do not need to do
1517515205 // anything.
15206+ parser->current_block_exits = previous_block_exits;
1517615207 } else if (previous_block_exits != NULL) {
1517715208 // If we did not matching a trailing while/until, then all of the block
1517815209 // exits contained in the list are invalid for this specific context.
1517915210 // However, they could still become valid in a higher level context if
1518015211 // there is another list above this one. In this case we'll push all of
1518115212 // the block exits up to the previous list.
1518215213 pm_node_list_concat(previous_block_exits, parser->current_block_exits);
15214+ parser->current_block_exits = previous_block_exits;
1518315215 } else {
1518415216 // If we did not match a trailing while/until and this was the last
1518515217 // chance to do so, then all of the block exits in the list are invalid
1518615218 // and we need to add an error for each of them.
15187- pm_node_t *block_exit;
15188- PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15189- const char *type;
15190-
15191- switch (PM_NODE_TYPE(block_exit)) {
15192- case PM_BREAK_NODE: type = "break"; break;
15193- case PM_NEXT_NODE: type = "next"; break;
15194- case PM_REDO_NODE: type = "redo"; break;
15195- default: assert(false && "unreachable"); type = ""; break;
15196- }
15197-
15198- PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15199- }
15219+ flush_block_exits(parser, previous_block_exits);
1520015220 }
15201-
15202- parser->current_block_exits = previous_block_exits;
1520315221}
1520415222
1520515223static inline pm_node_t *
@@ -18309,6 +18327,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1830918327 return (pm_node_t *) begin_node;
1831018328 }
1831118329 case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
18330+ pm_node_list_t current_block_exits = { 0 };
18331+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
18332+
1831218333 if (binding_power != PM_BINDING_POWER_STATEMENT) {
1831318334 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
1831418335 }
@@ -18325,6 +18346,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1832518346 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
1832618347 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
1832718348 }
18349+
18350+ flush_block_exits(parser, previous_block_exits);
18351+ pm_node_list_free(¤t_block_exits);
18352+
1832818353 return (pm_node_t *) pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
1832918354 }
1833018355 case PM_TOKEN_KEYWORD_BREAK:
@@ -18349,12 +18374,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1834918374 switch (keyword.type) {
1835018375 case PM_TOKEN_KEYWORD_BREAK: {
1835118376 pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
18352- if (!parser->parsing_eval) parse_block_exit(parser, node, "break" );
18377+ if (!parser->parsing_eval) parse_block_exit(parser, node);
1835318378 return node;
1835418379 }
1835518380 case PM_TOKEN_KEYWORD_NEXT: {
1835618381 pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
18357- if (!parser->parsing_eval) parse_block_exit(parser, node, "next" );
18382+ if (!parser->parsing_eval) parse_block_exit(parser, node);
1835818383 return node;
1835918384 }
1836018385 case PM_TOKEN_KEYWORD_RETURN: {
@@ -18415,6 +18440,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1841518440 pm_token_t class_keyword = parser->previous;
1841618441 pm_do_loop_stack_push(parser, false);
1841718442
18443+ pm_node_list_t current_block_exits = { 0 };
18444+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
18445+
1841818446 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
1841918447 pm_token_t operator = parser->previous;
1842018448 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS);
@@ -18442,12 +18470,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1844218470 pm_parser_scope_pop(parser);
1844318471 pm_do_loop_stack_pop(parser);
1844418472
18473+ flush_block_exits(parser, previous_block_exits);
18474+ pm_node_list_free(¤t_block_exits);
18475+
1844518476 return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous);
1844618477 }
1844718478
18448- pm_node_list_t current_block_exits = { 0 };
18449- pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
18450-
1845118479 pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_CLASS_NAME);
1845218480 pm_token_t name = parser->previous;
1845318481 if (name.type != PM_TOKEN_CONSTANT) {
@@ -18512,6 +18540,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1851218540 return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous);
1851318541 }
1851418542 case PM_TOKEN_KEYWORD_DEF: {
18543+ pm_node_list_t current_block_exits = { 0 };
18544+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
18545+
1851518546 pm_token_t def_keyword = parser->current;
1851618547
1851718548 pm_node_t *receiver = NULL;
@@ -18772,6 +18803,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1877218803 */
1877318804 pm_constant_id_t name_id = pm_parser_constant_id_location(parser, name.start, parse_operator_symbol_name(&name));
1877418805
18806+ flush_block_exits(parser, previous_block_exits);
18807+ pm_node_list_free(¤t_block_exits);
18808+
1877518809 return (pm_node_t *) pm_def_node_create(
1877618810 parser,
1877718811 name_id,
@@ -19043,7 +19077,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1904319077 parser_lex(parser);
1904419078
1904519079 pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
19046- if (!parser->parsing_eval) parse_block_exit(parser, node, "redo" );
19080+ if (!parser->parsing_eval) parse_block_exit(parser, node);
1904719081
1904819082 return node;
1904919083 }
@@ -21203,6 +21237,9 @@ parse_program(pm_parser_t *parser) {
2120321237 pm_parser_scope_push(parser, true);
2120421238 }
2120521239
21240+ pm_node_list_t current_block_exits = { 0 };
21241+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
21242+
2120621243 parser_lex(parser);
2120721244 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN);
2120821245
@@ -21231,6 +21268,9 @@ parse_program(pm_parser_t *parser) {
2123121268 // node with a while loop based on the options.
2123221269 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
2123321270 statements = wrap_statements(parser, statements);
21271+ } else {
21272+ flush_block_exits(parser, previous_block_exits);
21273+ pm_node_list_free(¤t_block_exits);
2123421274 }
2123521275
2123621276 return (pm_node_t *) pm_program_node_create(parser, &locals, statements);
0 commit comments