summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTSUYUSATO Kitsune <make.just.on@gmail.com>2023-11-24 01:29:18 +0900
committergit <svn-admin@ruby-lang.org>2023-11-28 17:27:09 +0000
commitb5796d7b113a78b17e27f2ad23c209f4e2d2900b (patch)
tree72305bb09db658e189711bc6878dc8e880d69159
parentfadd28c7ba6b55ec934f379cfc30a8111e340404 (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.h1
-rw-r--r--prism/parser.h7
-rw-r--r--prism/prism.c20
-rw-r--r--test/prism/errors_test.rb16
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")