From a83736c27d6f59ee0ea025d4cba877dbbc31503f Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Apr 2024 11:02:59 -0400 Subject: [ruby/prism] Log errors for invalid jumps https://github.com/ruby/prism/commit/892d0f9310 --- prism/config.yml | 1 + prism/prism.c | 66 ++++++++++++++++++++++++++++++++++++ prism/templates/src/diagnostic.c.erb | 1 + 3 files changed, 68 insertions(+) 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 @@ -15700,6 +15700,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. */ @@ -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 }, -- cgit v1.2.3