diff options
| author | TSUYUSATO Kitsune <make.just.on@gmail.com> | 2023-11-24 01:29:18 +0900 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2023-11-28 17:27:09 +0000 |
| commit | b5796d7b113a78b17e27f2ad23c209f4e2d2900b (patch) | |
| tree | 72305bb09db658e189711bc6878dc8e880d69159 | |
| parent | fadd28c7ba6b55ec934f379cfc30a8111e340404 (diff) | |
[ruby/prism] Check circular references in default values of params
Fix https://github.com/ruby/prism/pull/1637
https://github.com/ruby/prism/commit/0172d69cba
| -rw-r--r-- | prism/diagnostic.h | 1 | ||||
| -rw-r--r-- | prism/parser.h | 7 | ||||
| -rw-r--r-- | prism/prism.c | 20 | ||||
| -rw-r--r-- | test/prism/errors_test.rb | 16 |
4 files changed, 43 insertions, 1 deletions
diff --git a/prism/diagnostic.h b/prism/diagnostic.h index 06d90ec026..3614a38ae7 100644 --- a/prism/diagnostic.h +++ b/prism/diagnostic.h @@ -192,6 +192,7 @@ typedef enum { PM_ERR_OPERATOR_WRITE_BLOCK, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI, PM_ERR_PARAMETER_BLOCK_MULTI, + PM_ERR_PARAMETER_CIRCULAR, PM_ERR_PARAMETER_METHOD_NAME, PM_ERR_PARAMETER_NAME_REPEAT, PM_ERR_PARAMETER_NO_DEFAULT, diff --git a/prism/parser.h b/prism/parser.h index 86442d2a22..89963ab08a 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -676,6 +676,13 @@ struct pm_parser { bool in_keyword_arg; /** + * The current parameter name id on parsing its default value. + * Since this is used for detecting circular references, this is available + * only on `PM_CONTEXT_DEFAULT_PARAM`. + */ + pm_constant_id_t current_param_name; + + /** * Whether or not the parser has seen a token that has semantic meaning * (i.e., a token that is not a comment or whitespace). */ diff --git a/prism/prism.c b/prism/prism.c index 1720d9089d..d0cfbef41d 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -4040,11 +4040,22 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c return node; } +static bool context_p(pm_parser_t *parser, pm_context_t context); + /** * Allocate a new LocalVariableReadNode node. */ static pm_local_variable_read_node_t * pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name); + + if ( + context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && + parser->current_param_name == name_id + ) { + pm_parser_err_token(parser, name, PM_ERR_PARAMETER_CIRCULAR); + } + pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t); *node = (pm_local_variable_read_node_t) { @@ -4052,7 +4063,7 @@ pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, .type = PM_LOCAL_VARIABLE_READ_NODE, .location = PM_LOCATION_TOKEN_VALUE(name) }, - .name = pm_parser_constant_id_token(parser, name), + .name = name_id, .depth = depth }; @@ -11596,10 +11607,14 @@ parse_parameters( if (accept1(parser, PM_TOKEN_EQUAL)) { pm_token_t operator = parser->previous; context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = pm_parser_constant_id_token(parser, &name); pm_node_t *value = parse_value_expression(parser, binding_power, PM_ERR_PARAMETER_NO_DEFAULT); pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); pm_parameters_node_optionals_append(params, param); + + parser->current_param_name = old_param_name; context_pop(parser); // If parsing the value of the parameter resulted in error recovery, @@ -11655,7 +11670,10 @@ parse_parameters( if (token_begins_expression_p(parser->current.type)) { context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + pm_constant_id_t old_param_name = parser->current_param_name; + parser->current_param_name = pm_parser_constant_id_token(parser, &local); pm_node_t *value = parse_value_expression(parser, binding_power, PM_ERR_PARAMETER_NO_DEFAULT_KW); + parser->current_param_name = old_param_name; context_pop(parser); param = (pm_node_t *) pm_optional_keyword_parameter_node_create(parser, &name, value); } diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index bcc9629622..9c96725463 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -1829,6 +1829,22 @@ module Prism ] end + def test_circular_param + source = <<~RUBY + def foo(bar = bar) = 42 + def foo(bar: bar) = 42 + proc { |foo = foo| } + proc { |foo: foo| } + RUBY + message = 'Invalid circular reference in a default parameter' + assert_errors expression(source), source, [ + [message, 14..17], + [message, 37..40], + [message, 61..64], + [message, 81..84], + ], compare_ripper: false # Ripper does not check 'circular reference'. + end + private def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby") |
