From 0513cf22ad51bd9c596388bd37406f0dafa99181 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 13 Jun 2025 12:44:08 +0900 Subject: [PATCH 1/2] Reject `true && not true` A command-call-like `not true` must be rejected after `&&` and `||`. https://bugs.ruby-lang.org/issues/21337 --- config.yml | 1 + src/prism.c | 5 +++++ templates/src/diagnostic.c.erb | 1 + test/prism/errors/command_calls_31.txt | 12 ++++++++++++ 4 files changed, 19 insertions(+) create mode 100644 test/prism/errors/command_calls_31.txt diff --git a/config.yml b/config.yml index 0046810e4e..ee34047dbe 100644 --- a/config.yml +++ b/config.yml @@ -101,6 +101,7 @@ errors: - EXPECT_FOR_DELIMITER - EXPECT_IDENT_REQ_PARAMETER - EXPECT_IN_DELIMITER + - EXPECT_LPAREN_AFTER_NOT - EXPECT_LPAREN_REQ_PARAMETER - EXPECT_MESSAGE - EXPECT_RBRACKET diff --git a/src/prism.c b/src/prism.c index c5f5744f58..7e3477cf0e 100644 --- a/src/prism.c +++ b/src/prism.c @@ -19756,6 +19756,11 @@ 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 (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT); + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + } + accept1(parser, PM_TOKEN_NEWLINE); if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index ce98dc5acd..389b1dc484 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -184,6 +184,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EXPECT_FOR_DELIMITER] = { "unexpected %s; expected a 'do', newline, or ';' after the 'for' loop collection", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IN_DELIMITER] = { "expected a delimiter after the patterns of an `in` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT] = { "expected a `(` after `not`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors/command_calls_31.txt b/test/prism/errors/command_calls_31.txt new file mode 100644 index 0000000000..72d5fc588f --- /dev/null +++ b/test/prism/errors/command_calls_31.txt @@ -0,0 +1,12 @@ +true && not true + ^~~~ expected a `(` after `not` + ^~~~ unexpected 'true', expecting end-of-input + +true || not true + ^~~~ expected a `(` after `not` + ^~~~ unexpected 'true', expecting end-of-input + +true && not (true) + ^ expected a `(` after `not` + ^ unexpected '(', expecting end-of-input + From d9151b8a82cc95cb88e2e8b546c43acc514b0e66 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 7 Jul 2025 19:06:26 +0900 Subject: [PATCH 2/2] Improve error handling for missing parentheses after 'not' in command calls --- config.yml | 3 ++- src/prism.c | 11 ++++++++++- templates/src/diagnostic.c.erb | 3 ++- test/prism/errors/command_calls_31.txt | 7 ++++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/config.yml b/config.yml index ee34047dbe..b37b98cbdf 100644 --- a/config.yml +++ b/config.yml @@ -101,7 +101,8 @@ errors: - EXPECT_FOR_DELIMITER - EXPECT_IDENT_REQ_PARAMETER - EXPECT_IN_DELIMITER - - EXPECT_LPAREN_AFTER_NOT + - EXPECT_LPAREN_AFTER_NOT_LPAREN + - EXPECT_LPAREN_AFTER_NOT_OTHER - EXPECT_LPAREN_REQ_PARAMETER - EXPECT_MESSAGE - EXPECT_RBRACKET diff --git a/src/prism.c b/src/prism.c index 7e3477cf0e..eb7a4f7b81 100644 --- a/src/prism.c +++ b/src/prism.c @@ -19756,8 +19756,17 @@ 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)) { - pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT); + if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) { + pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN); + } else { + accept1(parser, PM_TOKEN_NEWLINE); + pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER); + } + return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); } diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index 389b1dc484..9a30a57e3b 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -184,7 +184,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EXPECT_FOR_DELIMITER] = { "unexpected %s; expected a 'do', newline, or ';' after the 'for' loop collection", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IN_DELIMITER] = { "expected a delimiter after the patterns of an `in` clause", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_EXPECT_LPAREN_AFTER_NOT] = { "expected a `(` after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN] = { "expected a `(` immediately after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER] = { "expected a `(` after `not`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors/command_calls_31.txt b/test/prism/errors/command_calls_31.txt index 72d5fc588f..e662b25444 100644 --- a/test/prism/errors/command_calls_31.txt +++ b/test/prism/errors/command_calls_31.txt @@ -7,6 +7,11 @@ true || not true ^~~~ unexpected 'true', expecting end-of-input true && not (true) - ^ expected a `(` after `not` + ^ expected a `(` immediately after `not` ^ unexpected '(', expecting end-of-input +true && not +true +^~~~ expected a `(` after `not` +^~~~ unexpected 'true', expecting end-of-input +