Skip to content

Commit 93bec2c

Browse files
committed
Handle errors when operator writes on a call with a block
1 parent d813c30 commit 93bec2c

File tree

4 files changed

+56
-13
lines changed

4 files changed

+56
-13
lines changed

include/yarp/diagnostic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ typedef enum {
151151
YP_ERR_NOT_EXPRESSION,
152152
YP_ERR_NUMBER_LITERAL_UNDERSCORE,
153153
YP_ERR_OPERATOR_MULTI_ASSIGN,
154+
YP_ERR_OPERATOR_WRITE_BLOCK,
154155
YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI,
155156
YP_ERR_PARAMETER_BLOCK_MULTI,
156157
YP_ERR_PARAMETER_NAME_REPEAT,

src/diagnostic.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ static const char* const diagnostic_messages[YP_DIAGNOSTIC_ID_LEN] = {
184184
[YP_ERR_MULTI_ASSIGN_MULTI_SPLATS] = "Multiple splats in multiple assignment",
185185
[YP_ERR_NOT_EXPRESSION] = "Expected an expression after `not`",
186186
[YP_ERR_NUMBER_LITERAL_UNDERSCORE] = "Number literal ending with a `_`",
187+
[YP_ERR_OPERATOR_WRITE_BLOCK] = "Unexpected operator after a call with a block",
187188
[YP_ERR_OPERATOR_MULTI_ASSIGN] = "Unexpected operator for a multiple assignment",
188189
[YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = "Unexpected multiple `**` splat parameters",
189190
[YP_ERR_PARAMETER_BLOCK_MULTI] = "Multiple block parameters; only one block is allowed",

src/yarp.c

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13112,6 +13112,20 @@ parse_assignment_value(yp_parser_t *parser, yp_binding_power_t previous_binding_
1311213112
return value;
1311313113
}
1311413114

13115+
// Ensures a call node that is about to become a call operator node does not
13116+
// have a block attached. If it does, then we'll need to add an error message
13117+
// and destroy the block. Ideally we would keep the node around so that
13118+
// consumers would still have access to it, but we don't have a great structure
13119+
// for that at the moment.
13120+
static void
13121+
parse_call_operator_write_block(yp_parser_t *parser, yp_call_node_t *call_node, const yp_token_t *operator) {
13122+
if (call_node->block != NULL) {
13123+
yp_diagnostic_list_append(&parser->error_list, operator->start, operator->end, YP_ERR_OPERATOR_WRITE_BLOCK);
13124+
yp_node_destroy(parser, (yp_node_t *) call_node->block);
13125+
call_node->block = NULL;
13126+
}
13127+
}
13128+
1311513129
static inline yp_node_t *
1311613130
parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t previous_binding_power, yp_binding_power_t binding_power) {
1311713131
yp_token_t token = parser->current;
@@ -13217,13 +13231,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1321713231
return result;
1321813232
}
1321913233
case YP_CALL_NODE: {
13220-
yp_call_node_t *call_node = (yp_call_node_t *) node;
13221-
1322213234
// If we have a vcall (a method with no arguments and no
1322313235
// receiver that could have been a local variable) then we
1322413236
// will transform it into a local variable write.
13225-
if (yp_call_node_variable_call_p(call_node)) {
13226-
yp_location_t message_loc = call_node->message_loc;
13237+
if (yp_call_node_variable_call_p((yp_call_node_t *) node)) {
13238+
yp_location_t message_loc = ((yp_call_node_t *) node)->message_loc;
1322713239
yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
1322813240

1322913241
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
@@ -13241,6 +13253,9 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1324113253
parser_lex(parser);
1324213254
node = parse_target(parser, node);
1324313255

13256+
assert(YP_NODE_TYPE_P(node, YP_CALL_NODE));
13257+
parse_call_operator_write_block(parser, (yp_call_node_t *) node, &token);
13258+
1324413259
yp_node_t *value = parse_expression(parser, binding_power, YP_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
1324513260
return (yp_node_t *) yp_call_and_write_node_create(parser, (yp_call_node_t *) node, &token, value);
1324613261
}
@@ -13318,13 +13333,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1331813333
return result;
1331913334
}
1332013335
case YP_CALL_NODE: {
13321-
yp_call_node_t *call_node = (yp_call_node_t *) node;
13322-
1332313336
// If we have a vcall (a method with no arguments and no
1332413337
// receiver that could have been a local variable) then we
1332513338
// will transform it into a local variable write.
13326-
if (yp_call_node_variable_call_p(call_node)) {
13327-
yp_location_t message_loc = call_node->message_loc;
13339+
if (yp_call_node_variable_call_p((yp_call_node_t *) node)) {
13340+
yp_location_t message_loc = ((yp_call_node_t *) node)->message_loc;
1332813341
yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
1332913342

1333013343
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
@@ -13342,6 +13355,9 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1334213355
parser_lex(parser);
1334313356
node = parse_target(parser, node);
1334413357

13358+
assert(YP_NODE_TYPE_P(node, YP_CALL_NODE));
13359+
parse_call_operator_write_block(parser, (yp_call_node_t *) node, &token);
13360+
1334513361
yp_node_t *value = parse_expression(parser, binding_power, YP_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
1334613362
return (yp_node_t *) yp_call_or_write_node_create(parser, (yp_call_node_t *) node, &token, value);
1334713363
}
@@ -13429,13 +13445,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1342913445
return result;
1343013446
}
1343113447
case YP_CALL_NODE: {
13432-
yp_call_node_t *call_node = (yp_call_node_t *) node;
13433-
1343413448
// If we have a vcall (a method with no arguments and no
1343513449
// receiver that could have been a local variable) then we
1343613450
// will transform it into a local variable write.
13437-
if (yp_call_node_variable_call_p(call_node)) {
13438-
yp_location_t message_loc = call_node->message_loc;
13451+
if (yp_call_node_variable_call_p((yp_call_node_t *) node)) {
13452+
yp_location_t message_loc = ((yp_call_node_t *) node)->message_loc;
1343913453
yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
1344013454

1344113455
if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
@@ -13450,8 +13464,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
1345013464
return result;
1345113465
}
1345213466

13453-
node = parse_target(parser, node);
1345413467
parser_lex(parser);
13468+
node = parse_target(parser, node);
13469+
13470+
assert(YP_NODE_TYPE_P(node, YP_CALL_NODE));
13471+
parse_call_operator_write_block(parser, (yp_call_node_t *) node, &token);
1345513472

1345613473
yp_node_t *value = parse_expression(parser, binding_power, YP_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
1345713474
return (yp_node_t *) yp_call_operator_write_node_create(parser, (yp_call_node_t *) node, &token, value);

test/yarp/errors_test.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,30 @@ def test_invalid_global_variable_write
11561156
]
11571157
end
11581158

1159+
def test_call_with_block_and_write
1160+
source = "foo {} &&= 1"
1161+
assert_errors expression(source), source, [
1162+
["Unexpected write target", 0..6],
1163+
["Unexpected operator after a call with a block", 7..10]
1164+
]
1165+
end
1166+
1167+
def test_call_with_block_or_write
1168+
source = "foo {} ||= 1"
1169+
assert_errors expression(source), source, [
1170+
["Unexpected write target", 0..6],
1171+
["Unexpected operator after a call with a block", 7..10]
1172+
]
1173+
end
1174+
1175+
def test_call_with_block_operator_write
1176+
source = "foo {} += 1"
1177+
assert_errors expression(source), source, [
1178+
["Unexpected write target", 0..6],
1179+
["Unexpected operator after a call with a block", 7..9]
1180+
]
1181+
end
1182+
11591183
private
11601184

11611185
def assert_errors(expected, source, errors)

0 commit comments

Comments
 (0)