summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <[email protected]>2024-05-21 09:55:31 -0400
committergit <[email protected]>2024-05-21 15:46:45 +0000
commitff43b4a28b137af6435603eccb57d6cae85d210b (patch)
tree7c834b505316d02ea394ea2a1a6146e8e844a0c2
parent8277cf0799145bed0c813bfcc187739f77b3102d (diff)
[ruby/prism] Add error for numbered parameter used in inner block
https://github.com/ruby/prism/commit/c386ba6d48
-rw-r--r--prism/config.yml4
-rw-r--r--prism/parser.h1
-rw-r--r--prism/prism.c37
-rw-r--r--prism/templates/src/diagnostic.c.erb4
-rw-r--r--test/prism/errors_test.rb2
5 files changed, 38 insertions, 10 deletions
diff --git a/prism/config.yml b/prism/config.yml
index 6b4b1aa9c0..cfcfd2e7aa 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -114,6 +114,7 @@ errors:
- EXPRESSION_NOT_WRITABLE_FILE
- EXPRESSION_NOT_WRITABLE_LINE
- EXPRESSION_NOT_WRITABLE_NIL
+ - EXPRESSION_NOT_WRITABLE_NUMBERED
- EXPRESSION_NOT_WRITABLE_SELF
- EXPRESSION_NOT_WRITABLE_TRUE
- FLOAT_PARSE
@@ -185,9 +186,10 @@ errors:
- NO_LOCAL_VARIABLE
- NOT_EXPRESSION
- NUMBER_LITERAL_UNDERSCORE
+ - NUMBERED_PARAMETER_INNER_BLOCK
- NUMBERED_PARAMETER_IT
- NUMBERED_PARAMETER_ORDINARY
- - NUMBERED_PARAMETER_OUTER_SCOPE
+ - NUMBERED_PARAMETER_OUTER_BLOCK
- OPERATOR_MULTI_ASSIGN
- OPERATOR_WRITE_ARGUMENTS
- OPERATOR_WRITE_BLOCK
diff --git a/prism/parser.h b/prism/parser.h
index 8054e332f7..ff1261841c 100644
--- a/prism/parser.h
+++ b/prism/parser.h
@@ -609,6 +609,7 @@ static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS = 0x10;
static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_BLOCK = 0x20;
static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x40;
+static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_INNER = -2;
static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED = -1;
static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_NONE = 0;
diff --git a/prism/prism.c b/prism/prism.c
index cf8ce7c830..e146799393 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -13365,14 +13365,20 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
return (pm_node_t *) node;
}
case PM_LOCAL_VARIABLE_READ_NODE: {
- pm_refute_numbered_parameter(parser, target->location.start, target->location.end);
pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target;
pm_constant_id_t name = local_read->name;
+ pm_location_t name_loc = target->location;
+
uint32_t depth = local_read->depth;
- pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
+ pm_scope_t *scope = pm_parser_scope_find(parser, depth);
- pm_location_t name_loc = target->location;
+ if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) {
+ pm_diagnostic_id_t diag_id = scope->parameters > PM_SCOPE_NUMBERED_PARAMETERS_NONE ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
+ PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, diag_id, target->location.start);
+ }
+
+ pm_locals_unread(&scope->locals, name);
pm_node_destroy(parser, target);
return (pm_node_t *) pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator);
@@ -15829,17 +15835,34 @@ parse_variable(pm_parser_t *parser) {
} else if (current_scope->parameters & PM_SCOPE_PARAMETERS_IT) {
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_IT);
} else if (outer_scope_using_numbered_parameters_p(parser)) {
- pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
+ pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
+ } else if (current_scope->numbered_parameters == PM_SCOPE_NUMBERED_PARAMETERS_INNER) {
+ pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
} else {
// Indicate that this scope is using numbered params so that child
// scopes cannot. We subtract the value for the character '0' to get
// the actual integer value of the number (only _1 through _9 are
// valid).
int8_t numbered_parameters = (int8_t) (parser->previous.start[1] - '0');
- current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED;
- if (numbered_parameters > current_scope->numbered_parameters) {
- current_scope->numbered_parameters = numbered_parameters;
+ // If we're about to match an =, then this is an invalid use of
+ // numbered parameters. We'll create all of the necessary
+ // infrastructure around it, but not actually mark the scope as
+ // using numbered parameters so that we can get the right error
+ // message.
+ if (!match1(parser, PM_TOKEN_EQUAL)) {
+ current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED;
+
+ if (numbered_parameters > current_scope->numbered_parameters) {
+ current_scope->numbered_parameters = numbered_parameters;
+ }
+
+ // Go through the parent scopes and mark them as being
+ // disallowed from using numbered parameters because this inner
+ // scope is using them.
+ for (pm_scope_t *scope = current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
+ scope->numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_INNER;
+ }
}
// When you use a numbered parameter, it implies the existence
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
index 57c52602f4..30d6b0ffed 100644
--- a/prism/templates/src/diagnostic.c.erb
+++ b/prism/templates/src/diagnostic.c.erb
@@ -197,6 +197,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_EXPRESSION_NOT_WRITABLE_FILE] = { "Can't assign to __FILE__", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_EXPRESSION_NOT_WRITABLE_LINE] = { "Can't assign to __LINE__", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_EXPRESSION_NOT_WRITABLE_NIL] = { "Can't assign to nil", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED] = { "Can't assign to numbered parameter %.2s", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_EXPRESSION_NOT_WRITABLE_SELF] = { "Can't change the value of self", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE] = { "Can't assign to true", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_FLOAT_PARSE] = { "could not parse the float '%.*s'", PM_ERROR_LEVEL_SYNTAX },
@@ -267,9 +268,10 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK] = { "numbered parameter is already used in inner block", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_NUMBERED_PARAMETER_IT] = { "numbered parameters are not allowed when an 'it' parameter is defined", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_NUMBERED_PARAMETER_ORDINARY] = { "numbered parameters are not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX },
- [PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE] = { "numbered parameter is already used in outer scope", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK] = { "numbered parameter is already used in outer block", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_OPERATOR_MULTI_ASSIGN] = { "unexpected operator for a multiple assignment", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_OPERATOR_WRITE_ARGUMENTS] = { "unexpected operator after a call with arguments", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_OPERATOR_WRITE_BLOCK] = { "unexpected operator after a call with a block", PM_ERROR_LEVEL_SYNTAX },
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index 3670b90dd7..6f926452fa 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -1377,7 +1377,7 @@ module Prism
def test_double_scope_numbered_parameters
source = "-> { _1 + -> { _2 } }"
- errors = [["numbered parameter is already used in outer scope", 15..17]]
+ errors = [["numbered parameter is already used in outer block", 15..17]]
assert_errors expression(source), source, errors
end