@@ -7538,6 +7538,29 @@ pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end
7538
7538
node->base.location.end = end_keyword->end;
7539
7539
}
7540
7540
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
+
7541
7564
/**
7542
7565
* Allocate a new UntilNode node.
7543
7566
*/
@@ -7571,6 +7594,7 @@ static pm_until_node_t *
7571
7594
pm_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) {
7572
7595
pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
7573
7596
pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7597
+ pm_loop_modifier_block_exits(parser, statements);
7574
7598
7575
7599
*node = (pm_until_node_t) {
7576
7600
{
@@ -7677,6 +7701,7 @@ static pm_while_node_t *
7677
7701
pm_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) {
7678
7702
pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
7679
7703
pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7704
+ pm_loop_modifier_block_exits(parser, statements);
7680
7705
7681
7706
*node = (pm_while_node_t) {
7682
7707
{
@@ -15052,11 +15077,8 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
15052
15077
* context. If it isn't, add an error to the parser.
15053
15078
*/
15054
15079
static 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) {
15060
15082
switch (context_node->context) {
15061
15083
case PM_CONTEXT_BLOCK_BRACES:
15062
15084
case PM_CONTEXT_BLOCK_KEYWORDS:
@@ -15090,65 +15112,49 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
15090
15112
case PM_CONTEXT_SCLASS_RESCUE:
15091
15113
// These are the bad cases. We're not allowed to have a block
15092
15114
// 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);
15110
15124
return;
15111
- case PM_CONTEXT_NONE:
15112
- // This case should never happen.
15113
- assert(false && "unreachable");
15114
- break;
15115
- case PM_CONTEXT_BEGIN:
15116
15125
case PM_CONTEXT_BEGIN_ELSE:
15117
15126
case PM_CONTEXT_BEGIN_ENSURE:
15118
15127
case PM_CONTEXT_BEGIN_RESCUE:
15128
+ case PM_CONTEXT_BEGIN:
15119
15129
case PM_CONTEXT_CASE_IN:
15120
15130
case PM_CONTEXT_CASE_WHEN:
15121
- case PM_CONTEXT_CLASS:
15122
15131
case PM_CONTEXT_CLASS_ELSE:
15123
15132
case PM_CONTEXT_CLASS_ENSURE:
15124
15133
case PM_CONTEXT_CLASS_RESCUE:
15134
+ case PM_CONTEXT_CLASS:
15135
+ case PM_CONTEXT_DEFAULT_PARAMS:
15125
15136
case PM_CONTEXT_ELSE:
15126
15137
case PM_CONTEXT_ELSIF:
15138
+ case PM_CONTEXT_EMBEXPR:
15139
+ case PM_CONTEXT_FOR_INDEX:
15127
15140
case PM_CONTEXT_IF:
15128
- case PM_CONTEXT_MODULE:
15129
15141
case PM_CONTEXT_MODULE_ELSE:
15130
15142
case PM_CONTEXT_MODULE_ENSURE:
15131
15143
case PM_CONTEXT_MODULE_RESCUE:
15144
+ case PM_CONTEXT_MODULE:
15132
15145
case PM_CONTEXT_PARENS:
15146
+ case PM_CONTEXT_PREDICATE:
15133
15147
case PM_CONTEXT_RESCUE_MODIFIER:
15134
15148
case PM_CONTEXT_TERNARY:
15135
15149
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:
15146
15150
// In these contexts we should continue walking up the list of
15147
15151
// contexts.
15148
15152
break;
15153
+ case PM_CONTEXT_NONE:
15154
+ // This case should never happen.
15155
+ assert(false && "unreachable");
15156
+ break;
15149
15157
}
15150
-
15151
- context_node = context_node->prev;
15152
15158
}
15153
15159
}
15154
15160
@@ -15163,6 +15169,30 @@ push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
15163
15169
return previous_block_exits;
15164
15170
}
15165
15171
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
+
15166
15196
/**
15167
15197
* Pop the current level of block exits from the parser, and add errors to the
15168
15198
* 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) {
15173
15203
// If we matched a trailing while/until, then all of the block exits in
15174
15204
// the contained list are valid. In this case we do not need to do
15175
15205
// anything.
15206
+ parser->current_block_exits = previous_block_exits;
15176
15207
} else if (previous_block_exits != NULL) {
15177
15208
// If we did not matching a trailing while/until, then all of the block
15178
15209
// exits contained in the list are invalid for this specific context.
15179
15210
// However, they could still become valid in a higher level context if
15180
15211
// there is another list above this one. In this case we'll push all of
15181
15212
// the block exits up to the previous list.
15182
15213
pm_node_list_concat(previous_block_exits, parser->current_block_exits);
15214
+ parser->current_block_exits = previous_block_exits;
15183
15215
} else {
15184
15216
// If we did not match a trailing while/until and this was the last
15185
15217
// chance to do so, then all of the block exits in the list are invalid
15186
15218
// 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);
15200
15220
}
15201
-
15202
- parser->current_block_exits = previous_block_exits;
15203
15221
}
15204
15222
15205
15223
static inline pm_node_t *
@@ -18309,6 +18327,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18309
18327
return (pm_node_t *) begin_node;
18310
18328
}
18311
18329
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
+
18312
18333
if (binding_power != PM_BINDING_POWER_STATEMENT) {
18313
18334
pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
18314
18335
}
@@ -18325,6 +18346,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18325
18346
if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
18326
18347
pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
18327
18348
}
18349
+
18350
+ flush_block_exits(parser, previous_block_exits);
18351
+ pm_node_list_free(¤t_block_exits);
18352
+
18328
18353
return (pm_node_t *) pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
18329
18354
}
18330
18355
case PM_TOKEN_KEYWORD_BREAK:
@@ -18349,12 +18374,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18349
18374
switch (keyword.type) {
18350
18375
case PM_TOKEN_KEYWORD_BREAK: {
18351
18376
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);
18353
18378
return node;
18354
18379
}
18355
18380
case PM_TOKEN_KEYWORD_NEXT: {
18356
18381
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);
18358
18383
return node;
18359
18384
}
18360
18385
case PM_TOKEN_KEYWORD_RETURN: {
@@ -18415,6 +18440,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18415
18440
pm_token_t class_keyword = parser->previous;
18416
18441
pm_do_loop_stack_push(parser, false);
18417
18442
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
+
18418
18446
if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18419
18447
pm_token_t operator = parser->previous;
18420
18448
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
18442
18470
pm_parser_scope_pop(parser);
18443
18471
pm_do_loop_stack_pop(parser);
18444
18472
18473
+ flush_block_exits(parser, previous_block_exits);
18474
+ pm_node_list_free(¤t_block_exits);
18475
+
18445
18476
return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous);
18446
18477
}
18447
18478
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
-
18451
18479
pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_CLASS_NAME);
18452
18480
pm_token_t name = parser->previous;
18453
18481
if (name.type != PM_TOKEN_CONSTANT) {
@@ -18512,6 +18540,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18512
18540
return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous);
18513
18541
}
18514
18542
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
+
18515
18546
pm_token_t def_keyword = parser->current;
18516
18547
18517
18548
pm_node_t *receiver = NULL;
@@ -18772,6 +18803,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18772
18803
*/
18773
18804
pm_constant_id_t name_id = pm_parser_constant_id_location(parser, name.start, parse_operator_symbol_name(&name));
18774
18805
18806
+ flush_block_exits(parser, previous_block_exits);
18807
+ pm_node_list_free(¤t_block_exits);
18808
+
18775
18809
return (pm_node_t *) pm_def_node_create(
18776
18810
parser,
18777
18811
name_id,
@@ -19043,7 +19077,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19043
19077
parser_lex(parser);
19044
19078
19045
19079
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);
19047
19081
19048
19082
return node;
19049
19083
}
@@ -21203,6 +21237,9 @@ parse_program(pm_parser_t *parser) {
21203
21237
pm_parser_scope_push(parser, true);
21204
21238
}
21205
21239
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
+
21206
21243
parser_lex(parser);
21207
21244
pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN);
21208
21245
@@ -21231,6 +21268,9 @@ parse_program(pm_parser_t *parser) {
21231
21268
// node with a while loop based on the options.
21232
21269
if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
21233
21270
statements = wrap_statements(parser, statements);
21271
+ } else {
21272
+ flush_block_exits(parser, previous_block_exits);
21273
+ pm_node_list_free(¤t_block_exits);
21234
21274
}
21235
21275
21236
21276
return (pm_node_t *) pm_program_node_create(parser, &locals, statements);
0 commit comments