Skip to content

Commit 3237be1

Browse files
Earlopainkddnewton
authored andcommitted
[Bug #21669] Thoroughly implement void value expression check (prism)
1 parent 6a4e53f commit 3237be1

18 files changed

+261
-26
lines changed

bootstraptest/test_flow.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ def m1 *args; $a << 2
376376
; $a << 3
377377
end; $a << 4
378378
def m2; $a << 5
379-
m1(:a, :b, (return 1; :c)); $a << 6
379+
m1(:a, :b, (return 1 if true; :c)); $a << 6
380380
end; $a << 7
381381
m2; $a << 8
382382
; $a << 9
@@ -399,7 +399,7 @@ def m(); $a << 4
399399
m2(begin; $a << 5
400400
2; $a << 6
401401
ensure; $a << 7
402-
return 3; $a << 8
402+
return 3 if true; $a << 8
403403
end); $a << 9
404404
4; $a << 10
405405
end; $a << 11

bootstraptest/test_syntax.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
571571

572572
assert_equal 'ok', %q{
573573
1.times{
574-
p(1, (next; 2))
574+
p(1, (next if true; 2))
575575
}; :ok
576576
}
577577
assert_equal '3', %q{
@@ -585,7 +585,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
585585
i = 0
586586
1 + (while true
587587
break 2 if (i+=1) > 1
588-
p(1, (next; 2))
588+
p(1, (next if true; 2))
589589
end)
590590
}
591591
# redo
@@ -594,7 +594,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
594594
1.times{
595595
break if i>1
596596
i+=1
597-
p(1, (redo; 2))
597+
p(1, (redo if true; 2))
598598
}; :ok
599599
}
600600
assert_equal '3', %q{
@@ -608,7 +608,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
608608
i = 0
609609
1 + (while true
610610
break 2 if (i+=1) > 1
611-
p(1, (redo; 2))
611+
p(1, (redo if true; 2))
612612
end)
613613
}
614614
assert_equal '1', %q{

prism/prism.c

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,13 @@ pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
10541054
return pm_parser_constant_id_raw(parser, token->start, token->end);
10551055
}
10561056

