diff options
| author | Kevin Newton <kddnewton@gmail.com> | 2024-07-23 10:07:05 -0400 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2024-07-23 14:54:01 +0000 |
| commit | 744cf0549ef508e2ba5ebc68749ff02edbd3f032 (patch) | |
| tree | 8c201f783cd57e84f40be9e45c2c110f88de82f4 | |
| parent | f0d8a0a2bfa7026423795f96b40d4d81094f8788 (diff) | |
[ruby/prism] Recurse upward to detect invalid returns
https://github.com/ruby/prism/commit/3d39b7961f
| -rw-r--r-- | prism/prism.c | 87 |
1 files changed, 80 insertions, 7 deletions
diff --git a/prism/prism.c b/prism/prism.c index 55f7eb22c5..1e2f71cf28 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15337,6 +15337,83 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } /** + * Check that the return is allowed in the current context. If it isn't, add an + * error to the parser. + */ +static void +parse_return(pm_parser_t *parser, pm_node_t *node) { + for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { + switch (context_node->context) { + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LOOP_PREDICATE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_PREEXE: + 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_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // Keep iterating up the lists of contexts, because returns can + // see through these. + continue; + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE: + // These contexts are invalid for a return. + pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); + return; + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + // These contexts are valid for a return, and we should not + // continue to loop. + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + } + } +} + +/** * Check that the block exit (next, break, redo) is allowed in the current * context. If it isn't, add an error to the parser. */ @@ -18669,13 +18746,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return node; } case PM_TOKEN_KEYWORD_RETURN: { - if ( - (parser->current_context->context == PM_CONTEXT_CLASS) || - (parser->current_context->context == PM_CONTEXT_MODULE) - ) { - pm_parser_err_previous(parser, PM_ERR_RETURN_INVALID); - } - return (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments); + pm_node_t *node = (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments); + parse_return(parser, node); + return node; } default: assert(false && "unreachable"); |
