mirror of
https://github.com/ruby/ruby.git
synced 2026-01-29 21:44:28 +00:00
[ruby/prism] Parse it default parameter
https://github.com/ruby/prism/commit/a0c5361b9f
This commit is contained in:
parent
cd4290910c
commit
603f2ca730
@ -175,6 +175,7 @@ static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
|
||||
[PM_ERR_INVALID_PERCENT] = "invalid `%` token", // TODO WHAT?
|
||||
[PM_ERR_INVALID_TOKEN] = "invalid token", // TODO WHAT?
|
||||
[PM_ERR_INVALID_VARIABLE_GLOBAL] = "invalid global variable",
|
||||
[PM_ERR_IT_NOT_ALLOWED] = "`it` is not allowed when an ordinary parameter is defined",
|
||||
[PM_ERR_LAMBDA_OPEN] = "expected a `do` keyword or a `{` to open the lambda block",
|
||||
[PM_ERR_LAMBDA_TERM_BRACE] = "expected a lambda block beginning with `{` to end with `}`",
|
||||
[PM_ERR_LAMBDA_TERM_END] = "expected a lambda block beginning with `do` to end with `end`",
|
||||
|
||||
@ -167,6 +167,7 @@ typedef enum {
|
||||
PM_ERR_INVALID_PERCENT,
|
||||
PM_ERR_INVALID_TOKEN,
|
||||
PM_ERR_INVALID_VARIABLE_GLOBAL,
|
||||
PM_ERR_IT_NOT_ALLOWED,
|
||||
PM_ERR_LAMBDA_OPEN,
|
||||
PM_ERR_LAMBDA_TERM_BRACE,
|
||||
PM_ERR_LAMBDA_TERM_END,
|
||||
|
||||
@ -4196,12 +4196,10 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new LocalVariableReadNode node.
|
||||
* Allocate a new LocalVariableReadNode node with constant_id.
|
||||
*/
|
||||
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);
|
||||
|
||||
pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth) {
|
||||
if (parser->current_param_name == name_id) {
|
||||
pm_parser_err_token(parser, name, PM_ERR_PARAMETER_CIRCULAR);
|
||||
}
|
||||
@ -4220,6 +4218,15 @@ pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name,
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate and initialize a new LocalVariableWriteNode node.
|
||||
*/
|
||||
@ -4245,6 +4252,57 @@ pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name,
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given bounds comprise `it`.
|
||||
*/
|
||||
static inline bool
|
||||
pm_token_is_it(const uint8_t *start, const uint8_t *end) {
|
||||
return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given node is `it` default parameter.
|
||||
*/
|
||||
static inline bool
|
||||
pm_node_is_it(pm_parser_t *parser, pm_node_t *node) {
|
||||
// Check if it's a local variable reference
|
||||
if (node->type != PM_CALL_NODE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's a variable call
|
||||
pm_call_node_t *call_node = (pm_call_node_t *) node;
|
||||
if (!pm_call_node_variable_call_p(call_node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's called `it`
|
||||
pm_constant_id_t id = ((pm_call_node_t *)node)->name;
|
||||
pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, id);
|
||||
return pm_token_is_it(constant->start, constant->start + constant->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a `it` variable call node to a node for `it` default parameter.
|
||||
*/
|
||||
static pm_node_t *
|
||||
pm_node_check_it(pm_parser_t *parser, pm_node_t *node) {
|
||||
if (
|
||||
(parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) &&
|
||||
!parser->current_scope->closed &&
|
||||
pm_node_is_it(parser, node)
|
||||
) {
|
||||
if (parser->current_scope->explicit_params) {
|
||||
pm_parser_err_previous(parser, PM_ERR_IT_NOT_ALLOWED);
|
||||
} else {
|
||||
pm_node_destroy(parser, node);
|
||||
pm_constant_id_t name_id = pm_parser_constant_id_constant(parser, "0it", 3);
|
||||
node = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given bounds comprise a numbered parameter (i.e., they
|
||||
* are of the form /^_\d$/).
|
||||
@ -14449,6 +14507,31 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
||||
if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
|
||||
node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
|
||||
}
|
||||
else {
|
||||
// Check if `it` is not going to be assigned.
|
||||
switch (parser->current.type) {
|
||||
case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
|
||||
case PM_TOKEN_AMPERSAND_EQUAL:
|
||||
case PM_TOKEN_CARET_EQUAL:
|
||||
case PM_TOKEN_EQUAL:
|
||||
case PM_TOKEN_GREATER_GREATER_EQUAL:
|
||||
case PM_TOKEN_LESS_LESS_EQUAL:
|
||||
case PM_TOKEN_MINUS_EQUAL:
|
||||
case PM_TOKEN_PARENTHESIS_RIGHT:
|
||||
case PM_TOKEN_PERCENT_EQUAL:
|
||||
case PM_TOKEN_PIPE_EQUAL:
|
||||
case PM_TOKEN_PIPE_PIPE_EQUAL:
|
||||
case PM_TOKEN_PLUS_EQUAL:
|
||||
case PM_TOKEN_SLASH_EQUAL:
|
||||
case PM_TOKEN_STAR_EQUAL:
|
||||
case PM_TOKEN_STAR_STAR_EQUAL:
|
||||
break;
|
||||
default:
|
||||
// Once we know it's neither a method call nor an assignment,
|
||||
// we can finally create `it` default parameter.
|
||||
node = pm_node_check_it(parser, node);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -15056,6 +15139,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
||||
|
||||
if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
|
||||
receiver = parse_variable_call(parser);
|
||||
receiver = pm_node_check_it(parser, receiver);
|
||||
|
||||
saved_param_name = pm_parser_current_param_name_unset(parser);
|
||||
pm_parser_scope_push(parser, true);
|
||||
|
||||
@ -2069,6 +2069,13 @@ module Prism
|
||||
], compare_ripper: false # Ripper does not check 'both block arg and actual block given'.
|
||||
end
|
||||
|
||||
def test_it_with_ordinary_parameter
|
||||
source = "proc { || it }"
|
||||
errors = [["`it` is not allowed when an ordinary parameter is defined", 10..12]]
|
||||
|
||||
assert_errors expression(source), source, errors, compare_ripper: false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")
|
||||
|
||||
@ -570,6 +570,12 @@ module Prism
|
||||
|
||||
def test_LocalVariableReadNode
|
||||
assert_location(LocalVariableReadNode, "foo = 1; foo", 9...12)
|
||||
assert_location(LocalVariableReadNode, "-> { it }", 5...7) do |node|
|
||||
node.body.body.first
|
||||
end
|
||||
assert_location(LocalVariableReadNode, "foo { it }", 6...8) do |node|
|
||||
node.block.body.body.first
|
||||
end
|
||||
end
|
||||
|
||||
def test_LocalVariableTargetNode
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user