diff --git a/snapshots/3.3-3.4/void_value.txt b/snapshots/3.3-3.4/void_value.txt new file mode 100644 index 0000000000..dac043ad8c --- /dev/null +++ b/snapshots/3.3-3.4/void_value.txt @@ -0,0 +1,151 @@ +@ ProgramNode (location: (1,0)-(17,3)) +├── flags: ∅ +├── locals: [:x] +└── statements: + @ StatementsNode (location: (1,0)-(17,3)) + ├── flags: ∅ + └── body: (length: 3) + ├── @ LocalVariableWriteNode (location: (1,0)-(7,3)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (1,0)-(1,1) = "x" + │ ├── value: + │ │ @ BeginNode (location: (1,4)-(7,3)) + │ │ ├── flags: ∅ + │ │ ├── begin_keyword_loc: (1,4)-(1,9) = "begin" + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (2,2)-(2,5)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (2,2)-(2,5)) + │ │ │ ├── flags: newline, variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :foo + │ │ │ ├── message_loc: (2,2)-(2,5) = "foo" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── rescue_clause: + │ │ │ @ RescueNode (location: (3,0)-(4,8)) + │ │ │ ├── flags: ∅ + │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" + │ │ │ ├── exceptions: (length: 0) + │ │ │ ├── operator_loc: ∅ + │ │ │ ├── reference: ∅ + │ │ │ ├── then_keyword_loc: ∅ + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (4,2)-(4,8)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (4,2)-(4,8)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (4,2)-(4,8) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ └── subsequent: ∅ + │ │ ├── else_clause: + │ │ │ @ ElseNode (location: (5,0)-(7,3)) + │ │ │ ├── flags: ∅ + │ │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else" + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (6,2)-(6,8)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (6,2)-(6,8)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (6,2)-(6,8) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ └── end_keyword_loc: (7,0)-(7,3) = "end" + │ │ ├── ensure_clause: ∅ + │ │ └── end_keyword_loc: (7,0)-(7,3) = "end" + │ └── operator_loc: (1,2)-(1,3) = "=" + ├── @ LocalVariableWriteNode (location: (9,0)-(12,3)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (9,0)-(9,1) = "x" + │ ├── value: + │ │ @ CaseNode (location: (9,4)-(12,3)) + │ │ ├── flags: ∅ + │ │ ├── predicate: ∅ + │ │ ├── conditions: (length: 1) + │ │ │ └── @ WhenNode (location: (10,2)-(10,20)) + │ │ │ ├── flags: ∅ + │ │ │ ├── keyword_loc: (10,2)-(10,6) = "when" + │ │ │ ├── conditions: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (10,7)-(10,8)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ ├── then_keyword_loc: (10,9)-(10,13) = "then" + │ │ │ └── statements: + │ │ │ @ StatementsNode (location: (10,14)-(10,20)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ ReturnNode (location: (10,14)-(10,20)) + │ │ │ ├── flags: newline + │ │ │ ├── keyword_loc: (10,14)-(10,20) = "return" + │ │ │ └── arguments: ∅ + │ │ ├── else_clause: + │ │ │ @ ElseNode (location: (11,2)-(12,3)) + │ │ │ ├── flags: ∅ + │ │ │ ├── else_keyword_loc: (11,2)-(11,6) = "else" + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (11,14)-(11,20)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (11,14)-(11,20)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (11,14)-(11,20) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ └── end_keyword_loc: (12,0)-(12,3) = "end" + │ │ ├── case_keyword_loc: (9,4)-(9,8) = "case" + │ │ └── end_keyword_loc: (12,0)-(12,3) = "end" + │ └── operator_loc: (9,2)-(9,3) = "=" + └── @ LocalVariableWriteNode (location: (14,0)-(17,3)) + ├── flags: newline + ├── name: :x + ├── depth: 0 + ├── name_loc: (14,0)-(14,1) = "x" + ├── value: + │ @ CaseMatchNode (location: (14,4)-(17,3)) + │ ├── flags: ∅ + │ ├── predicate: + │ │ @ IntegerNode (location: (14,9)-(14,10)) + │ │ ├── flags: static_literal, decimal + │ │ └── value: 1 + │ ├── conditions: (length: 1) + │ │ └── @ InNode (location: (15,2)-(15,18)) + │ │ ├── flags: ∅ + │ │ ├── pattern: + │ │ │ @ IntegerNode (location: (15,5)-(15,6)) + │ │ │ ├── flags: static_literal, decimal + │ │ │ └── value: 2 + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (15,12)-(15,18)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ ReturnNode (location: (15,12)-(15,18)) + │ │ │ ├── flags: newline + │ │ │ ├── keyword_loc: (15,12)-(15,18) = "return" + │ │ │ └── arguments: ∅ + │ │ ├── in_loc: (15,2)-(15,4) = "in" + │ │ └── then_loc: (15,7)-(15,11) = "then" + │ ├── else_clause: + │ │ @ ElseNode (location: (16,2)-(17,3)) + │ │ ├── flags: ∅ + │ │ ├── else_keyword_loc: (16,2)-(16,6) = "else" + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (16,12)-(16,18)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ ReturnNode (location: (16,12)-(16,18)) + │ │ │ ├── flags: newline + │ │ │ ├── keyword_loc: (16,12)-(16,18) = "return" + │ │ │ └── arguments: ∅ + │ │ └── end_keyword_loc: (17,0)-(17,3) = "end" + │ ├── case_keyword_loc: (14,4)-(14,8) = "case" + │ └── end_keyword_loc: (17,0)-(17,3) = "end" + └── operator_loc: (14,2)-(14,3) = "=" diff --git a/snapshots/4.0/void_value.txt b/snapshots/4.0/void_value.txt new file mode 100644 index 0000000000..b6ca0253aa --- /dev/null +++ b/snapshots/4.0/void_value.txt @@ -0,0 +1,59 @@ +@ ProgramNode (location: (1,0)-(7,3)) +├── flags: ∅ +├── locals: [:x] +└── statements: + @ StatementsNode (location: (1,0)-(7,3)) + ├── flags: ∅ + └── body: (length: 1) + └── @ LocalVariableWriteNode (location: (1,0)-(7,3)) + ├── flags: newline + ├── name: :x + ├── depth: 0 + ├── name_loc: (1,0)-(1,1) = "x" + ├── value: + │ @ BeginNode (location: (1,4)-(7,3)) + │ ├── flags: ∅ + │ ├── begin_keyword_loc: (1,4)-(1,9) = "begin" + │ ├── statements: + │ │ @ StatementsNode (location: (2,2)-(2,8)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ReturnNode (location: (2,2)-(2,8)) + │ │ ├── flags: newline + │ │ ├── keyword_loc: (2,2)-(2,8) = "return" + │ │ └── arguments: ∅ + │ ├── rescue_clause: + │ │ @ RescueNode (location: (3,0)-(4,6)) + │ │ ├── flags: ∅ + │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" + │ │ ├── exceptions: (length: 0) + │ │ ├── operator_loc: ∅ + │ │ ├── reference: ∅ + │ │ ├── then_keyword_loc: ∅ + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (4,2)-(4,6)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ StringNode (location: (4,2)-(4,6)) + │ │ │ ├── flags: newline + │ │ │ ├── opening_loc: (4,2)-(4,3) = "\"" + │ │ │ ├── content_loc: (4,3)-(4,5) = "OK" + │ │ │ ├── closing_loc: (4,5)-(4,6) = "\"" + │ │ │ └── unescaped: "OK" + │ │ └── subsequent: ∅ + │ ├── else_clause: + │ │ @ ElseNode (location: (5,0)-(7,3)) + │ │ ├── flags: ∅ + │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else" + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (6,2)-(6,8)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ ReturnNode (location: (6,2)-(6,8)) + │ │ │ ├── flags: newline + │ │ │ ├── keyword_loc: (6,2)-(6,8) = "return" + │ │ │ └── arguments: ∅ + │ │ └── end_keyword_loc: (7,0)-(7,3) = "end" + │ ├── ensure_clause: ∅ + │ └── end_keyword_loc: (7,0)-(7,3) = "end" + └── operator_loc: (1,2)-(1,3) = "=" diff --git a/snapshots/non_void_value.txt b/snapshots/non_void_value.txt new file mode 100644 index 0000000000..6ddcecbdf5 --- /dev/null +++ b/snapshots/non_void_value.txt @@ -0,0 +1,232 @@ +@ ProgramNode (location: (1,0)-(26,3)) +├── flags: ∅ +├── locals: [:x] +└── statements: + @ StatementsNode (location: (1,0)-(26,3)) + ├── flags: ∅ + └── body: (length: 4) + ├── @ LocalVariableWriteNode (location: (1,0)-(4,3)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (1,0)-(1,1) = "x" + │ ├── value: + │ │ @ BeginNode (location: (1,4)-(4,3)) + │ │ ├── flags: ∅ + │ │ ├── begin_keyword_loc: (1,4)-(1,9) = "begin" + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (2,2)-(3,15)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 2) + │ │ │ ├── @ IfNode (location: (2,2)-(2,16)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── if_keyword_loc: (2,9)-(2,11) = "if" + │ │ │ │ ├── predicate: + │ │ │ │ │ @ TrueNode (location: (2,12)-(2,16)) + │ │ │ │ │ └── flags: static_literal + │ │ │ │ ├── then_keyword_loc: ∅ + │ │ │ │ ├── statements: + │ │ │ │ │ @ StatementsNode (location: (2,2)-(2,8)) + │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ └── body: (length: 1) + │ │ │ │ │ └── @ ReturnNode (location: (2,2)-(2,8)) + │ │ │ │ │ ├── flags: newline + │ │ │ │ │ ├── keyword_loc: (2,2)-(2,8) = "return" + │ │ │ │ │ └── arguments: ∅ + │ │ │ │ ├── subsequent: ∅ + │ │ │ │ └── end_keyword_loc: ∅ + │ │ │ └── @ StringNode (location: (3,2)-(3,15)) + │ │ │ ├── flags: newline + │ │ │ ├── opening_loc: (3,2)-(3,3) = "\"" + │ │ │ ├── content_loc: (3,3)-(3,14) = "conditional" + │ │ │ ├── closing_loc: (3,14)-(3,15) = "\"" + │ │ │ └── unescaped: "conditional" + │ │ ├── rescue_clause: ∅ + │ │ ├── else_clause: ∅ + │ │ ├── ensure_clause: ∅ + │ │ └── end_keyword_loc: (4,0)-(4,3) = "end" + │ └── operator_loc: (1,2)-(1,3) = "=" + ├── @ LocalVariableWriteNode (location: (6,0)-(11,3)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (6,0)-(6,1) = "x" + │ ├── value: + │ │ @ IfNode (location: (6,4)-(11,3)) + │ │ ├── flags: newline + │ │ ├── if_keyword_loc: (6,4)-(6,6) = "if" + │ │ ├── predicate: + │ │ │ @ TrueNode (location: (6,7)-(6,11)) + │ │ │ └── flags: static_literal + │ │ ├── then_keyword_loc: ∅ + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (7,2)-(8,15)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 2) + │ │ │ ├── @ IfNode (location: (7,2)-(7,16)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── if_keyword_loc: (7,9)-(7,11) = "if" + │ │ │ │ ├── predicate: + │ │ │ │ │ @ TrueNode (location: (7,12)-(7,16)) + │ │ │ │ │ └── flags: static_literal + │ │ │ │ ├── then_keyword_loc: ∅ + │ │ │ │ ├── statements: + │ │ │ │ │ @ StatementsNode (location: (7,2)-(7,8)) + │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ └── body: (length: 1) + │ │ │ │ │ └── @ ReturnNode (location: (7,2)-(7,8)) + │ │ │ │ │ ├── flags: newline + │ │ │ │ │ ├── keyword_loc: (7,2)-(7,8) = "return" + │ │ │ │ │ └── arguments: ∅ + │ │ │ │ ├── subsequent: ∅ + │ │ │ │ └── end_keyword_loc: ∅ + │ │ │ └── @ StringNode (location: (8,2)-(8,15)) + │ │ │ ├── flags: newline + │ │ │ ├── opening_loc: (8,2)-(8,3) = "\"" + │ │ │ ├── content_loc: (8,3)-(8,14) = "conditional" + │ │ │ ├── closing_loc: (8,14)-(8,15) = "\"" + │ │ │ └── unescaped: "conditional" + │ │ ├── subsequent: + │ │ │ @ ElseNode (location: (9,0)-(11,3)) + │ │ │ ├── flags: ∅ + │ │ │ ├── else_keyword_loc: (9,0)-(9,4) = "else" + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (10,2)-(10,8)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (10,2)-(10,8)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (10,2)-(10,8) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ └── end_keyword_loc: (11,0)-(11,3) = "end" + │ │ └── end_keyword_loc: (11,0)-(11,3) = "end" + │ └── operator_loc: (6,2)-(6,3) = "=" + ├── @ LocalVariableWriteNode (location: (13,0)-(18,3)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (13,0)-(13,1) = "x" + │ ├── value: + │ │ @ IfNode (location: (13,4)-(18,3)) + │ │ ├── flags: newline + │ │ ├── if_keyword_loc: (13,4)-(13,6) = "if" + │ │ ├── predicate: + │ │ │ @ TrueNode (location: (13,7)-(13,11)) + │ │ │ └── flags: static_literal + │ │ ├── then_keyword_loc: ∅ + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (14,2)-(14,16)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ IfNode (location: (14,2)-(14,16)) + │ │ │ ├── flags: newline + │ │ │ ├── if_keyword_loc: (14,9)-(14,11) = "if" + │ │ │ ├── predicate: + │ │ │ │ @ TrueNode (location: (14,12)-(14,16)) + │ │ │ │ └── flags: static_literal + │ │ │ ├── then_keyword_loc: ∅ + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (14,2)-(14,8)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (14,2)-(14,8)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (14,2)-(14,8) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ ├── subsequent: ∅ + │ │ │ └── end_keyword_loc: ∅ + │ │ ├── subsequent: + │ │ │ @ ElseNode (location: (15,0)-(18,3)) + │ │ │ ├── flags: ∅ + │ │ │ ├── else_keyword_loc: (15,0)-(15,4) = "else" + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (16,2)-(17,15)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 2) + │ │ │ │ ├── @ IfNode (location: (16,2)-(16,16)) + │ │ │ │ │ ├── flags: newline + │ │ │ │ │ ├── if_keyword_loc: (16,9)-(16,11) = "if" + │ │ │ │ │ ├── predicate: + │ │ │ │ │ │ @ TrueNode (location: (16,12)-(16,16)) + │ │ │ │ │ │ └── flags: static_literal + │ │ │ │ │ ├── then_keyword_loc: ∅ + │ │ │ │ │ ├── statements: + │ │ │ │ │ │ @ StatementsNode (location: (16,2)-(16,8)) + │ │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ │ └── body: (length: 1) + │ │ │ │ │ │ └── @ ReturnNode (location: (16,2)-(16,8)) + │ │ │ │ │ │ ├── flags: newline + │ │ │ │ │ │ ├── keyword_loc: (16,2)-(16,8) = "return" + │ │ │ │ │ │ └── arguments: ∅ + │ │ │ │ │ ├── subsequent: ∅ + │ │ │ │ │ └── end_keyword_loc: ∅ + │ │ │ │ └── @ StringNode (location: (17,2)-(17,15)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── opening_loc: (17,2)-(17,3) = "\"" + │ │ │ │ ├── content_loc: (17,3)-(17,14) = "conditional" + │ │ │ │ ├── closing_loc: (17,14)-(17,15) = "\"" + │ │ │ │ └── unescaped: "conditional" + │ │ │ └── end_keyword_loc: (18,0)-(18,3) = "end" + │ │ └── end_keyword_loc: (18,0)-(18,3) = "end" + │ └── operator_loc: (13,2)-(13,3) = "=" + └── @ LocalVariableWriteNode (location: (20,0)-(26,3)) + ├── flags: newline + ├── name: :x + ├── depth: 0 + ├── name_loc: (20,0)-(20,1) = "x" + ├── value: + │ @ CaseNode (location: (20,4)-(26,3)) + │ ├── flags: ∅ + │ ├── predicate: ∅ + │ ├── conditions: (length: 1) + │ │ └── @ WhenNode (location: (21,2)-(23,17)) + │ │ ├── flags: ∅ + │ │ ├── keyword_loc: (21,2)-(21,6) = "when" + │ │ ├── conditions: (length: 1) + │ │ │ └── @ IntegerNode (location: (21,7)-(21,8)) + │ │ │ ├── flags: static_literal, decimal + │ │ │ └── value: 1 + │ │ ├── then_keyword_loc: ∅ + │ │ └── statements: + │ │ @ StatementsNode (location: (22,4)-(23,17)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 2) + │ │ ├── @ IfNode (location: (22,4)-(22,18)) + │ │ │ ├── flags: newline + │ │ │ ├── if_keyword_loc: (22,11)-(22,13) = "if" + │ │ │ ├── predicate: + │ │ │ │ @ TrueNode (location: (22,14)-(22,18)) + │ │ │ │ └── flags: static_literal + │ │ │ ├── then_keyword_loc: ∅ + │ │ │ ├── statements: + │ │ │ │ @ StatementsNode (location: (22,4)-(22,10)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ ReturnNode (location: (22,4)-(22,10)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── keyword_loc: (22,4)-(22,10) = "return" + │ │ │ │ └── arguments: ∅ + │ │ │ ├── subsequent: ∅ + │ │ │ └── end_keyword_loc: ∅ + │ │ └── @ StringNode (location: (23,4)-(23,17)) + │ │ ├── flags: newline + │ │ ├── opening_loc: (23,4)-(23,5) = "\"" + │ │ ├── content_loc: (23,5)-(23,16) = "conditional" + │ │ ├── closing_loc: (23,16)-(23,17) = "\"" + │ │ └── unescaped: "conditional" + │ ├── else_clause: + │ │ @ ElseNode (location: (24,2)-(26,3)) + │ │ ├── flags: ∅ + │ │ ├── else_keyword_loc: (24,2)-(24,6) = "else" + │ │ ├── statements: + │ │ │ @ StatementsNode (location: (25,4)-(25,10)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ ReturnNode (location: (25,4)-(25,10)) + │ │ │ ├── flags: newline + │ │ │ ├── keyword_loc: (25,4)-(25,10) = "return" + │ │ │ └── arguments: ∅ + │ │ └── end_keyword_loc: (26,0)-(26,3) = "end" + │ ├── case_keyword_loc: (20,4)-(20,8) = "case" + │ └── end_keyword_loc: (26,0)-(26,3) = "end" + └── operator_loc: (20,2)-(20,3) = "=" diff --git a/src/prism.c b/src/prism.c index a87932f1b7..8152aa283b 100644 --- a/src/prism.c +++ b/src/prism.c @@ -1024,6 +1024,13 @@ pm_parser_optional_constant_id_token(pm_parser_t *parser, const pm_token_t *toke return token->type == PM_TOKEN_NOT_PROVIDED ? 0 : pm_parser_constant_id_token(parser, token); } +/** + * 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. @@ -1035,12 +1042,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; @@ -1060,25 +1062,36 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { node = (pm_node_t *) 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_0) { + if (cast->statements == NULL) return NULL; - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); - if (vn == NULL) return NULL; - if (void_node == NULL) void_node = vn; + pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) 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, (pm_node_t *) rescue_clause->statements); + if (vn == NULL) { + // https://bugs.ruby-lang.org/issues/21669 + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + return NULL; + } void_node = NULL; break; } - if (void_node == NULL) { - void_node = vn; - } } if (cast->else_clause != NULL) { node = (pm_node_t *) cast->else_clause; + + // https://bugs.ruby-lang.org/issues/21669 + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + pm_node_t *vn = pm_check_value_expression(parser, node); + if (vn != NULL) return vn; + } } else { return void_node; } @@ -1088,6 +1101,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_0) { + 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, (pm_node_t *) cast->statements); + if (vn == NULL) return NULL; + if (void_node == NULL) void_node = vn; + } + + node = (pm_node_t *) cast->else_clause; + break; + } + case PM_CASE_MATCH_NODE: { + // https://bugs.ruby-lang.org/issues/21669 + if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_0) { + 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, (pm_node_t *) cast->statements); + if (vn == NULL) return NULL; + if (void_node == NULL) void_node = vn; + } + + node = (pm_node_t *) cast->else_clause; + break; + } case PM_ENSURE_NODE: { pm_ensure_node_t *cast = (pm_ensure_node_t *) node; node = (pm_node_t *) cast->statements; @@ -1100,6 +1157,20 @@ 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_0) { + 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; + break; + default: break; + } + } + } + node = cast->body.nodes[cast->body.size - 1]; break; } diff --git a/test/prism/errors/3.4-3.4/void_value.txt b/test/prism/errors/3.4-3.4/void_value.txt new file mode 100644 index 0000000000..c03139bb05 --- /dev/null +++ b/test/prism/errors/3.4-3.4/void_value.txt @@ -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 + diff --git a/test/prism/errors/4.0/void_value.txt b/test/prism/errors/4.0/void_value.txt new file mode 100644 index 0000000000..6f34b0c3b0 --- /dev/null +++ b/test/prism/errors/4.0/void_value.txt @@ -0,0 +1,30 @@ +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 + diff --git a/test/prism/errors/void_value_expression_in_begin_statement.txt b/test/prism/errors/void_value_expression_in_begin_statement.txt index aa8f1ded96..fb968a12e1 100644 --- a/test/prism/errors/void_value_expression_in_begin_statement.txt +++ b/test/prism/errors/void_value_expression_in_begin_statement.txt @@ -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 diff --git a/test/prism/fixtures/3.3-3.4/void_value.txt b/test/prism/fixtures/3.3-3.4/void_value.txt new file mode 100644 index 0000000000..b723d6a0fd --- /dev/null +++ b/test/prism/fixtures/3.3-3.4/void_value.txt @@ -0,0 +1,17 @@ +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 diff --git a/test/prism/fixtures/4.0/void_value.txt b/test/prism/fixtures/4.0/void_value.txt new file mode 100644 index 0000000000..915112d623 --- /dev/null +++ b/test/prism/fixtures/4.0/void_value.txt @@ -0,0 +1,7 @@ +x = begin + return +rescue + "OK" +else + return +end diff --git a/test/prism/fixtures/non_void_value.txt b/test/prism/fixtures/non_void_value.txt new file mode 100644 index 0000000000..9a4522af8b --- /dev/null +++ b/test/prism/fixtures/non_void_value.txt @@ -0,0 +1,26 @@ +x = begin + return if true + "conditional" +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 diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 2aebb18477..8635ac354f 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -37,6 +37,7 @@ class FixturesTest < TestCase # Leaving these out until they are supported by parse.y. except << "4.0/leading_logical.txt" except << "4.0/endless_methods_command_call.txt" + except << "4.0/void_value.txt" # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 19dd845d75..49df97d137 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -48,6 +48,9 @@ class LexTest < TestCase # https://bugs.ruby-lang.org/issues/17398#note-12 except << "4.0/endless_methods_command_call.txt" + # https://bugs.ruby-lang.org/issues/21669 + except << "4.0/void_value.txt" + # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 814c9a9978..83a45a007f 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -40,6 +40,7 @@ class LocalsTest < TestCase # Leaving these out until they are supported by parse.y. "4.0/leading_logical.txt", "4.0/endless_methods_command_call.txt", + "4.0/void_value.txt", "command_method_call_2.txt" ] diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 648c44e77a..c2b419fb44 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -73,6 +73,7 @@ class ParserTest < TestCase # Ruby >= 4.0 specific syntax "4.0/endless_methods_command_call.txt", + "4.0/void_value.txt", # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 400139acc0..40b275e55a 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -36,12 +36,17 @@ class RipperTest < TestCase "3.3-3.3/keyword_args_in_array_assignment.txt", "3.3-3.3/return_in_sclass.txt", + "3.3-3.4/void_value.txt", + # https://bugs.ruby-lang.org/issues/20478 "3.4/circular_parameters.txt", # https://bugs.ruby-lang.org/issues/17398#note-12 "4.0/endless_methods_command_call.txt", + # https://bugs.ruby-lang.org/issues/21669 + "4.0/void_value.txt", + # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index fae5077e20..e797710064 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -82,6 +82,8 @@ class RubyParserTest < TestCase "3.3-3.3/keyword_args_in_array_assignment.txt", "3.3-3.3/return_in_sclass.txt", + "3.3-3.4/void_value.txt", + "3.4/circular_parameters.txt", "4.0/endless_methods_command_call.txt",