[ruby/prism] Disallow defining a numbered parameter method

(https://github.com/ruby/prism/pull/1797)

https://github.com/ruby/prism/commit/c13165e6aa
This commit is contained in:
Kevin Newton 2023-11-20 11:12:46 -05:00 committed by git
parent f9fb05f9d0
commit 5b0256e3c4
2 changed files with 52 additions and 47 deletions

View File

@ -4007,21 +4007,33 @@ pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name,
return node; return node;
} }
/**
* Returns true if the given bounds comprise a numbered parameter (i.e., they
* are of the form /^_\d$/).
*/
static inline bool static inline bool
token_is_numbered_parameter(const uint8_t *start, const uint8_t *end) { pm_token_is_numbered_parameter(const uint8_t *start, const uint8_t *end) {
return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (pm_char_is_decimal_digit(start[1])); return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (pm_char_is_decimal_digit(start[1]));
} }
/**
* Ensure the given bounds do not comprise a numbered parameter. If they do, add
* an appropriate error message to the parser.
*/
static inline void
pm_refute_numbered_parameter(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
if (pm_token_is_numbered_parameter(start, end)) {
pm_parser_err(parser, start, end, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
}
/** /**
* Allocate and initialize a new LocalVariableTargetNode node. * Allocate and initialize a new LocalVariableTargetNode node.
*/ */
static pm_local_variable_target_node_t * static pm_local_variable_target_node_t *
pm_local_variable_target_node_create(pm_parser_t *parser, const pm_token_t *name) { pm_local_variable_target_node_create(pm_parser_t *parser, const pm_token_t *name) {
pm_local_variable_target_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_target_node_t); pm_local_variable_target_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_target_node_t);
pm_refute_numbered_parameter(parser, name->start, name->end);
if (token_is_numbered_parameter(name->start, name->end)) {
pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
*node = (pm_local_variable_target_node_t) { *node = (pm_local_variable_target_node_t) {
{ {
@ -5752,9 +5764,7 @@ static void
pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) { pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
// We want to check whether the parameter name is a numbered parameter or // We want to check whether the parameter name is a numbered parameter or
// not. // not.
if (token_is_numbered_parameter(name->start, name->end)) { pm_refute_numbered_parameter(parser, name->start, name->end);
pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
// We want to ignore any parameter name that starts with an underscore. // We want to ignore any parameter name that starts with an underscore.
if ((name->start < name->end) && (*name->start == '_')) return; if ((name->start < name->end) && (*name->start == '_')) return;
@ -9150,7 +9160,7 @@ parser_lex(pm_parser_t *parser) {
!(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) && !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
(type == PM_TOKEN_IDENTIFIER) && (type == PM_TOKEN_IDENTIFIER) &&
((pm_parser_local_depth(parser, &parser->current) != -1) || ((pm_parser_local_depth(parser, &parser->current) != -1) ||
token_is_numbered_parameter(parser->current.start, parser->current.end)) pm_token_is_numbered_parameter(parser->current.start, parser->current.end))
) { ) {
lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL); lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
} }
@ -10488,7 +10498,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
target->type = PM_GLOBAL_VARIABLE_TARGET_NODE; target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
return target; return target;
case PM_LOCAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE:
if (token_is_numbered_parameter(target->location.start, target->location.end)) { if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) {
pm_parser_err_node(parser, target, PM_ERR_PARAMETER_NUMBERED_RESERVED); pm_parser_err_node(parser, target, PM_ERR_PARAMETER_NUMBERED_RESERVED);
} else { } else {
assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t));
@ -10547,10 +10557,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t));
target->type = PM_LOCAL_VARIABLE_TARGET_NODE; target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
if (token_is_numbered_parameter(message.start, message.end)) { pm_refute_numbered_parameter(parser, message.start, message.end);
pm_parser_err_location(parser, &message, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
return target; return target;
} }
@ -10631,10 +10638,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
return (pm_node_t *) node; return (pm_node_t *) node;
} }
case PM_LOCAL_VARIABLE_READ_NODE: { case PM_LOCAL_VARIABLE_READ_NODE: {
if (token_is_numbered_parameter(target->location.start, target->location.end)) { pm_refute_numbered_parameter(parser, target->location.start, target->location.end);
pm_parser_err_node(parser, target, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target;
pm_constant_id_t constant_id = local_read->name; pm_constant_id_t constant_id = local_read->name;
@ -10696,10 +10700,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end); pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end);
target = (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator); target = (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator);
if (token_is_numbered_parameter(message.start, message.end)) { pm_refute_numbered_parameter(parser, message.start, message.end);
pm_parser_err_location(parser, &message, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
return target; return target;
} }
@ -12506,7 +12507,7 @@ parse_variable_call(pm_parser_t *parser) {
return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth); return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
} }
if (!parser->current_scope->closed && token_is_numbered_parameter(parser->previous.start, parser->previous.end)) { if (!parser->current_scope->closed && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
// Indicate that this scope is using numbered params so that child // Indicate that this scope is using numbered params so that child
// scopes cannot. // scopes cannot.
parser->current_scope->numbered_params = true; parser->current_scope->numbered_params = true;
@ -12553,15 +12554,23 @@ parse_variable_call(pm_parser_t *parser) {
return (pm_node_t *) node; return (pm_node_t *) node;
} }
/**
* Parse the method definition name based on the current token available on the
* parser. If it does not match a valid method definition name, then a missing
* token is returned.
*/
static inline pm_token_t static inline pm_token_t
parse_method_definition_name(pm_parser_t *parser) { parse_method_definition_name(pm_parser_t *parser) {
switch (parser->current.type) { switch (parser->current.type) {
case PM_CASE_KEYWORD: case PM_CASE_KEYWORD:
case PM_TOKEN_CONSTANT: case PM_TOKEN_CONSTANT:
case PM_TOKEN_IDENTIFIER:
case PM_TOKEN_METHOD_NAME: case PM_TOKEN_METHOD_NAME:
parser_lex(parser); parser_lex(parser);
return parser->previous; return parser->previous;
case PM_TOKEN_IDENTIFIER:
pm_refute_numbered_parameter(parser, parser->current.start, parser->current.end);
parser_lex(parser);
return parser->previous;
case PM_CASE_OPERATOR: case PM_CASE_OPERATOR:
lex_state_set(parser, PM_LEX_STATE_ENDFN); lex_state_set(parser, PM_LEX_STATE_ENDFN);
parser_lex(parser); parser_lex(parser);
@ -14513,6 +14522,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
operator = parser->previous; operator = parser->previous;
name = parse_method_definition_name(parser); name = parse_method_definition_name(parser);
} else { } else {
pm_refute_numbered_parameter(parser, parser->previous.start, parser->previous.end);
pm_parser_scope_push(parser, true); pm_parser_scope_push(parser, true);
name = parser->previous; name = parser->previous;
} }
@ -15771,10 +15781,7 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *
// If the unescaped string is a slice of the source, then we can // If the unescaped string is a slice of the source, then we can
// copy the names directly. The pointers will line up. // copy the names directly. The pointers will line up.
local = pm_parser_local_add_location(parser, source, source + length); local = pm_parser_local_add_location(parser, source, source + length);
pm_refute_numbered_parameter(parser, source, source + length);
if (token_is_numbered_parameter(source, source + length)) {
pm_parser_err(parser, source, source + length, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
} else { } else {
// Otherwise, the name is a slice of the malloc-ed owned string, // Otherwise, the name is a slice of the malloc-ed owned string,
// in which case we need to copy it out into a new string. // in which case we need to copy it out into a new string.
@ -15787,7 +15794,7 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *
// NOLINTNEXTLINE(clang-analyzer-*) // NOLINTNEXTLINE(clang-analyzer-*)
local = pm_parser_local_add_owned(parser, (const uint8_t *) memory, length); local = pm_parser_local_add_owned(parser, (const uint8_t *) memory, length);
if (token_is_numbered_parameter(source, source + length)) { if (pm_token_is_numbered_parameter(source, source + length)) {
const pm_location_t *location = &call->receiver->location; const pm_location_t *location = &call->receiver->location;
pm_parser_err_location(parser, location, PM_ERR_PARAMETER_NUMBERED_RESERVED); pm_parser_err_location(parser, location, PM_ERR_PARAMETER_NUMBERED_RESERVED);
} }
@ -15924,13 +15931,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
// receiver that could have been a local variable) then we // receiver that could have been a local variable) then we
// will transform it into a local variable write. // will transform it into a local variable write.
if (pm_call_node_variable_call_p(cast)) { if (pm_call_node_variable_call_p(cast)) {
pm_location_t message_loc = cast->message_loc; pm_location_t *message_loc = &cast->message_loc;
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc.start, message_loc.end); pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
pm_parser_err_location(parser, &message_loc, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end);
pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
@ -16038,13 +16042,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
// receiver that could have been a local variable) then we // receiver that could have been a local variable) then we
// will transform it into a local variable write. // will transform it into a local variable write.
if (pm_call_node_variable_call_p(cast)) { if (pm_call_node_variable_call_p(cast)) {
pm_location_t message_loc = cast->message_loc; pm_location_t *message_loc = &cast->message_loc;
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc.start, message_loc.end); pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
pm_parser_err_location(parser, &message_loc, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end);
pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
@ -16162,13 +16163,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
// receiver that could have been a local variable) then we // receiver that could have been a local variable) then we
// will transform it into a local variable write. // will transform it into a local variable write.
if (pm_call_node_variable_call_p(cast)) { if (pm_call_node_variable_call_p(cast)) {
pm_location_t message_loc = cast->message_loc; pm_location_t *message_loc = &cast->message_loc;
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc.start, message_loc.end); pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
pm_parser_err_location(parser, &message_loc, PM_ERR_PARAMETER_NUMBERED_RESERVED);
}
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end);
pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); pm_node_t *value = parse_assignment_value(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);

View File

@ -1263,6 +1263,13 @@ module Prism
] ]
end end
def test_defining_numbered_parameter
error_messages = ["Token reserved for a numbered parameter"]
assert_error_messages "def _1; end", error_messages
assert_error_messages "def self._1; end", error_messages
end
def test_double_scope_numbered_parameters def test_double_scope_numbered_parameters
source = "-> { _1 + -> { _2 } }" source = "-> { _1 + -> { _2 } }"
errors = [["Numbered parameter is already used in outer scope", 15..17]] errors = [["Numbered parameter is already used in outer scope", 15..17]]