summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--prism/config.yml1
-rw-r--r--prism/prism.c66
-rw-r--r--prism/templates/src/diagnostic.c.erb1
3 files changed, 68 insertions, 0 deletions
diff --git a/prism/config.yml b/prism/config.yml
index c813b02d9f..510b8de045 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -122,6 +122,7 @@ errors:
- INCOMPLETE_VARIABLE_INSTANCE
- INCOMPLETE_VARIABLE_INSTANCE_3_3_0
- INSTANCE_VARIABLE_BARE
+ - INVALID_BLOCK_EXIT
- INVALID_CHARACTER
- INVALID_ENCODING_MAGIC_COMMENT
- INVALID_FLOAT_EXPONENT
diff --git a/prism/prism.c b/prism/prism.c
index b6a00cddf9..8f66c8298c 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -15701,6 +15701,69 @@ pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
}
/**
+ * Check that the block exit (next, break, redo) is allowed in the current
+ * context. If it isn't, add an error to the parser.
+ */
+static void
+parse_block_exit(pm_parser_t *parser, pm_token_t *token) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_POSTEXE:
+ // These are the good cases. We're allowed to have a block exit
+ // in these contexts.
+ return;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_SCLASS:
+ // These are the bad cases. We're not allowed to have a block
+ // exit in these contexts.
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, *token, PM_ERR_INVALID_BLOCK_EXIT);
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_ENSURE:
+ case PM_CONTEXT_ENSURE_DEF:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_PREDICATE:
+ case PM_CONTEXT_RESCUE_ELSE:
+ case PM_CONTEXT_RESCUE_ELSE_DEF:
+ case PM_CONTEXT_RESCUE:
+ case PM_CONTEXT_RESCUE_DEF:
+ case PM_CONTEXT_UNLESS:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
* Parse an expression that begins with the previous node that we just lexed.
*/
static inline pm_node_t *
@@ -16617,8 +16680,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
switch (keyword.type) {
case PM_TOKEN_KEYWORD_BREAK:
+ parse_block_exit(parser, &keyword);
return (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
case PM_TOKEN_KEYWORD_NEXT:
+ parse_block_exit(parser, &keyword);
return (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
case PM_TOKEN_KEYWORD_RETURN: {
if (
@@ -17286,6 +17351,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
parser_lex(parser);
return (pm_node_t *) pm_nil_node_create(parser, &parser->previous);
case PM_TOKEN_KEYWORD_REDO:
+ parse_block_exit(parser, &parser->current);
parser_lex(parser);
return (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
case PM_TOKEN_KEYWORD_RETRY:
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
index 316e07bdee..3b674b5b3a 100644
--- a/prism/templates/src/diagnostic.c.erb
+++ b/prism/templates/src/diagnostic.c.erb
@@ -206,6 +206,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %.*s", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_SYNTAX },