Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bootstraptest/test_flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ def m1 *args; $a << 2
; $a << 3
end; $a << 4
def m2; $a << 5
m1(:a, :b, (return 1; :c)); $a << 6
m1(:a, :b, (return 1 if true; :c)); $a << 6
end; $a << 7
m2; $a << 8
; $a << 9
Expand All @@ -399,7 +399,7 @@ def m(); $a << 4
m2(begin; $a << 5
2; $a << 6
ensure; $a << 7
return 3; $a << 8
return 3 if true; $a << 8
end); $a << 9
4; $a << 10
end; $a << 11
Expand Down
8 changes: 4 additions & 4 deletions bootstraptest/test_syntax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1

assert_equal 'ok', %q{
1.times{
p(1, (next; 2))
p(1, (next if true; 2))
}; :ok
}
assert_equal '3', %q{
Expand All @@ -585,7 +585,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
i = 0
1 + (while true
break 2 if (i+=1) > 1
p(1, (next; 2))
p(1, (next if true; 2))
end)
}
# redo
Expand All @@ -594,7 +594,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
1.times{
break if i>1
i+=1
p(1, (redo; 2))
p(1, (redo if true; 2))
}; :ok
}
assert_equal '3', %q{
Expand All @@ -608,7 +608,7 @@ class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
i = 0
1 + (while true
break 2 if (i+=1) > 1
p(1, (redo; 2))
p(1, (redo if true; 2))
end)
}
assert_equal '1', %q{
Expand Down
2 changes: 0 additions & 2 deletions prism/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,10 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
const char *version = check_string(value);

if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
const char *ruby_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
if (!pm_options_version_set(options, ruby_version, 3)) {
rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version));
}
} else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) {
const char *ruby_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
const char *nearest_version;

if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) {
Expand Down
1 change: 1 addition & 0 deletions prism/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <ruby.h>
#include <ruby/encoding.h>
#include <ruby/version.h>
#include "prism.h"

VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding, bool freeze);
Expand Down
99 changes: 86 additions & 13 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,13 @@ pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
return pm_parser_constant_id_raw(parser, token->start, token->end);
}

/**
* This macro allows you to define a case statement for all of the nodes that
* may result in a void value.
*/
#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE

/**
* Check whether or not the given node is value expression.
* If the node is value node, it returns NULL.
Expand All @@ -1065,12 +1072,7 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {

while (node != NULL) {
switch (PM_NODE_TYPE(node)) {
case PM_RETURN_NODE:
case PM_BREAK_NODE:
case PM_NEXT_NODE:
case PM_REDO_NODE:
case PM_RETRY_NODE:
case PM_MATCH_REQUIRED_NODE:
case PM_CASE_VOID_VALUE:
return void_node != NULL ? void_node : node;
case PM_MATCH_PREDICATE_NODE:
return NULL;
Expand All @@ -1090,25 +1092,36 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {

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

pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
if (vn == NULL) return NULL;
if (void_node == NULL) void_node = vn;
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
if (vn == NULL) return NULL;
if (void_node == NULL) void_node = vn;
}

for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));

if (vn == NULL) {
// https://bugs.ruby-lang.org/issues/21669
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
return NULL;
}
void_node = NULL;
break;
}
if (void_node == NULL) {
void_node = vn;
}
}

if (cast->else_clause != NULL) {
node = UP(cast->else_clause);

// https://bugs.ruby-lang.org/issues/21669
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
pm_node_t *vn = pm_check_value_expression(parser, node);
if (vn != NULL) return vn;
}
} else {
return void_node;
}
Expand All @@ -1118,6 +1131,50 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {

break;
}
case PM_CASE_NODE: {
// https://bugs.ruby-lang.org/issues/21669
if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
return NULL;
}

pm_case_node_t *cast = (pm_case_node_t *) node;
if (cast->else_clause == NULL) return NULL;

pm_node_t *condition;
PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));

pm_when_node_t *cast = (pm_when_node_t *) condition;
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
if (vn == NULL) return NULL;
if (void_node == NULL) void_node = vn;
}

node = UP(cast->else_clause);
break;
}
case PM_CASE_MATCH_NODE: {
// https://bugs.ruby-lang.org/issues/21669
if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
return NULL;
}

pm_case_match_node_t *cast = (pm_case_match_node_t *) node;
if (cast->else_clause == NULL) return NULL;

pm_node_t *condition;
PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));

pm_in_node_t *cast = (pm_in_node_t *) condition;
pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
if (vn == NULL) return NULL;
if (void_node == NULL) void_node = vn;
}

node = UP(cast->else_clause);
break;
}
case PM_ENSURE_NODE: {
pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
node = UP(cast->statements);
Expand All @@ -1130,6 +1187,22 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
}
case PM_STATEMENTS_NODE: {
pm_statements_node_t *cast = (pm_statements_node_t *) node;

// https://bugs.ruby-lang.org/issues/21669
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
pm_node_t *body_part;
PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
switch (PM_NODE_TYPE(body_part)) {
case PM_CASE_VOID_VALUE:
if (void_node == NULL) {
void_node = body_part;
}
return void_node;
default: break;
}
}
}

node = cast->body.nodes[cast->body.size - 1];
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def ((return; 1)).bar; end
^ cannot define singleton method for literals

18 changes: 18 additions & 0 deletions test/prism/errors/3.4-4.0/void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
x = begin
return
^~~~~~ unexpected void value expression
rescue
return
else
return
end

x = begin
return
rescue
"OK"
else
return
^~~~~~ unexpected void value expression
end

4 changes: 4 additions & 0 deletions test/prism/errors/4.1/singleton_method_with_void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def ((return; 1)).bar; end
^~~~~~ unexpected void value expression
^ cannot define singleton method for literals

44 changes: 44 additions & 0 deletions test/prism/errors/4.1/void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
x = begin
return
rescue
return
else
return
^~~~~~ unexpected void value expression
end

x = begin
ignored_because_else_branch
rescue
return
else
return
^~~~~~ unexpected void value expression
end

x = case
when 1 then return
^~~~~~ unexpected void value expression
else return
end

x = case 1
in 2 then return
^~~~~~ unexpected void value expression
else return
end

x = begin
return
^~~~~~ unexpected void value expression
"NG"
end

x = if rand < 0.5
return
^~~~~~ unexpected void value expression
"NG"
else
return
end

2 changes: 0 additions & 2 deletions test/prism/errors/singleton_method_for_literals.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ def (1).g; end
^ cannot define singleton method for literals
def ((a; 1)).foo; end
^ cannot define singleton method for literals
def ((return; 1)).bar; end
^ cannot define singleton method for literals
def (((1))).foo; end
^ cannot define singleton method for literals
def (__FILE__).foo; end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ x = begin return ensure return end
^~~~~~ unexpected void value expression
x = begin return; rescue; return end
^~~~~~ unexpected void value expression
x = begin return; rescue; return; else return end
^~~~~~ unexpected void value expression
x = begin; return; rescue; retry; end
^~~~~~ unexpected void value expression

29 changes: 29 additions & 0 deletions test/prism/fixtures/3.3-4.0/void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
x = begin
foo
rescue
return
else
return
end

x = case
when 1 then return
else return
end

x = case 1
in 2 then return
else return
end

x = begin
return
"NG"
end

x = if rand < 0.5
return
"NG"
else
return
end
7 changes: 7 additions & 0 deletions test/prism/fixtures/4.1/void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
x = begin
return
rescue
"OK"
else
return
end
31 changes: 31 additions & 0 deletions test/prism/fixtures/non_void_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
x = begin
return if true
"conditional"
end

x = if rand < 0.5
return
"else is nil"
end

x = if true
return if true
"conditional"
else
return
end

x = if true
return if true
else
return if true
"conditional"
end

x = case
when 1
return if true
"conditional"
else
return
end
3 changes: 3 additions & 0 deletions test/prism/fixtures_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ class FixturesTest < TestCase
except << "whitequark/ruby_bug_19281.txt"
end

# https://bugs.ruby-lang.org/issues/21168#note-5
except << "command_method_call_2.txt"
# https://bugs.ruby-lang.org/issues/21669
except << "4.1/void_value.txt"

Fixture.each_for_current_ruby(except: except) do |fixture|
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }
Expand Down
Loading