1057+
/**
1058+
* This macro allows you to define a case statement for all of the nodes that
1059+
* may result in a void value.
1060+
*/
1061+
#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
1062+
case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE
1063+
10571064
/**
10581065
* Check whether or not the given node is value expression.
10591066
* If the node is value node, it returns NULL.
@@ -1065,12 +1072,7 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
10651072

10661073
while (node != NULL) {
10671074
switch (PM_NODE_TYPE(node)) {
1068-
case PM_RETURN_NODE:
1069-
case PM_BREAK_NODE:
1070-
case PM_NEXT_NODE:
1071-
case PM_REDO_NODE:
1072-
case PM_RETRY_NODE:
1073-
case PM_MATCH_REQUIRED_NODE:
1075+
case PM_CASE_VOID_VALUE:
10741076
return void_node != NULL ? void_node : node;
10751077
case PM_MATCH_PREDICATE_NODE:
10761078
return NULL;
@@ -1090,25 +1092,36 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
10901092

10911093
node = UP(cast->ensure_clause);
10921094
} else if (cast->rescue_clause != NULL) {
1093-
if (cast->statements == NULL) return NULL;
1095+
// https://bugs.ruby-lang.org/issues/21669
1096+
if (cast->else_clause == NULL || parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1097+
if (cast->statements == NULL) return NULL;
10941098

1095-
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1096-
if (vn == NULL) return NULL;
1097-
if (void_node == NULL) void_node = vn;
1099+
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1100+
if (vn == NULL) return NULL;
1101+
if (void_node == NULL) void_node = vn;
1102+
}
10981103

10991104
for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
11001105
pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1106+
11011107
if (vn == NULL) {
1108+
// https://bugs.ruby-lang.org/issues/21669
1109+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1110+
return NULL;
1111+
}
11021112
void_node = NULL;
11031113
break;
11041114
}
1105-
if (void_node == NULL) {
1106-
void_node = vn;
1107-
}
11081115
}
11091116

11101117
if (cast->else_clause != NULL) {
11111118
node = UP(cast->else_clause);
1119+
1120+
// https://bugs.ruby-lang.org/issues/21669
1121+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1122+
pm_node_t *vn = pm_check_value_expression(parser, node);
1123+
if (vn != NULL) return vn;
1124+
}
11121125
} else {
11131126
return void_node;
11141127
}
@@ -1118,6 +1131,50 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
11181131

11191132
break;
11201133
}
1134+
case PM_CASE_NODE: {
1135+
// https://bugs.ruby-lang.org/issues/21669
1136+
if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1137+
return NULL;
1138+
}
1139+
1140+
pm_case_node_t *cast = (pm_case_node_t *) node;
1141+
if (cast->else_clause == NULL) return NULL;
1142+
1143+
pm_node_t *condition;
1144+
PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1145+
assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
1146+
1147+
pm_when_node_t *cast = (pm_when_node_t *) condition;
1148+
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1149+
if (vn == NULL) return NULL;
1150+
if (void_node == NULL) void_node = vn;
1151+
}
1152+
1153+
node = UP(cast->else_clause);
1154+
break;
1155+
}
1156+
case PM_CASE_MATCH_NODE: {
1157+
// https://bugs.ruby-lang.org/issues/21669
1158+
if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1159+
return NULL;
1160+
}
1161+
1162+
pm_case_match_node_t *cast = (pm_case_match_node_t *) node;
1163+
if (cast->else_clause == NULL) return NULL;
1164+
1165+
pm_node_t *condition;
1166+
PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1167+
assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
1168+
1169+
pm_in_node_t *cast = (pm_in_node_t *) condition;
1170+
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1171+
if (vn == NULL) return NULL;
1172+
if (void_node == NULL) void_node = vn;
1173+
}
1174+
1175+
node = UP(cast->else_clause);
1176+
break;
1177+
}
11211178
case PM_ENSURE_NODE: {
11221179
pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
11231180
node = UP(cast->statements);
@@ -1130,6 +1187,22 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
11301187
}
11311188
case PM_STATEMENTS_NODE: {
11321189
pm_statements_node_t *cast = (pm_statements_node_t *) node;
1190+
1191+
// https://bugs.ruby-lang.org/issues/21669
1192+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1193+
pm_node_t *body_part;
1194+
PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
1195+
switch (PM_NODE_TYPE(body_part)) {
1196+
case PM_CASE_VOID_VALUE:
1197+
if (void_node == NULL) {
1198+
void_node = body_part;
1199+
}
1200+
return void_node;
1201+
default: break;
1202+
}
1203+
}
1204+
}
1205+
11331206
node = cast->body.nodes[cast->body.size - 1];
11341207
break;
11351208
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def ((return; 1)).bar; end
2+
^ cannot define singleton method for literals
3+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
x = begin
2+
return
3+
^~~~~~ unexpected void value expression
4+
rescue
5+
return
6+
else
7+
return
8+
end
9+
10+
x = begin
11+
return
12+
rescue
13+
"OK"
14+
else
15+
return
16+
^~~~~~ unexpected void value expression
17+
end
18+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def ((return; 1)).bar; end
2+
^~~~~~ unexpected void value expression
3+
^ cannot define singleton method for literals
4+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
x = begin
2+
return
3+
rescue
4+
return
5+
else
6+
return
7+
^~~~~~ unexpected void value expression
8+
end
9+
10+
x = begin
11+
ignored_because_else_branch
12+
rescue
13+
return
14+
else
15+
return
16+
^~~~~~ unexpected void value expression
17+
end
18+
19+
x = case
20+
when 1 then return
21+
^~~~~~ unexpected void value expression
22+
else return
23+
end
24+
25+
x = case 1
26+
in 2 then return
27+
^~~~~~ unexpected void value expression
28+
else return
29+
end
30+
31+
x = begin
32+
return
33+
^~~~~~ unexpected void value expression
34+
"NG"
35+
end
36+
37+
x = if rand < 0.5
38+
return
39+
^~~~~~ unexpected void value expression
40+
"NG"
41+
else
42+
return
43+
end
44+

test/prism/errors/singleton_method_for_literals.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ def (1).g; end
22
^ cannot define singleton method for literals
33
def ((a; 1)).foo; end
44
^ cannot define singleton method for literals
5-
def ((return; 1)).bar; end
6-
^ cannot define singleton method for literals
75
def (((1))).foo; end
86
^ cannot define singleton method for literals
97
def (__FILE__).foo; end

test/prism/errors/void_value_expression_in_begin_statement.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ x = begin return ensure return end
1414
^~~~~~ unexpected void value expression
1515
x = begin return; rescue; return end
1616
^~~~~~ unexpected void value expression
17-
x = begin return; rescue; return; else return end
18-
^~~~~~ unexpected void value expression
1917
x = begin; return; rescue; retry; end
2018
^~~~~~ unexpected void value expression
2119

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
x = begin
2+
foo
3+
rescue
4+
return
5+
else
6+
return
7+
end
8+
9+
x = case
10+
when 1 then return
11+
else return
12+
end
13+
14+
x = case 1
15+
in 2 then return
16+
else return
17+
end
18+
19+
x = begin
20+
return
21+
"NG"
22+
end
23+
24+
x = if rand < 0.5
25+
return
26+
"NG"
27+
else
28+
return
29+
end

0 commit comments

Comments
 (0)