Skip to content

Commit

Permalink
Merge pull request #2768 from ruby/disallow-safe-navigation-call-target
Browse files Browse the repository at this point in the history
Disallow safe navigation in a call target node
  • Loading branch information
kddnewton committed May 3, 2024
2 parents 32e3808 + b1917ad commit 994316d
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 12 deletions.
1 change: 1 addition & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ errors:
- UNEXPECTED_BLOCK_ARGUMENT
- UNEXPECTED_INDEX_BLOCK
- UNEXPECTED_INDEX_KEYWORDS
- UNEXPECTED_SAFE_NAVIGATION
- UNEXPECTED_TOKEN_CLOSE_CONTEXT
- UNEXPECTED_TOKEN_IGNORE
- UNTIL_TERM
Expand Down
28 changes: 16 additions & 12 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -13034,7 +13034,7 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
* Convert the given node into a valid target node.
*/
static pm_node_t *
parse_target(pm_parser_t *parser, pm_node_t *target) {
parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple) {
switch (PM_NODE_TYPE(target)) {
case PM_MISSING_NODE:
return target;
Expand Down Expand Up @@ -13102,7 +13102,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
pm_splat_node_t *splat = (pm_splat_node_t *) target;

if (splat->expression != NULL) {
splat->expression = parse_target(parser, splat->expression);
splat->expression = parse_target(parser, splat->expression, multiple);
}

return (pm_node_t *) splat;
Expand Down Expand Up @@ -13140,6 +13140,10 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
}

if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) {
if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
}

parse_write_name(parser, &call->name);
return (pm_node_t *) pm_call_target_node_create(parser, call);
}
Expand Down Expand Up @@ -13167,8 +13171,8 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
* assignment.
*/
static pm_node_t *
parse_target_validate(pm_parser_t *parser, pm_node_t *target) {
pm_node_t *result = parse_target(parser, target);
parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
pm_node_t *result = parse_target(parser, target, multiple);

// Ensure that we have one of an =, an 'in' in for indexes, and a ')' in parens after the targets.
if (
Expand Down Expand Up @@ -13405,7 +13409,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b
bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);

pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target));
pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true));

while (accept1(parser, PM_TOKEN_COMMA)) {
if (accept1(parser, PM_TOKEN_USTAR)) {
Expand All @@ -13421,15 +13425,15 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b

if (token_begins_expression_p(parser->current.type)) {
name = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
name = parse_target(parser, name);
name = parse_target(parser, name, true);
}

pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
pm_multi_target_node_targets_append(parser, result, splat);
has_rest = true;
} else if (token_begins_expression_p(parser->current.type)) {
pm_node_t *target = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA);
target = parse_target(parser, target);
target = parse_target(parser, target, true);

pm_multi_target_node_targets_append(parser, result, target);
} else if (!match1(parser, PM_TOKEN_EOF)) {
Expand Down Expand Up @@ -14450,7 +14454,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
pm_rescue_node_operator_set(rescue, &parser->previous);

pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
reference = parse_target(parser, reference);
reference = parse_target(parser, reference, false);

pm_rescue_node_reference_set(rescue, reference);
break;
Expand Down Expand Up @@ -14480,7 +14484,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
pm_rescue_node_operator_set(rescue, &parser->previous);

pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
reference = parse_target(parser, reference);
reference = parse_target(parser, reference, false);

pm_rescue_node_reference_set(rescue, reference);
break;
Expand Down Expand Up @@ -17264,7 +17268,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
return (pm_node_t *) multi_target;
}

return parse_target_validate(parser, (pm_node_t *) multi_target);
return parse_target_validate(parser, (pm_node_t *) multi_target, false);
}

// If we have a single statement and are ending on a right parenthesis
Expand Down Expand Up @@ -18564,7 +18568,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match1(parser, PM_TOKEN_COMMA)) {
index = parse_targets(parser, index, PM_BINDING_POWER_INDEX);
} else {
index = parse_target(parser, index);
index = parse_target(parser, index, false);
}

context_pop(parser);
Expand Down Expand Up @@ -19343,7 +19347,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match1(parser, PM_TOKEN_COMMA)) {
return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX);
} else {
return parse_target_validate(parser, splat);
return parse_target_validate(parser, splat, false);
}
}
case PM_TOKEN_BANG: {
Expand Down
1 change: 1 addition & 0 deletions templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_INDEX_BLOCK] = { "unexpected block arg given in index; blocks are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index; keywords are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX },
Expand Down

0 comments on commit 994316d

Please sign in to comment.