[ruby/prism] Add warning for chained comparisons

https://github.com/ruby/prism/commit/f9f3620d44
This commit is contained in:
Kevin Newton 2024-03-12 14:46:21 -04:00 коммит произвёл git
Родитель 157733b1b7
Коммит e914fa0d10
4 изменённых файлов: 27 добавлений и 11 удалений

Просмотреть файл

@ -235,6 +235,7 @@ warnings:
- AMBIGUOUS_FIRST_ARGUMENT_PLUS - AMBIGUOUS_FIRST_ARGUMENT_PLUS
- AMBIGUOUS_PREFIX_STAR - AMBIGUOUS_PREFIX_STAR
- AMBIGUOUS_SLASH - AMBIGUOUS_SLASH
- COMPARISON_AFTER_COMPARISON
- DOT_DOT_DOT_EOL - DOT_DOT_DOT_EOL
- EQUAL_IN_CONDITIONAL - EQUAL_IN_CONDITIONAL
- EQUAL_IN_CONDITIONAL_3_3_0 - EQUAL_IN_CONDITIONAL_3_3_0

Просмотреть файл

@ -1959,6 +1959,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument
return node; return node;
} }
// There are certain flags that we want to use internally but don't want to
// expose because they are not relevant beyond parsing. Therefore we'll define
// them here and not define them in config.yml/a header file.
static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x10;
/** /**
* Allocate and initialize a new CallNode node. This sets everything to NULL or * Allocate and initialize a new CallNode node. This sets everything to NULL or
* PM_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden * PM_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden
@ -2026,11 +2031,11 @@ pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_
* Allocate and initialize a new CallNode node from a binary expression. * Allocate and initialize a new CallNode node from a binary expression.
*/ */
static pm_call_node_t * static pm_call_node_t *
pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_node_t *argument) { pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_node_t *argument, pm_node_flags_t flags) {
pm_assert_value_expression(parser, receiver); pm_assert_value_expression(parser, receiver);
pm_assert_value_expression(parser, argument); pm_assert_value_expression(parser, argument);
pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver)); pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
node->base.location.start = MIN(receiver->location.start, argument->location.start); node->base.location.start = MIN(receiver->location.start, argument->location.start);
node->base.location.end = MAX(receiver->location.end, argument->location.end); node->base.location.end = MAX(receiver->location.end, argument->location.end);
@ -17507,7 +17512,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (accept1(parser, PM_TOKEN_STAR_STAR)) { if (accept1(parser, PM_TOKEN_STAR_STAR)) {
pm_token_t exponent_operator = parser->previous; pm_token_t exponent_operator = parser->previous;
pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, false, PM_ERR_EXPECT_ARGUMENT); pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, false, PM_ERR_EXPECT_ARGUMENT);
node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent); node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0);
node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@");
} else { } else {
switch (PM_NODE_TYPE(node)) { switch (PM_NODE_TYPE(node)) {
@ -18262,7 +18267,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
// By default, we're going to create a call node and then return it. // By default, we're going to create a call node and then return it.
pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument); pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
pm_node_t *result = (pm_node_t *) call; pm_node_t *result = (pm_node_t *) call;
// If the receiver of this =~ is a regular expression node, then we // If the receiver of this =~ is a regular expression node, then we
@ -18327,10 +18332,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
case PM_TOKEN_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL:
case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL_EQUAL:
case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL_GREATER:
case PM_TOKEN_GREATER:
case PM_TOKEN_GREATER_EQUAL:
case PM_TOKEN_LESS:
case PM_TOKEN_LESS_EQUAL:
case PM_TOKEN_CARET: case PM_TOKEN_CARET:
case PM_TOKEN_PIPE: case PM_TOKEN_PIPE:
case PM_TOKEN_AMPERSAND: case PM_TOKEN_AMPERSAND:
@ -18343,9 +18344,20 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
case PM_TOKEN_STAR: case PM_TOKEN_STAR:
case PM_TOKEN_STAR_STAR: { case PM_TOKEN_STAR_STAR: {
parser_lex(parser); parser_lex(parser);
pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument); return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, 0);
}
case PM_TOKEN_GREATER:
case PM_TOKEN_GREATER_EQUAL:
case PM_TOKEN_LESS:
case PM_TOKEN_LESS_EQUAL: {
if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
}
parser_lex(parser);
pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON);
} }
case PM_TOKEN_AMPERSAND_DOT: case PM_TOKEN_AMPERSAND_DOT:
case PM_TOKEN_DOT: { case PM_TOKEN_DOT: {
@ -18670,6 +18682,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
current_binding_powers.binary current_binding_powers.binary
) { ) {
node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call); node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call);
if (current_binding_powers.nonassoc) { if (current_binding_powers.nonassoc) {
bool endless_range_p = PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL; bool endless_range_p = PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL;
pm_binding_power_t left = endless_range_p ? PM_BINDING_POWER_TERM : current_binding_powers.left; pm_binding_power_t left = endless_range_p ? PM_BINDING_POWER_TERM : current_binding_powers.left;
@ -18683,6 +18696,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
break; break;
} }
} }
if (accepts_command_call) { if (accepts_command_call) {
// A command-style method call is only accepted on method chains. // A command-style method call is only accepted on method chains.
// Thus, we check whether the parsed node can continue method chains. // Thus, we check whether the parsed node can continue method chains.

Просмотреть файл

@ -315,6 +315,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_AMBIGUOUS_PREFIX_STAR] = { "ambiguous `*` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_PREFIX_STAR] = { "ambiguous `*` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_AMBIGUOUS_SLASH] = { "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_SLASH] = { "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_COMPARISON_AFTER_COMPARISON] = { "comparison '%.*s' after comparison", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_DOT_DOT_DOT_EOL] = { "... at EOL, should be parenthesized?", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_DOT_DOT_DOT_EOL] = { "... at EOL, should be parenthesized?", PM_WARNING_LEVEL_DEFAULT },
[PM_WARN_DUPLICATED_HASH_KEY] = { "key %.*s is duplicated and overwritten on line %" PRIi32, PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_DUPLICATED_HASH_KEY] = { "key %.*s is duplicated and overwritten on line %" PRIi32, PM_WARNING_LEVEL_DEFAULT },
[PM_WARN_DUPLICATED_WHEN_CLAUSE] = { "duplicated 'when' clause with line %" PRIi32 " is ignored", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_DUPLICATED_WHEN_CLAUSE] = { "duplicated 'when' clause with line %" PRIi32 " is ignored", PM_WARNING_LEVEL_VERBOSE },

Просмотреть файл

@ -15,7 +15,7 @@ module Prism
assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false) assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false)
> 1 | "%W"\u" > 1 | "%W"\u"
| ^ invalid character `\` | ^ unexpected backslash, ignoring it
| ^ unexpected local variable or method, expecting end-of-input | ^ unexpected local variable or method, expecting end-of-input
| ^ unterminated string meets end of file | ^ unterminated string meets end of file
ERROR ERROR