Skip to content

Commit ee87ed0

Browse files
committed
Add warning for assignments to literals in conditionals
1 parent 20b0602 commit ee87ed0

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

include/prism/diagnostic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ typedef enum {
298298
PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS,
299299
PM_WARN_AMBIGUOUS_PREFIX_STAR,
300300
PM_WARN_AMBIGUOUS_SLASH,
301+
PM_WARN_EQUAL_IN_CONDITIONAL,
301302
PM_WARN_END_IN_METHOD,
302303

303304
// This is the number of diagnostic codes.

src/diagnostic.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
300300
[PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE },
301301
[PM_WARN_AMBIGUOUS_PREFIX_STAR] = { "ambiguous `*` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE },
302302
[PM_WARN_AMBIGUOUS_SLASH] = { "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", PM_WARNING_LEVEL_VERBOSE },
303+
[PM_WARN_EQUAL_IN_CONDITIONAL] = { "found `= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT },
303304
[PM_WARN_END_IN_METHOD] = { "END in method; use at_exit", PM_WARNING_LEVEL_DEFAULT },
304305
};
305306

src/prism.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3572,6 +3572,68 @@ pm_hash_node_closing_loc_set(pm_hash_node_t *hash, pm_token_t *token) {
35723572
hash->closing_loc = PM_LOCATION_TOKEN_VALUE(token);
35733573
}
35743574

3575+
static pm_node_t *
3576+
pm_assignment_value_node(pm_node_t *node) {
3577+
switch (PM_NODE_TYPE(node)) {
3578+
case PM_CLASS_VARIABLE_WRITE_NODE: {
3579+
const pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *)node;
3580+
return cast->value;
3581+
}
3582+
case PM_CONSTANT_WRITE_NODE: {
3583+
const pm_constant_write_node_t *cast = (pm_constant_write_node_t *)node;
3584+
return cast->value;
3585+
}
3586+
case PM_GLOBAL_VARIABLE_WRITE_NODE: {
3587+
const pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *)node;
3588+
return cast->value;
3589+
}
3590+
case PM_INSTANCE_VARIABLE_WRITE_NODE: {
3591+
const pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *)node;
3592+
return cast->value;
3593+
}
3594+
case PM_LOCAL_VARIABLE_WRITE_NODE: {
3595+
const pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *)node;
3596+
return cast->value;
3597+
}
3598+
case PM_MULTI_WRITE_NODE: {
3599+
const pm_multi_write_node_t *cast = (pm_multi_write_node_t *)node;
3600+
return cast->value;
3601+
}
3602+
case PM_PARENTHESES_NODE: {
3603+
const pm_parentheses_node_t *cast = (pm_parentheses_node_t *)node;
3604+
if (cast->body != NULL) {
3605+
return pm_assignment_value_node(cast->body);
3606+
}
3607+
return NULL;
3608+
}
3609+
case PM_BEGIN_NODE: {
3610+
const pm_begin_node_t *cast = (pm_begin_node_t *)node;
3611+
if (cast->statements != NULL) {
3612+
return pm_assignment_value_node((pm_node_t *) cast->statements);
3613+
}
3614+
return NULL;
3615+
}
3616+
case PM_STATEMENTS_NODE: {
3617+
const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3618+
return pm_assignment_value_node(cast->body.nodes[cast->body.size - 1]);
3619+
}
3620+
default:
3621+
return NULL;
3622+
}
3623+
}
3624+
3625+
/**
3626+
* Check whether the predicate contains an assigment where the assigned value is a
3627+
* literal. If such an assignment is found, it generates a warning.
3628+
*/
3629+
static void
3630+
pm_check_predicate_assignment(pm_parser_t *parser, pm_node_t *predicate) {
3631+
pm_node_t *value = pm_assignment_value_node(predicate);
3632+
if ((value != NULL) && PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL)) {
3633+
pm_parser_warn_token(parser, &parser->current, PM_WARN_EQUAL_IN_CONDITIONAL);
3634+
}
3635+
}
3636+
35753637
/**
35763638
* Allocate a new IfNode node.
35773639
*/
@@ -3585,6 +3647,7 @@ pm_if_node_create(pm_parser_t *parser,
35853647
const pm_token_t *end_keyword
35863648
) {
35873649
pm_conditional_predicate(predicate);
3650+
pm_check_predicate_assignment(parser, predicate);
35883651
pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
35893652

35903653
const uint8_t *end;
@@ -3624,6 +3687,7 @@ pm_if_node_create(pm_parser_t *parser,
36243687
static pm_if_node_t *
36253688
pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
36263689
pm_conditional_predicate(predicate);
3690+
pm_check_predicate_assignment(parser, predicate);
36273691
pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
36283692

36293693
pm_statements_node_t *statements = pm_statements_node_create(parser);
@@ -3656,6 +3720,7 @@ static pm_if_node_t *
36563720
pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_token_t *qmark, pm_node_t *true_expression, const pm_token_t *colon, pm_node_t *false_expression) {
36573721
pm_assert_value_expression(parser, predicate);
36583722
pm_conditional_predicate(predicate);
3723+
pm_check_predicate_assignment(parser, predicate);
36593724

36603725
pm_statements_node_t *if_statements = pm_statements_node_create(parser);
36613726
pm_statements_node_body_append(if_statements, true_expression);
@@ -5899,6 +5964,7 @@ pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) {
58995964
static pm_unless_node_t *
59005965
pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) {
59015966
pm_conditional_predicate(predicate);
5967+
pm_check_predicate_assignment(parser, predicate);
59025968
pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t);
59035969

59045970
const uint8_t *end;
@@ -5934,6 +6000,7 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t
59346000
static pm_unless_node_t *
59356001
pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
59366002
pm_conditional_predicate(predicate);
6003+
pm_check_predicate_assignment(parser, predicate);
59376004
pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t);
59386005

