Bug #21669
openThoroughly implement void value expression check
Description
A void-value-expression check is a syntax check that raises a SyntaxError if an expression that cannot grammatically return a value (a "void value expression," such as a return expression) appears in a context where a value is expected (e.g., the right-hand side of an assignment, the argument of a method call, etc.). A typical example rejected by this check is x = return.
However, it has become clear that this check is incomplete. This ticket summarizes the results of a discussion with @matz (Yukihiro Matsumoto) at the hackathon before RubyWorld Conference today.
1. Expressions containing branches¶
@matz (Yukihiro Matsumoto) said that an expression containing branches should be considered a void value expression only if it can be grammatically determined that all possible branches do not return a value.
Based on this, both parse.y and prism currently reject the following code with a "void value expression" SyntaxError, but it should be accepted:
x = begin
raise
return
rescue
"OK"
else
return
end
# Expected: Code parses and executes successfully.
# Actual: Rejected with "unexpected void value expression" SyntaxError.
Reason: The rescue clause can return a value ("OK"), so the entire begin expression is not void.
Conversely, the following code is currently accepted by both parse.y and prism, but it should be rejected:
x = begin
foo
rescue
return
else
return
end
# Expected: Rejected with SyntaxError.
# Actual: Code parses and executes.
Reason: This expression always does return, so it is void whether foo raises an exception or not.
Furthermore, case expressions must also be rejected similarly when all branches are void:
x =
case
when 1; return
when 2; return
else return
end
# Expected: Rejected with SyntaxError.
# Actual: Code parses and executes.
Note that if expressions appear to be implemented correctly as intended:
x = if rand < 0.5
return
else
return
end
# Expected: Rejected with SyntaxError.
# Actual: Rejected. (OK!)
2. Statement lists containing a void value expression¶
@matz (Yukihiro Matsumoto) also said that if a statements node contains a void value expression, the entire statement list should be treated as a void value expression, even if the void expression is not the last one.
Therefore, the following code is currently accepted but should be rejected:
x = begin
return
"NG"
end
# Expected: Rejected with SyntaxError.
# Actual: Code parses and executes.
The same applies to branches within other expressions:
x = if rand < 0.5
return
"NG"
else
return
end
# Expected: Rejected with SyntaxError.
# Actual: Code parses and executes.
Note that if a return statement is inside a branch, that statement cannot be considered a void value expression, so the entire expression also cannot be a void value expression.
x = begin
return if true
"NG"
end
# Expected: Code parses and executes.
# Actual: Code parses and executes as expected. (OK!)
This is considered a bug, but if it causes practical compatibility issues, it may be necessary to plan a migration path or revisit the specifications.
(Both prism and parse.y need to be fixed, but I'll assign it to the prism team for now.)