From 3a38b192e399a1c5e993906e269fbd9ceb367a00 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 29 Aug 2025 22:35:51 +0200 Subject: [PATCH] Reject some cases with `return` and command calls The same also applies to `break`/`next`. https://bugs.ruby-lang.org/issues/21540 --- lib/prism/translation/parser/lexer.rb | 2 +- snapshots/break.txt | 195 ++++++++++++++++++------- snapshots/next.txt | 141 ++++++++++++++---- snapshots/return.txt | 91 +++++++++--- src/prism.c | 6 + test/prism/errors/command_calls_32.txt | 19 +++ test/prism/fixtures/break.txt | 4 + test/prism/fixtures/next.txt | 4 + test/prism/fixtures/return.txt | 3 + 9 files changed, 367 insertions(+), 98 deletions(-) create mode 100644 test/prism/errors/command_calls_32.txt diff --git a/lib/prism/translation/parser/lexer.rb b/lib/prism/translation/parser/lexer.rb index 222df74559..75c48ef667 100644 --- a/lib/prism/translation/parser/lexer.rb +++ b/lib/prism/translation/parser/lexer.rb @@ -203,7 +203,7 @@ class Lexer # The following token types are listed as those classified as `tLPAREN`. LPAREN_CONVERSION_TOKEN_TYPES = Set.new([ :kBREAK, :tCARET, :kCASE, :tDIVIDE, :kFOR, :kIF, :kNEXT, :kRETURN, :kUNTIL, :kWHILE, :tAMPER, :tANDOP, :tBANG, :tCOMMA, :tDOT2, :tDOT3, - :tEQL, :tLPAREN, :tLPAREN2, :tLPAREN_ARG, :tLSHFT, :tNL, :tOP_ASGN, :tOROP, :tPIPE, :tSEMI, :tSTRING_DBEG, :tUMINUS, :tUPLUS + :tEQL, :tLPAREN, :tLPAREN2, :tLPAREN_ARG, :tLSHFT, :tNL, :tOP_ASGN, :tOROP, :tPIPE, :tSEMI, :tSTRING_DBEG, :tUMINUS, :tUPLUS, :tLCURLY ]) # Types of tokens that are allowed to continue a method call with comments in-between. diff --git a/snapshots/break.txt b/snapshots/break.txt index 6e7bc9a2ee..7e5b8da0aa 100644 --- a/snapshots/break.txt +++ b/snapshots/break.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(29,21)) +@ ProgramNode (location: (1,0)-(33,21)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(29,21)) + @ StatementsNode (location: (1,0)-(33,21)) ├── flags: ∅ - └── body: (length: 13) + └── body: (length: 15) ├── @ CallNode (location: (1,0)-(1,13)) │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ @@ -343,76 +343,167 @@ │ │ └── keyword_loc: (21,6)-(21,11) = "break" │ ├── opening_loc: (21,4)-(21,5) = "{" │ └── closing_loc: (21,15)-(21,16) = "}" - ├── @ CallNode (location: (23,0)-(23,22)) + ├── @ CallNode (location: (23,0)-(23,17)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :tap + │ ├── message_loc: (23,0)-(23,3) = "tap" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: + │ @ BlockNode (location: (23,4)-(23,17)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (23,6)-(23,15)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ParenthesesNode (location: (23,6)-(23,15)) + │ │ ├── flags: newline + │ │ ├── body: + │ │ │ @ StatementsNode (location: (23,7)-(23,14)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ BreakNode (location: (23,7)-(23,14)) + │ │ │ ├── flags: newline + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (23,13)-(23,14)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (23,13)-(23,14)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ └── keyword_loc: (23,7)-(23,12) = "break" + │ │ ├── opening_loc: (23,6)-(23,7) = "(" + │ │ └── closing_loc: (23,14)-(23,15) = ")" + │ ├── opening_loc: (23,4)-(23,5) = "{" + │ └── closing_loc: (23,16)-(23,17) = "}" + ├── @ CallNode (location: (25,0)-(25,24)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :tap + │ ├── message_loc: (25,0)-(25,3) = "tap" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: + │ @ BlockNode (location: (25,4)-(25,24)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (25,6)-(25,22)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ AndNode (location: (25,6)-(25,22)) + │ │ ├── flags: newline + │ │ ├── left: + │ │ │ @ CallNode (location: (25,6)-(25,9)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :foo + │ │ │ ├── message_loc: (25,6)-(25,9) = "foo" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── right: + │ │ │ @ ParenthesesNode (location: (25,13)-(25,22)) + │ │ │ ├── flags: ∅ + │ │ │ ├── body: + │ │ │ │ @ StatementsNode (location: (25,14)-(25,21)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ BreakNode (location: (25,14)-(25,21)) + │ │ │ │ ├── flags: newline + │ │ │ │ ├── arguments: + │ │ │ │ │ @ ArgumentsNode (location: (25,20)-(25,21)) + │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ └── arguments: (length: 1) + │ │ │ │ │ └── @ IntegerNode (location: (25,20)-(25,21)) + │ │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ │ └── value: 1 + │ │ │ │ └── keyword_loc: (25,14)-(25,19) = "break" + │ │ │ ├── opening_loc: (25,13)-(25,14) = "(" + │ │ │ └── closing_loc: (25,21)-(25,22) = ")" + │ │ └── operator_loc: (25,10)-(25,12) = "&&" + │ ├── opening_loc: (25,4)-(25,5) = "{" + │ └── closing_loc: (25,23)-(25,24) = "}" + ├── @ CallNode (location: (27,0)-(27,22)) │ ├── flags: newline │ ├── receiver: - │ │ @ CallNode (location: (23,0)-(23,16)) + │ │ @ CallNode (location: (27,0)-(27,16)) │ │ ├── flags: ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo - │ │ ├── message_loc: (23,0)-(23,3) = "foo" + │ │ ├── message_loc: (27,0)-(27,3) = "foo" │ │ ├── opening_loc: ∅ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: - │ │ @ BlockNode (location: (23,4)-(23,16)) + │ │ @ BlockNode (location: (27,4)-(27,16)) │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: - │ │ │ @ StatementsNode (location: (23,6)-(23,14)) + │ │ │ @ StatementsNode (location: (27,6)-(27,14)) │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) - │ │ │ └── @ BreakNode (location: (23,6)-(23,14)) + │ │ │ └── @ BreakNode (location: (27,6)-(27,14)) │ │ │ ├── flags: newline │ │ │ ├── arguments: - │ │ │ │ @ ArgumentsNode (location: (23,12)-(23,14)) + │ │ │ │ @ ArgumentsNode (location: (27,12)-(27,14)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) - │ │ │ │ └── @ IntegerNode (location: (23,12)-(23,14)) + │ │ │ │ └── @ IntegerNode (location: (27,12)-(27,14)) │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 - │ │ │ └── keyword_loc: (23,6)-(23,11) = "break" - │ │ ├── opening_loc: (23,4)-(23,5) = "{" - │ │ └── closing_loc: (23,15)-(23,16) = "}" + │ │ │ └── keyword_loc: (27,6)-(27,11) = "break" + │ │ ├── opening_loc: (27,4)-(27,5) = "{" + │ │ └── closing_loc: (27,15)-(27,16) = "}" │ ├── call_operator_loc: ∅ │ ├── name: :== - │ ├── message_loc: (23,17)-(23,19) = "==" + │ ├── message_loc: (27,17)-(27,19) = "==" │ ├── opening_loc: ∅ │ ├── arguments: - │ │ @ ArgumentsNode (location: (23,20)-(23,22)) + │ │ @ ArgumentsNode (location: (27,20)-(27,22)) │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) - │ │ └── @ IntegerNode (location: (23,20)-(23,22)) + │ │ └── @ IntegerNode (location: (27,20)-(27,22)) │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ - ├── @ CallNode (location: (25,0)-(25,23)) + ├── @ CallNode (location: (29,0)-(29,23)) │ ├── flags: newline │ ├── receiver: - │ │ @ CallNode (location: (25,0)-(25,17)) + │ │ @ CallNode (location: (29,0)-(29,17)) │ │ ├── flags: ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo - │ │ ├── message_loc: (25,0)-(25,3) = "foo" + │ │ ├── message_loc: (29,0)-(29,3) = "foo" │ │ ├── opening_loc: ∅ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: - │ │ @ BlockNode (location: (25,4)-(25,17)) + │ │ @ BlockNode (location: (29,4)-(29,17)) │ │ ├── flags: ∅ │ │ ├── locals: [:a] │ │ ├── parameters: - │ │ │ @ BlockParametersNode (location: (25,6)-(25,9)) + │ │ │ @ BlockParametersNode (location: (29,6)-(29,9)) │ │ │ ├── flags: ∅ │ │ │ ├── parameters: - │ │ │ │ @ ParametersNode (location: (25,7)-(25,8)) + │ │ │ │ @ ParametersNode (location: (29,7)-(29,8)) │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) - │ │ │ │ │ └── @ RequiredParameterNode (location: (25,7)-(25,8)) + │ │ │ │ │ └── @ RequiredParameterNode (location: (29,7)-(29,8)) │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :a │ │ │ │ ├── optionals: (length: 0) @@ -422,80 +513,80 @@ │ │ │ │ ├── keyword_rest: ∅ │ │ │ │ └── block: ∅ │ │ │ ├── locals: (length: 0) - │ │ │ ├── opening_loc: (25,6)-(25,7) = "|" - │ │ │ └── closing_loc: (25,8)-(25,9) = "|" + │ │ │ ├── opening_loc: (29,6)-(29,7) = "|" + │ │ │ └── closing_loc: (29,8)-(29,9) = "|" │ │ ├── body: - │ │ │ @ StatementsNode (location: (25,10)-(25,15)) + │ │ │ @ StatementsNode (location: (29,10)-(29,15)) │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) - │ │ │ └── @ BreakNode (location: (25,10)-(25,15)) + │ │ │ └── @ BreakNode (location: (29,10)-(29,15)) │ │ │ ├── flags: newline │ │ │ ├── arguments: ∅ - │ │ │ └── keyword_loc: (25,10)-(25,15) = "break" - │ │ ├── opening_loc: (25,4)-(25,5) = "{" - │ │ └── closing_loc: (25,16)-(25,17) = "}" + │ │ │ └── keyword_loc: (29,10)-(29,15) = "break" + │ │ ├── opening_loc: (29,4)-(29,5) = "{" + │ │ └── closing_loc: (29,16)-(29,17) = "}" │ ├── call_operator_loc: ∅ │ ├── name: :== - │ ├── message_loc: (25,18)-(25,20) = "==" + │ ├── message_loc: (29,18)-(29,20) = "==" │ ├── opening_loc: ∅ │ ├── arguments: - │ │ @ ArgumentsNode (location: (25,21)-(25,23)) + │ │ @ ArgumentsNode (location: (29,21)-(29,23)) │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) - │ │ └── @ IntegerNode (location: (25,21)-(25,23)) + │ │ └── @ IntegerNode (location: (29,21)-(29,23)) │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ - ├── @ WhileNode (location: (27,0)-(27,21)) + ├── @ WhileNode (location: (31,0)-(31,21)) │ ├── flags: newline - │ ├── keyword_loc: (27,0)-(27,5) = "while" + │ ├── keyword_loc: (31,0)-(31,5) = "while" │ ├── do_keyword_loc: ∅ - │ ├── closing_loc: (27,18)-(27,21) = "end" + │ ├── closing_loc: (31,18)-(31,21) = "end" │ ├── predicate: - │ │ @ AndNode (location: (27,6)-(27,16)) + │ │ @ AndNode (location: (31,6)-(31,16)) │ │ ├── flags: ∅ │ │ ├── left: - │ │ │ @ CallNode (location: (27,6)-(27,7)) + │ │ │ @ CallNode (location: (31,6)-(31,7)) │ │ │ ├── flags: variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :_ - │ │ │ ├── message_loc: (27,6)-(27,7) = "_" + │ │ │ ├── message_loc: (31,6)-(31,7) = "_" │ │ │ ├── opening_loc: ∅ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ ├── right: - │ │ │ @ BreakNode (location: (27,11)-(27,16)) + │ │ │ @ BreakNode (location: (31,11)-(31,16)) │ │ │ ├── flags: ∅ │ │ │ ├── arguments: ∅ - │ │ │ └── keyword_loc: (27,11)-(27,16) = "break" - │ │ └── operator_loc: (27,8)-(27,10) = "&&" + │ │ │ └── keyword_loc: (31,11)-(31,16) = "break" + │ │ └── operator_loc: (31,8)-(31,10) = "&&" │ └── statements: ∅ - └── @ UntilNode (location: (29,0)-(29,21)) + └── @ UntilNode (location: (33,0)-(33,21)) ├── flags: newline - ├── keyword_loc: (29,0)-(29,5) = "until" + ├── keyword_loc: (33,0)-(33,5) = "until" ├── do_keyword_loc: ∅ - ├── closing_loc: (29,18)-(29,21) = "end" + ├── closing_loc: (33,18)-(33,21) = "end" ├── predicate: - │ @ AndNode (location: (29,6)-(29,16)) + │ @ AndNode (location: (33,6)-(33,16)) │ ├── flags: ∅ │ ├── left: - │ │ @ CallNode (location: (29,6)-(29,7)) + │ │ @ CallNode (location: (33,6)-(33,7)) │ │ ├── flags: variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :_ - │ │ ├── message_loc: (29,6)-(29,7) = "_" + │ │ ├── message_loc: (33,6)-(33,7) = "_" │ │ ├── opening_loc: ∅ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── right: - │ │ @ BreakNode (location: (29,11)-(29,16)) + │ │ @ BreakNode (location: (33,11)-(33,16)) │ │ ├── flags: ∅ │ │ ├── arguments: ∅ - │ │ └── keyword_loc: (29,11)-(29,16) = "break" - │ └── operator_loc: (29,8)-(29,10) = "&&" + │ │ └── keyword_loc: (33,11)-(33,16) = "break" + │ └── operator_loc: (33,8)-(33,10) = "&&" └── statements: ∅ diff --git a/snapshots/next.txt b/snapshots/next.txt index f38323e4e7..bf8447e837 100644 --- a/snapshots/next.txt +++ b/snapshots/next.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(24,15)) +@ ProgramNode (location: (1,0)-(28,23)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(24,15)) + @ StatementsNode (location: (1,0)-(28,23)) ├── flags: ∅ - └── body: (length: 10) + └── body: (length: 12) ├── @ CallNode (location: (1,0)-(1,12)) │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ @@ -332,41 +332,132 @@ │ │ └── keyword_loc: (22,6)-(22,10) = "next" │ ├── opening_loc: (22,4)-(22,5) = "{" │ └── closing_loc: (22,13)-(22,14) = "}" - └── @ CallNode (location: (24,0)-(24,15)) + ├── @ CallNode (location: (24,0)-(24,15)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :tap + │ ├── message_loc: (24,0)-(24,3) = "tap" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: + │ @ BlockNode (location: (24,4)-(24,15)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (24,6)-(24,13)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ NextNode (location: (24,6)-(24,13)) + │ │ ├── flags: newline + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (24,10)-(24,13)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ ParenthesesNode (location: (24,10)-(24,13)) + │ │ │ ├── flags: ∅ + │ │ │ ├── body: + │ │ │ │ @ StatementsNode (location: (24,11)-(24,12)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── body: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (24,11)-(24,12)) + │ │ │ │ ├── flags: newline, static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ ├── opening_loc: (24,10)-(24,11) = "(" + │ │ │ └── closing_loc: (24,12)-(24,13) = ")" + │ │ └── keyword_loc: (24,6)-(24,10) = "next" + │ ├── opening_loc: (24,4)-(24,5) = "{" + │ └── closing_loc: (24,14)-(24,15) = "}" + ├── @ CallNode (location: (26,0)-(26,16)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :tap + │ ├── message_loc: (26,0)-(26,3) = "tap" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: + │ @ BlockNode (location: (26,4)-(26,16)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (26,6)-(26,14)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ParenthesesNode (location: (26,6)-(26,14)) + │ │ ├── flags: newline + │ │ ├── body: + │ │ │ @ StatementsNode (location: (26,7)-(26,13)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ NextNode (location: (26,7)-(26,13)) + │ │ │ ├── flags: newline + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (26,12)-(26,13)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (26,12)-(26,13)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ └── keyword_loc: (26,7)-(26,11) = "next" + │ │ ├── opening_loc: (26,6)-(26,7) = "(" + │ │ └── closing_loc: (26,13)-(26,14) = ")" + │ ├── opening_loc: (26,4)-(26,5) = "{" + │ └── closing_loc: (26,15)-(26,16) = "}" + └── @ CallNode (location: (28,0)-(28,23)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :tap - ├── message_loc: (24,0)-(24,3) = "tap" + ├── message_loc: (28,0)-(28,3) = "tap" ├── opening_loc: ∅ ├── arguments: ∅ ├── closing_loc: ∅ └── block: - @ BlockNode (location: (24,4)-(24,15)) + @ BlockNode (location: (28,4)-(28,23)) ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: - │ @ StatementsNode (location: (24,6)-(24,13)) + │ @ StatementsNode (location: (28,6)-(28,21)) │ ├── flags: ∅ │ └── body: (length: 1) - │ └── @ NextNode (location: (24,6)-(24,13)) + │ └── @ AndNode (location: (28,6)-(28,21)) │ ├── flags: newline - │ ├── arguments: - │ │ @ ArgumentsNode (location: (24,10)-(24,13)) + │ ├── left: + │ │ @ CallNode (location: (28,6)-(28,9)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :foo + │ │ ├── message_loc: (28,6)-(28,9) = "foo" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ └── block: ∅ + │ ├── right: + │ │ @ ParenthesesNode (location: (28,13)-(28,21)) │ │ ├── flags: ∅ - │ │ └── arguments: (length: 1) - │ │ └── @ ParenthesesNode (location: (24,10)-(24,13)) - │ │ ├── flags: ∅ - │ │ ├── body: - │ │ │ @ StatementsNode (location: (24,11)-(24,12)) - │ │ │ ├── flags: ∅ - │ │ │ └── body: (length: 1) - │ │ │ └── @ IntegerNode (location: (24,11)-(24,12)) - │ │ │ ├── flags: newline, static_literal, decimal - │ │ │ └── value: 1 - │ │ ├── opening_loc: (24,10)-(24,11) = "(" - │ │ └── closing_loc: (24,12)-(24,13) = ")" - │ └── keyword_loc: (24,6)-(24,10) = "next" - ├── opening_loc: (24,4)-(24,5) = "{" - └── closing_loc: (24,14)-(24,15) = "}" + │ │ ├── body: + │ │ │ @ StatementsNode (location: (28,14)-(28,20)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ NextNode (location: (28,14)-(28,20)) + │ │ │ ├── flags: newline + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (28,19)-(28,20)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (28,19)-(28,20)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ └── keyword_loc: (28,14)-(28,18) = "next" + │ │ ├── opening_loc: (28,13)-(28,14) = "(" + │ │ └── closing_loc: (28,20)-(28,21) = ")" + │ └── operator_loc: (28,10)-(28,12) = "&&" + ├── opening_loc: (28,4)-(28,5) = "{" + └── closing_loc: (28,22)-(28,23) = "}" diff --git a/snapshots/return.txt b/snapshots/return.txt index ff297438b3..6fb103e99c 100644 --- a/snapshots/return.txt +++ b/snapshots/return.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(23,9)) +@ ProgramNode (location: (1,0)-(27,17)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(23,9)) + @ StatementsNode (location: (1,0)-(27,17)) ├── flags: ∅ - └── body: (length: 10) + └── body: (length: 12) ├── @ ReturnNode (location: (1,0)-(1,6)) │ ├── flags: newline │ ├── keyword_loc: (1,0)-(1,6) = "return" @@ -159,21 +159,72 @@ │ ├── body: ∅ │ ├── opening_loc: (21,6)-(21,7) = "(" │ └── closing_loc: (21,7)-(21,8) = ")" - └── @ ReturnNode (location: (23,0)-(23,9)) + ├── @ ReturnNode (location: (23,0)-(23,9)) + │ ├── flags: newline + │ ├── keyword_loc: (23,0)-(23,6) = "return" + │ └── arguments: + │ @ ArgumentsNode (location: (23,6)-(23,9)) + │ ├── flags: ∅ + │ └── arguments: (length: 1) + │ └── @ ParenthesesNode (location: (23,6)-(23,9)) + │ ├── flags: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (23,7)-(23,8)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ IntegerNode (location: (23,7)-(23,8)) + │ │ ├── flags: newline, static_literal, decimal + │ │ └── value: 1 + │ ├── opening_loc: (23,6)-(23,7) = "(" + │ └── closing_loc: (23,8)-(23,9) = ")" + ├── @ ParenthesesNode (location: (25,0)-(25,10)) + │ ├── flags: newline + │ ├── body: + │ │ @ StatementsNode (location: (25,1)-(25,9)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ReturnNode (location: (25,1)-(25,9)) + │ │ ├── flags: newline + │ │ ├── keyword_loc: (25,1)-(25,7) = "return" + │ │ └── arguments: + │ │ @ ArgumentsNode (location: (25,8)-(25,9)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ IntegerNode (location: (25,8)-(25,9)) + │ │ ├── flags: static_literal, decimal + │ │ └── value: 1 + │ ├── opening_loc: (25,0)-(25,1) = "(" + │ └── closing_loc: (25,9)-(25,10) = ")" + └── @ AndNode (location: (27,0)-(27,17)) ├── flags: newline - ├── keyword_loc: (23,0)-(23,6) = "return" - └── arguments: - @ ArgumentsNode (location: (23,6)-(23,9)) - ├── flags: ∅ - └── arguments: (length: 1) - └── @ ParenthesesNode (location: (23,6)-(23,9)) - ├── flags: ∅ - ├── body: - │ @ StatementsNode (location: (23,7)-(23,8)) - │ ├── flags: ∅ - │ └── body: (length: 1) - │ └── @ IntegerNode (location: (23,7)-(23,8)) - │ ├── flags: newline, static_literal, decimal - │ └── value: 1 - ├── opening_loc: (23,6)-(23,7) = "(" - └── closing_loc: (23,8)-(23,9) = ")" + ├── left: + │ @ CallNode (location: (27,0)-(27,3)) + │ ├── flags: variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (27,0)-(27,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: ∅ + ├── right: + │ @ ParenthesesNode (location: (27,7)-(27,17)) + │ ├── flags: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (27,8)-(27,16)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ReturnNode (location: (27,8)-(27,16)) + │ │ ├── flags: newline + │ │ ├── keyword_loc: (27,8)-(27,14) = "return" + │ │ └── arguments: + │ │ @ ArgumentsNode (location: (27,15)-(27,16)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ IntegerNode (location: (27,15)-(27,16)) + │ │ ├── flags: static_literal, decimal + │ │ └── value: 1 + │ ├── opening_loc: (27,7)-(27,8) = "(" + │ └── closing_loc: (27,16)-(27,17) = ")" + └── operator_loc: (27,4)-(27,6) = "&&" diff --git a/src/prism.c b/src/prism.c index b7e2bdc1de..2f7ce0b986 100644 --- a/src/prism.c +++ b/src/prism.c @@ -19102,7 +19102,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left; if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) { + pm_token_t next = parser->current; parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1)); + + // Reject `foo && return bar`. + if (!accepts_command_call && arguments.arguments != NULL) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type)); + } } } diff --git a/test/prism/errors/command_calls_32.txt b/test/prism/errors/command_calls_32.txt new file mode 100644 index 0000000000..14488ca335 --- /dev/null +++ b/test/prism/errors/command_calls_32.txt @@ -0,0 +1,19 @@ +foo && return bar + ^~~ unexpected local variable or method, expecting end-of-input + +tap { foo && break bar } + ^~~ unexpected local variable or method, expecting end-of-input + +tap { foo && next bar } + ^~~ unexpected local variable or method, expecting end-of-input + +foo && return() + ^ unexpected '(', expecting end-of-input + +foo && return(bar) + ^ unexpected '(', expecting end-of-input + +foo && return(bar, baz) + ^~~~~~~~~~ unexpected write target + ^ unexpected '(', expecting end-of-input + diff --git a/test/prism/fixtures/break.txt b/test/prism/fixtures/break.txt index 5532322c5c..d823f866df 100644 --- a/test/prism/fixtures/break.txt +++ b/test/prism/fixtures/break.txt @@ -20,6 +20,10 @@ tap { break() } tap { break(1) } +tap { (break 1) } + +tap { foo && (break 1) } + foo { break 42 } == 42 foo { |a| break } == 42 diff --git a/test/prism/fixtures/next.txt b/test/prism/fixtures/next.txt index 2ef14c6304..0d2d6a11f5 100644 --- a/test/prism/fixtures/next.txt +++ b/test/prism/fixtures/next.txt @@ -22,3 +22,7 @@ tap { next tap { next() } tap { next(1) } + +tap { (next 1) } + +tap { foo && (next 1) } diff --git a/test/prism/fixtures/return.txt b/test/prism/fixtures/return.txt index a8b5b95fab..952fb80da8 100644 --- a/test/prism/fixtures/return.txt +++ b/test/prism/fixtures/return.txt @@ -22,3 +22,6 @@ return() return(1) +(return 1) + +foo && (return 1)