aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Tsujimoto <kazuki@callcc.net>2019-11-07 14:00:59 +0900
committerKazuki Tsujimoto <kazuki@callcc.net>2019-11-07 15:40:35 +0900
commita396bef8d8b72328d411abe7a275736ad995fbcb (patch)
tree9bf0f00530c0a3e78e5665ee32756bb6b8d8e77a
parent1e620c67af3d37b6f120765daf29e96a643325ab (diff)
downloadruby-a396bef8d8b72328d411abe7a275736ad995fbcb.tar.gz
Disallow duplicated pattern variable
-rw-r--r--parse.y45
-rw-r--r--test/ruby/test_pattern_matching.rb67
2 files changed, 100 insertions, 12 deletions
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
$<num>$ = p->in_kwarg;
p->in_kwarg = 1;
}
+ {
+ $<tbl>$ = p->pvtbl;
+ p->pvtbl = st_init_numtable();
+ }
p_top_expr_body
{
+ st_free_table(p->pvtbl);
+ p->pvtbl = $<tbl>4;
+ }
+ {
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!");
/*% %*/
- /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/
+ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
}
| arg %prec tLBRACE_ARG
;
@@ -3800,17 +3812,25 @@ p_case_body : keyword_in
$<num>$ = p->in_kwarg;
p->in_kwarg = 1;
}
+ {
+ $<tbl>$ = p->pvtbl;
+ p->pvtbl = st_init_numtable();
+ }
p_top_expr then
{
+ st_free_table(p->pvtbl);
+ p->pvtbl = $<tbl>3;
+ }
+ {
p->in_kwarg = !!$<num>2;
}
compstmt
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)) %*/
@@ -11530,6 +11553,20 @@ new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *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)
{
st_table *literal_keys = st_init_numtable_with_size(hash->nd_alen / 2);
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