1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Disallow duplicated pattern variable

This commit is contained in:
Kazuki Tsujimoto 2019-11-07 14:00:59 +09:00
parent 1e620c67af
commit a396bef8d8
No known key found for this signature in database
GPG key ID: BCEA306C49B81CD7
2 changed files with 100 additions and 12 deletions

45
parse.y
View file

@ -250,6 +250,7 @@ struct parser_params {
int heredoc_line_indent; int heredoc_line_indent;
char *tokenbuf; char *tokenbuf;
struct local_vars *lvtbl; struct local_vars *lvtbl;
st_table *pvtbl;
int line_count; int line_count;
int ruby_sourceline; /* current line no. */ int ruby_sourceline; /* current line no. */
const char *ruby_sourcefile; /* current source file */ const char *ruby_sourcefile; /* current source file */
@ -496,6 +497,8 @@ static NODE *heredoc_dedent(struct parser_params*,NODE*);
static void check_literal_when(struct parser_params *p, NODE *args, const YYLTYPE *loc); static void check_literal_when(struct parser_params *p, NODE *args, const YYLTYPE *loc);
static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc);
#define get_id(id) (id) #define get_id(id) (id)
#define get_value(val) (val) #define get_value(val) (val)
#define get_num(num) (num) #define get_num(num) (num)
@ -974,6 +977,7 @@ static int looking_at_eol_p(struct parser_params *p);
NODE *node; NODE *node;
ID id; ID id;
int num; int num;
st_table *tbl;
const struct vtable *vars; const struct vtable *vars;
struct rb_strterm_struct *strterm; struct rb_strterm_struct *strterm;
} }
@ -1561,14 +1565,22 @@ expr : command_call
$<num>$ = p->in_kwarg; $<num>$ = p->in_kwarg;
p->in_kwarg = 1; p->in_kwarg = 1;
} }
{
$<tbl>$ = p->pvtbl;
p->pvtbl = st_init_numtable();
}
p_top_expr_body p_top_expr_body
{
st_free_table(p->pvtbl);
p->pvtbl = $<tbl>4;
}
{ {
p->in_kwarg = !!$<num>3; p->in_kwarg = !!$<num>3;
/*%%%*/ /*%%%*/
$$ = NEW_CASE3($1, NEW_IN($4, NEW_TRUE(&@4), NEW_FALSE(&@4), &@4), &@$); $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$);
rb_warn0L(nd_line($$), "Pattern matching is experimental, and the behavior may change in future versions of Ruby!"); rb_warn0L(nd_line($$), "Pattern matching is experimental, and the behavior may change in future versions of Ruby!");
/*% %*/ /*% %*/
/*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
} }
| arg %prec tLBRACE_ARG | arg %prec tLBRACE_ARG
; ;
@ -3800,7 +3812,15 @@ p_case_body : keyword_in
$<num>$ = p->in_kwarg; $<num>$ = p->in_kwarg;
p->in_kwarg = 1; p->in_kwarg = 1;
} }
{
$<tbl>$ = p->pvtbl;
p->pvtbl = st_init_numtable();
}
p_top_expr then p_top_expr then
{
st_free_table(p->pvtbl);
p->pvtbl = $<tbl>3;
}
{ {
p->in_kwarg = !!$<num>2; p->in_kwarg = !!$<num>2;
} }
@ -3808,9 +3828,9 @@ p_case_body : keyword_in
p_cases p_cases
{ {
/*%%%*/ /*%%%*/
$$ = NEW_IN($3, $6, $7, &@$); $$ = NEW_IN($4, $8, $9, &@$);
/*% %*/ /*% %*/
/*% ripper: in!($3, $6, escape_Qundef($7)) %*/ /*% ripper: in!($4, $8, escape_Qundef($9)) %*/
} }
; ;
@ -4089,6 +4109,7 @@ p_kw : tLABEL p_expr
yyerror1(&@1, "key must be valid as local variables"); yyerror1(&@1, "key must be valid as local variables");
} }
/*%%%*/ /*%%%*/
error_duplicate_pattern_variable(p, $1, &@1);
$$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@$), &@$), assignable(p, $1, 0, &@$)); $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@$), &@$), assignable(p, $1, 0, &@$));
/*% %*/ /*% %*/
/*% ripper: rb_ary_new_from_args(1, rb_ary_new_from_args(2, get_value($1), Qnil)) %*/ /*% ripper: rb_ary_new_from_args(1, rb_ary_new_from_args(2, get_value($1), Qnil)) %*/
@ -4119,6 +4140,7 @@ p_kw : tLABEL p_expr
if (!is_local_id(id)) { if (!is_local_id(id)) {
yyerror1(&loc, "key must be valid as local variables"); yyerror1(&loc, "key must be valid as local variables");
} }
error_duplicate_pattern_variable(p, id, &loc);
$$ = list_append(p, NEW_LIST(node, &loc), assignable(p, id, 0, &@$)); $$ = list_append(p, NEW_LIST(node, &loc), assignable(p, id, 0, &@$));
} }
else { else {
@ -4249,6 +4271,7 @@ p_primitive : literal
p_variable : tIDENTIFIER p_variable : tIDENTIFIER
{ {
/*%%%*/ /*%%%*/
error_duplicate_pattern_variable(p, $1, &@1);
$$ = assignable(p, $1, 0, &@$); $$ = assignable(p, $1, 0, &@$);
/*% %*/ /*% %*/
/*% ripper: assignable(p, var_field(p, $1)) %*/ /*% ripper: assignable(p, var_field(p, $1)) %*/
@ -11529,6 +11552,20 @@ new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
return NEW_HASH(hash, loc); return NEW_HASH(hash, loc);
} }
static void
error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc)
{
if (is_private_local_id(id)) {
return;
}
if (st_is_member(p->pvtbl, id)) {
yyerror1(loc, "duplicated variable name");
}
else {
st_insert(p->pvtbl, (st_data_t)id, 0);
}
}
static void static void
error_duplicate_keys(struct parser_params *p, NODE *hash) error_duplicate_keys(struct parser_params *p, NODE *hash)
{ {

View file

@ -193,25 +193,25 @@ class TestPatternMatching < Test::Unit::TestCase
def test_var_pattern def test_var_pattern
# NODE_DASGN_CURR # NODE_DASGN_CURR
assert_block do assert_block do
case [0, 1] case 0
in a, a in a
a == 1 a == 0
end end
end end
# NODE_DASGN # NODE_DASGN
b = 0 b = 0
assert_block do assert_block do
case [0, 1] case 1
in b, b in b
b == 1 b == 1
end end
end end
# NODE_LASGN # NODE_LASGN
case [0, 1] case 0
in c, c in c
assert_equal(1, c) assert_equal(0, c)
else else
flunk flunk
end end
@ -221,6 +221,57 @@ class TestPatternMatching < Test::Unit::TestCase
in ^a in ^a
end end
}, /no such local variable/) }, /no such local variable/)
assert_syntax_error(%q{
case 0
in a, a
end
}, /duplicated variable name/)
assert_block do
case [0, 1, 2, 3]
in _, _, _a, _a
true
end
end
assert_syntax_error(%q{
case 0
in a, {a:}
end
}, /duplicated variable name/)
assert_syntax_error(%q{
case 0
in a, {"a":}
end
}, /duplicated variable name/)
assert_block do
case [0, "1"]
in a, "#{case 1; in a; a; end}"
true
end
end
assert_syntax_error(%q{
case [0, "1"]
in a, "#{case 1; in a; a; end}", a
end
}, /duplicated variable name/)
assert_block do
case 0
in a
true
in a
flunk
end
end
assert_syntax_error(%q{
0 in [a, a]
}, /duplicated variable name/)
end end
def test_literal_value_pattern def test_literal_value_pattern