From 7d21e564ac4d0a7e2ebb4f2b63bc1db73a043c47 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 6 Mar 2026 16:01:02 -0500 Subject: [PATCH] Fix not without parentheses binding power --- src/prism.c | 10 ++++++---- test/prism/errors/not_without_parens_assignment.txt | 4 ++++ test/prism/errors/not_without_parens_call.txt | 7 +++++++ test/prism/errors/not_without_parens_command.txt | 4 ++++ test/prism/errors/not_without_parens_command_call.txt | 4 ++++ test/prism/errors/not_without_parens_return.txt | 4 ++++ 6 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test/prism/errors/not_without_parens_assignment.txt create mode 100644 test/prism/errors/not_without_parens_call.txt create mode 100644 test/prism/errors/not_without_parens_command.txt create mode 100644 test/prism/errors/not_without_parens_command_call.txt create mode 100644 test/prism/errors/not_without_parens_return.txt diff --git a/src/prism.c b/src/prism.c index efe21c5e7e..1a6165810c 100644 --- a/src/prism.c +++ b/src/prism.c @@ -19195,10 +19195,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_arguments_t arguments = { 0 }; pm_node_t *receiver = NULL; - // If we do not accept a command call, then we also do not accept a - // not without parentheses. In this case we need to reject this - // syntax. - if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + // The `not` keyword without parentheses is only valid in contexts + // where it would be parsed as an expression (i.e., at or below + // the `not` binding power level). In other contexts (e.g., method + // arguments, array elements, assignment right-hand sides), + // parentheses are required: `not(x)`. + if (binding_power > PM_BINDING_POWER_NOT && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) { pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN); } else { diff --git a/test/prism/errors/not_without_parens_assignment.txt b/test/prism/errors/not_without_parens_assignment.txt new file mode 100644 index 0000000000..32d58efedf --- /dev/null +++ b/test/prism/errors/not_without_parens_assignment.txt @@ -0,0 +1,4 @@ +x = not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_call.txt b/test/prism/errors/not_without_parens_call.txt new file mode 100644 index 0000000000..a778193400 --- /dev/null +++ b/test/prism/errors/not_without_parens_call.txt @@ -0,0 +1,7 @@ +foo(not y) + ^ expected a `(` after `not` + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/not_without_parens_command.txt b/test/prism/errors/not_without_parens_command.txt new file mode 100644 index 0000000000..957a06f8f1 --- /dev/null +++ b/test/prism/errors/not_without_parens_command.txt @@ -0,0 +1,4 @@ +foo not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_command_call.txt b/test/prism/errors/not_without_parens_command_call.txt new file mode 100644 index 0000000000..564833c7de --- /dev/null +++ b/test/prism/errors/not_without_parens_command_call.txt @@ -0,0 +1,4 @@ +a.b not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_return.txt b/test/prism/errors/not_without_parens_return.txt new file mode 100644 index 0000000000..1c7edb6ff1 --- /dev/null +++ b/test/prism/errors/not_without_parens_return.txt @@ -0,0 +1,4 @@ +return not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input +