Skip to content

Commit b1917ad

Browse files
committed
Disallow safe navigation in a call target node
1 parent 32e3808 commit b1917ad

File tree

3 files changed

+18
-12
lines changed

3 files changed

+18
-12
lines changed

config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ errors:
253253
- UNEXPECTED_BLOCK_ARGUMENT
254254
- UNEXPECTED_INDEX_BLOCK
255255
- UNEXPECTED_INDEX_KEYWORDS
256+
- UNEXPECTED_SAFE_NAVIGATION
256257
- UNEXPECTED_TOKEN_CLOSE_CONTEXT
257258
- UNEXPECTED_TOKEN_IGNORE
258259
- UNTIL_TERM

src/prism.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13034,7 +13034,7 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
1303413034
* Convert the given node into a valid target node.
1303513035
*/
1303613036
static pm_node_t *
13037-
parse_target(pm_parser_t *parser, pm_node_t *target) {
13037+
parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple) {
1303813038
switch (PM_NODE_TYPE(target)) {
1303913039
case PM_MISSING_NODE:
1304013040
return target;
@@ -13102,7 +13102,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
1310213102
pm_splat_node_t *splat = (pm_splat_node_t *) target;
1310313103

1310413104
if (splat->expression != NULL) {
13105-
splat->expression = parse_target(parser, splat->expression);
13105+
splat->expression = parse_target(parser, splat->expression, multiple);
1310613106
}
1310713107

1310813108
return (pm_node_t *) splat;
@@ -13140,6 +13140,10 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
1314013140
}
1314113141

1314213142
if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) {
13143+
if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13144+
pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13145+
}
13146+
1314313147
parse_write_name(parser, &call->name);
1314413148
return (pm_node_t *) pm_call_target_node_create(parser, call);
1314513149
}
@@ -13167,8 +13171,8 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
1316713171
* assignment.
1316813172
*/
1316913173
static pm_node_t *
13170-
parse_target_validate(pm_parser_t *parser, pm_node_t *target) {
13171-
pm_node_t *result = parse_target(parser, target);
13174+
parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13175+
pm_node_t *result = parse_target(parser, target, multiple);
1317213176

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

1340713411
pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13408-
pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target));
13412+
pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true));
1340913413

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

1342213426
if (token_begins_expression_p(parser->current.type)) {
1342313427
name = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
13424-
name = parse_target(parser, name);
13428+
name = parse_target(parser, name, true);
1342513429
}
1342613430

1342713431
pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
1342813432
pm_multi_target_node_targets_append(parser, result, splat);
1342913433
has_rest = true;
1343013434
} else if (token_begins_expression_p(parser->current.type)) {
1343113435
pm_node_t *target = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA);
13432-
target = parse_target(parser, target);
13436+
target = parse_target(parser, target, true);
1343313437

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

1445214456
pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
14453-
reference = parse_target(parser, reference);
14457+
reference = parse_target(parser, reference, false);
1445414458

1445514459
pm_rescue_node_reference_set(rescue, reference);
1445614460
break;
@@ -14480,7 +14484,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
1448014484
pm_rescue_node_operator_set(rescue, &parser->previous);
1448114485

1448214486
pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
14483-
reference = parse_target(parser, reference);
14487+
reference = parse_target(parser, reference, false);
1448414488

1448514489
pm_rescue_node_reference_set(rescue, reference);
1448614490
break;
@@ -17264,7 +17268,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1726417268
return (pm_node_t *) multi_target;
1726517269
}
1726617270

17267-
return parse_target_validate(parser, (pm_node_t *) multi_target);
17271+
return parse_target_validate(parser, (pm_node_t *) multi_target, false);
1726817272
}
1726917273

1727017274
// If we have a single statement and are ending on a right parenthesis
@@ -18564,7 +18568,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1856418568
if (match1(parser, PM_TOKEN_COMMA)) {
1856518569
index = parse_targets(parser, index, PM_BINDING_POWER_INDEX);
1856618570
} else {
18567-
index = parse_target(parser, index);
18571+
index = parse_target(parser, index, false);
1856818572
}
1856918573

1857018574
context_pop(parser);
@@ -19343,7 +19347,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1934319347
if (match1(parser, PM_TOKEN_COMMA)) {
1934419348
return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX);
1934519349
} else {
19346-
return parse_target_validate(parser, splat);
19350+
return parse_target_validate(parser, splat, false);
1934719351
}
1934819352
}
1934919353
case PM_TOKEN_BANG: {

templates/src/diagnostic.c.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
335335
[PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX },
336336
[PM_ERR_UNEXPECTED_INDEX_BLOCK] = { "unexpected block arg given in index; blocks are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
337337
[PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index; keywords are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
338+
[PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", PM_ERROR_LEVEL_SYNTAX },
338339
[PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX },
339340
[PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX },
340341
[PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX },

0 commit comments

Comments
 (0)