59396006
pm_statements_node_t *statements = pm_statements_node_create(parser);
@@ -5971,6 +6038,7 @@ pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end
59716038
static pm_until_node_t *
59726039
pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
59736040
pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
6041+
pm_check_predicate_assignment(parser, predicate);
59746042

59756043
*node = (pm_until_node_t) {
59766044
{
@@ -5996,6 +6064,7 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to
59966064
static pm_until_node_t *
59976065
pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
59986066
pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
6067+
pm_check_predicate_assignment(parser, predicate);
59996068

60006069
*node = (pm_until_node_t) {
60016070
{
@@ -6065,6 +6134,7 @@ pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statemen
60656134
static pm_while_node_t *
60666135
pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
60676136
pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
6137+
pm_check_predicate_assignment(parser, predicate);
60686138

60696139
*node = (pm_while_node_t) {
60706140
{
@@ -6090,6 +6160,7 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to
60906160
static pm_while_node_t *
60916161
pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
60926162
pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
6163+
pm_check_predicate_assignment(parser, predicate);
60936164

60946165
*node = (pm_while_node_t) {
60956166
{
@@ -15864,6 +15935,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1586415935
} else {
1586515936
receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_NOT_EXPRESSION);
1586615937
pm_conditional_predicate(receiver);
15938+
pm_check_predicate_assignment(parser, receiver);
1586715939

1586815940
if (!parser->recovering) {
1586915941
accept1(parser, PM_TOKEN_NEWLINE);
@@ -15874,6 +15946,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1587415946
} else {
1587515947
receiver = parse_expression(parser, PM_BINDING_POWER_NOT, true, PM_ERR_NOT_EXPRESSION);
1587615948
pm_conditional_predicate(receiver);
15949+
pm_check_predicate_assignment(parser, receiver);
1587715950
}
1587815951

1587915952
return (pm_node_t *) pm_call_node_not_create(parser, receiver, &message, &arguments);
@@ -16544,6 +16617,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1654416617
pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
1654516618

1654616619
pm_conditional_predicate(receiver);
16620+
pm_check_predicate_assignment(parser, receiver);
1654716621
return (pm_node_t *) node;
1654816622
}
1654916623
case PM_TOKEN_TILDE: {

test/prism/errors_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,36 @@ def ([1]).foo; end
21132113
assert_errors expression(source), source, errors, compare_ripper: false
21142114
end
21152115

2116+
def test_assignment_to_literal_in_conditionals
2117+
source = <<~RUBY
2118+
if (a = 2); end
2119+
if ($a = 2); end
2120+
if (@a = 2); end
2121+
if (@@a = 2); end
2122+
if a elsif b = 2; end
2123+
unless (a = 2); end
2124+
unless ($a = 2); end
2125+
unless (@a = 2); end
2126+
unless (@@a = 2); end
2127+
while (a = 2); end
2128+
while ($a = 2); end
2129+
while (@a = 2); end
2130+
while (@@a = 2); end
2131+
until (a = 2); end
2132+
until ($a = 2); end
2133+
until (@a = 2); end
2134+
until (@@a = 2); end
2135+
foo if a = 2
2136+
foo if (a, b = 2)
2137+
(@foo = 1) ? a : b
2138+
!(a = 2)
2139+
not a = 2
2140+
RUBY
2141+
assert_warning_messages source, [
2142+
"found `= literal' in conditional, should be =="
2143+
] * source.lines.count
2144+
end
2145+
21162146
private
21172147

21182148
def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")

0 commit comments

Comments
 (0)