diff --git a/parse.y b/parse.y index 21cb6e4fa3..8b5e1d5ad7 100644 --- a/parse.y +++ b/parse.y @@ -250,6 +250,7 @@ struct parser_params { int heredoc_line_indent; char *tokenbuf; struct local_vars *lvtbl; + st_table *pvtbl; int line_count; int ruby_sourceline; /* current line no. */ 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 error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc); + #define get_id(id) (id) #define get_value(val) (val) #define get_num(num) (num) @@ -974,6 +977,7 @@ static int looking_at_eol_p(struct parser_params *p); NODE *node; ID id; int num; + st_table *tbl; const struct vtable *vars; struct rb_strterm_struct *strterm; } @@ -1561,14 +1565,22 @@ expr : command_call $$ = p->in_kwarg; p->in_kwarg = 1; } + { + $$ = p->pvtbl; + p->pvtbl = st_init_numtable(); + } p_top_expr_body + { + st_free_table(p->pvtbl); + p->pvtbl = $4; + } { p->in_kwarg = !!$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!"); /*% %*/ - /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ + /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/ } | arg %prec tLBRACE_ARG ; @@ -3800,7 +3812,15 @@ p_case_body : keyword_in $$ = p->in_kwarg; p->in_kwarg = 1; } + { + $$ = p->pvtbl; + p->pvtbl = st_init_numtable(); + } p_top_expr then + { + st_free_table(p->pvtbl); + p->pvtbl = $3; + } { p->in_kwarg = !!$2; } @@ -3808,9 +3828,9 @@ p_case_body : keyword_in 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"); } /*%%%*/ + error_duplicate_pattern_variable(p, $1, &@1); $$ = 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)) %*/ @@ -4119,6 +4140,7 @@ p_kw : tLABEL p_expr if (!is_local_id(id)) { 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, &@$)); } else { @@ -4249,6 +4271,7 @@ p_primitive : literal p_variable : tIDENTIFIER { /*%%%*/ + error_duplicate_pattern_variable(p, $1, &@1); $$ = assignable(p, $1, 0, &@$); /*% %*/ /*% 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); } +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 error_duplicate_keys(struct parser_params *p, NODE *hash) { diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 5283d5ce87..0338f90a91 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -193,25 +193,25 @@ class TestPatternMatching < Test::Unit::TestCase def test_var_pattern # NODE_DASGN_CURR assert_block do - case [0, 1] - in a, a - a == 1 + case 0 + in a + a == 0 end end # NODE_DASGN b = 0 assert_block do - case [0, 1] - in b, b + case 1 + in b b == 1 end end # NODE_LASGN - case [0, 1] - in c, c - assert_equal(1, c) + case 0 + in c + assert_equal(0, c) else flunk end @@ -221,6 +221,57 @@ class TestPatternMatching < Test::Unit::TestCase in ^a end }, /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 def test_literal_value_pattern