Skip to content

Commit

Permalink
Add error for numbered parameter used in inner block
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed May 21, 2024
1 parent b2dc7e9 commit d5936ac
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 10 deletions.
4 changes: 3 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/prism/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
44 changes: 37 additions & 7 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -15450,6 +15456,13 @@ parse_string_part(pm_parser_t *parser) {
expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
pm_token_t closing = parser->previous;

// If this set of embedded statements only contains a single
// statement, then Ruby does not consider it as a possible statement
// that could emit a line event.
if (statements != NULL && statements->body.size == 1) {
pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
}

return (pm_node_t *) pm_embedded_statements_node_create(parser, &opening, statements, &closing);
}

Expand Down Expand Up @@ -15822,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
Expand Down
4 changes: 3 additions & 1 deletion templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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 },
Expand Down
2 changes: 1 addition & 1 deletion test/prism/errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1377,7 +1377,7 @@ def test_defining_numbered_parameter

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
Expand Down

0 comments on commit d5936ac

Please sign in to comment.