mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ripper] Fixed unique key check in pattern matching
Check keys * by an internal table, instead of unstable dispatched results * and by parsed key values, instead of escaped forms in the source
This commit is contained in:
parent
22dfd14c17
commit
9e01fcd0cb
2 changed files with 77 additions and 67 deletions
119
parse.y
119
parse.y
|
@ -251,6 +251,7 @@ struct parser_params {
|
||||||
char *tokenbuf;
|
char *tokenbuf;
|
||||||
struct local_vars *lvtbl;
|
struct local_vars *lvtbl;
|
||||||
st_table *pvtbl;
|
st_table *pvtbl;
|
||||||
|
st_table *pktbl;
|
||||||
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 */
|
||||||
|
@ -339,6 +340,21 @@ pop_pvtbl(struct parser_params *p, st_table *tbl)
|
||||||
p->pvtbl = tbl;
|
p->pvtbl = tbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static st_table *
|
||||||
|
push_pktbl(struct parser_params *p)
|
||||||
|
{
|
||||||
|
st_table *tbl = p->pktbl;
|
||||||
|
p->pktbl = 0;
|
||||||
|
return tbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pop_pktbl(struct parser_params *p, st_table *tbl)
|
||||||
|
{
|
||||||
|
if (p->pktbl) st_free_table(p->pktbl);
|
||||||
|
p->pktbl = tbl;
|
||||||
|
}
|
||||||
|
|
||||||
static int parser_yyerror(struct parser_params*, const YYLTYPE *yylloc, const char*);
|
static int parser_yyerror(struct parser_params*, const YYLTYPE *yylloc, const char*);
|
||||||
#define yyerror0(msg) parser_yyerror(p, NULL, (msg))
|
#define yyerror0(msg) parser_yyerror(p, NULL, (msg))
|
||||||
#define yyerror1(loc, msg) parser_yyerror(p, (loc), (msg))
|
#define yyerror1(loc, msg) parser_yyerror(p, (loc), (msg))
|
||||||
|
@ -583,6 +599,7 @@ YYLTYPE *rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc);
|
||||||
RUBY_SYMBOL_EXPORT_END
|
RUBY_SYMBOL_EXPORT_END
|
||||||
|
|
||||||
static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc);
|
static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc);
|
||||||
|
static void error_duplicate_pattern_key(struct parser_params *p, ID id, const YYLTYPE *loc);
|
||||||
static void parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp);
|
static void parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp);
|
||||||
static ID formal_argument(struct parser_params*, ID);
|
static ID formal_argument(struct parser_params*, ID);
|
||||||
static ID shadowing_lvar(struct parser_params*,ID);
|
static ID shadowing_lvar(struct parser_params*,ID);
|
||||||
|
@ -826,40 +843,7 @@ new_array_pattern_tail(struct parser_params *p, VALUE pre_args, VALUE has_rest,
|
||||||
static VALUE
|
static VALUE
|
||||||
new_unique_key_hash(struct parser_params *p, VALUE ary, const YYLTYPE *loc)
|
new_unique_key_hash(struct parser_params *p, VALUE ary, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
const long len = RARRAY_LEN(ary);
|
|
||||||
st_table *tbl;
|
|
||||||
long i;
|
|
||||||
|
|
||||||
tbl = st_init_strtable_with_size(len);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
VALUE key, a1, a2, a3;
|
|
||||||
a1 = RARRAY_AREF(ary, i);
|
|
||||||
if (!(RB_TYPE_P(a1, T_ARRAY) && RARRAY_LEN(a1) == 2)) goto error;
|
|
||||||
a2 = RARRAY_AREF(a1, 0);
|
|
||||||
if (!RB_TYPE_P(a2, T_ARRAY)) goto error;
|
|
||||||
switch (RARRAY_LEN(a2)) {
|
|
||||||
case 2: /* "key": */
|
|
||||||
a3 = RARRAY_AREF(a2, 1);
|
|
||||||
if (!(RB_TYPE_P(a3, T_ARRAY) && RARRAY_LEN(a3) == 3)) goto error;
|
|
||||||
key = RARRAY_AREF(a3, 1);
|
|
||||||
break;
|
|
||||||
case 3: /* key: */
|
|
||||||
key = RARRAY_AREF(a2, 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (!RB_TYPE_P(key, T_STRING)) goto error;
|
|
||||||
if (st_is_member(tbl, (st_data_t)RSTRING_PTR(key))) goto error;
|
|
||||||
st_insert(tbl, (st_data_t)RSTRING_PTR(key), (st_data_t)ary);
|
|
||||||
}
|
|
||||||
st_free_table(tbl);
|
|
||||||
return ary;
|
return ary;
|
||||||
|
|
||||||
error:
|
|
||||||
ripper_error(p);
|
|
||||||
st_free_table(tbl);
|
|
||||||
return Qnil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -3808,7 +3792,9 @@ p_case_body : keyword_in
|
||||||
p->in_kwarg = 1;
|
p->in_kwarg = 1;
|
||||||
}
|
}
|
||||||
{$<tbl>$ = push_pvtbl(p);}
|
{$<tbl>$ = push_pvtbl(p);}
|
||||||
|
{$<tbl>$ = push_pktbl(p);}
|
||||||
p_top_expr then
|
p_top_expr then
|
||||||
|
{pop_pktbl(p, $<tbl>4);}
|
||||||
{pop_pvtbl(p, $<tbl>3);}
|
{pop_pvtbl(p, $<tbl>3);}
|
||||||
{
|
{
|
||||||
p->in_kwarg = !!$<num>2;
|
p->in_kwarg = !!$<num>2;
|
||||||
|
@ -3817,9 +3803,9 @@ p_case_body : keyword_in
|
||||||
p_cases
|
p_cases
|
||||||
{
|
{
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = NEW_IN($4, $8, $9, &@$);
|
$$ = NEW_IN($5, $10, $11, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper: in!($4, $8, escape_Qundef($9)) %*/
|
/*% ripper: in!($5, $10, escape_Qundef($11)) %*/
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -3895,17 +3881,22 @@ p_alt : p_alt '|' p_expr_basic
|
||||||
| p_expr_basic
|
| p_expr_basic
|
||||||
;
|
;
|
||||||
|
|
||||||
|
p_lparen : '(' {$<tbl>$ = push_pktbl(p);};
|
||||||
|
p_lbracket : '[' {$<tbl>$ = push_pktbl(p);};
|
||||||
|
|
||||||
p_expr_basic : p_value
|
p_expr_basic : p_value
|
||||||
| p_const '(' p_args rparen
|
| p_const p_lparen p_args rparen
|
||||||
{
|
{
|
||||||
|
pop_pktbl(p, $<tbl>2);
|
||||||
$$ = new_array_pattern(p, $1, Qnone, $3, &@$);
|
$$ = new_array_pattern(p, $1, Qnone, $3, &@$);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
nd_set_first_loc($$, @1.beg_pos);
|
nd_set_first_loc($$, @1.beg_pos);
|
||||||
/*%
|
/*%
|
||||||
%*/
|
%*/
|
||||||
}
|
}
|
||||||
| p_const '(' p_kwargs rparen
|
| p_const p_lparen p_kwargs rparen
|
||||||
{
|
{
|
||||||
|
pop_pktbl(p, $<tbl>2);
|
||||||
$$ = new_hash_pattern(p, $1, $3, &@$);
|
$$ = new_hash_pattern(p, $1, $3, &@$);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
nd_set_first_loc($$, @1.beg_pos);
|
nd_set_first_loc($$, @1.beg_pos);
|
||||||
|
@ -3917,16 +3908,18 @@ p_expr_basic : p_value
|
||||||
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
||||||
$$ = new_array_pattern(p, $1, Qnone, $$, &@$);
|
$$ = new_array_pattern(p, $1, Qnone, $$, &@$);
|
||||||
}
|
}
|
||||||
| p_const '[' p_args rbracket
|
| p_const p_lbracket p_args rbracket
|
||||||
{
|
{
|
||||||
|
pop_pktbl(p, $<tbl>2);
|
||||||
$$ = new_array_pattern(p, $1, Qnone, $3, &@$);
|
$$ = new_array_pattern(p, $1, Qnone, $3, &@$);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
nd_set_first_loc($$, @1.beg_pos);
|
nd_set_first_loc($$, @1.beg_pos);
|
||||||
/*%
|
/*%
|
||||||
%*/
|
%*/
|
||||||
}
|
}
|
||||||
| p_const '[' p_kwargs rbracket
|
| p_const p_lbracket p_kwargs rbracket
|
||||||
{
|
{
|
||||||
|
pop_pktbl(p, $<tbl>2);
|
||||||
$$ = new_hash_pattern(p, $1, $3, &@$);
|
$$ = new_hash_pattern(p, $1, $3, &@$);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
nd_set_first_loc($$, @1.beg_pos);
|
nd_set_first_loc($$, @1.beg_pos);
|
||||||
|
@ -3938,27 +3931,30 @@ p_expr_basic : p_value
|
||||||
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
||||||
$$ = new_array_pattern(p, $1, Qnone, $$, &@$);
|
$$ = new_array_pattern(p, $1, Qnone, $$, &@$);
|
||||||
}
|
}
|
||||||
| tLBRACK p_args rbracket
|
| tLBRACK {$<tbl>$ = push_pktbl(p);} p_args rbracket
|
||||||
{
|
{
|
||||||
$$ = new_array_pattern(p, Qnone, Qnone, $2, &@$);
|
pop_pktbl(p, $<tbl>2);
|
||||||
|
$$ = new_array_pattern(p, Qnone, Qnone, $3, &@$);
|
||||||
}
|
}
|
||||||
| tLBRACK rbracket
|
| tLBRACK rbracket
|
||||||
{
|
{
|
||||||
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
$$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$);
|
||||||
$$ = new_array_pattern(p, Qnone, Qnone, $$, &@$);
|
$$ = new_array_pattern(p, Qnone, Qnone, $$, &@$);
|
||||||
}
|
}
|
||||||
| tLBRACE p_kwargs '}'
|
| tLBRACE {$<tbl>$ = push_pktbl(p);} p_kwargs '}'
|
||||||
{
|
{
|
||||||
$$ = new_hash_pattern(p, Qnone, $2, &@$);
|
pop_pktbl(p, $<tbl>2);
|
||||||
|
$$ = new_hash_pattern(p, Qnone, $3, &@$);
|
||||||
}
|
}
|
||||||
| tLBRACE '}'
|
| tLBRACE '}'
|
||||||
{
|
{
|
||||||
$$ = new_hash_pattern_tail(p, Qnone, 0, &@$);
|
$$ = new_hash_pattern_tail(p, Qnone, 0, &@$);
|
||||||
$$ = new_hash_pattern(p, Qnone, $$, &@$);
|
$$ = new_hash_pattern(p, Qnone, $$, &@$);
|
||||||
}
|
}
|
||||||
| tLPAREN p_expr rparen
|
| tLPAREN {$<tbl>$ = push_pktbl(p);} p_expr rparen
|
||||||
{
|
{
|
||||||
$$ = $2;
|
pop_pktbl(p, $<tbl>2);
|
||||||
|
$$ = $3;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -4088,6 +4084,7 @@ p_kwarg : p_kw
|
||||||
|
|
||||||
p_kw : p_kw_label p_expr
|
p_kw : p_kw_label p_expr
|
||||||
{
|
{
|
||||||
|
error_duplicate_pattern_key(p, get_id($1), &@1);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@$), &@$), $2);
|
$$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@$), &@$), $2);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
|
@ -4095,6 +4092,7 @@ p_kw : p_kw_label p_expr
|
||||||
}
|
}
|
||||||
| p_kw_label
|
| p_kw_label
|
||||||
{
|
{
|
||||||
|
error_duplicate_pattern_key(p, get_id($1), &@1);
|
||||||
if ($1 && !is_local_id(get_id($1))) {
|
if ($1 && !is_local_id(get_id($1))) {
|
||||||
yyerror1(&@1, "key must be valid as local variables");
|
yyerror1(&@1, "key must be valid as local variables");
|
||||||
}
|
}
|
||||||
|
@ -11540,36 +11538,23 @@ error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef RIPPER
|
|
||||||
static void
|
static void
|
||||||
error_duplicate_keys(struct parser_params *p, NODE *hash)
|
error_duplicate_pattern_key(struct parser_params *p, VALUE key, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
st_table *literal_keys = st_init_numtable_with_size(hash->nd_alen / 2);
|
if (!p->pktbl) {
|
||||||
while (hash && hash->nd_head && hash->nd_next) {
|
p->pktbl = st_init_numtable();
|
||||||
NODE *head = hash->nd_head;
|
|
||||||
NODE *next = hash->nd_next->nd_next;
|
|
||||||
VALUE key = (VALUE)head;
|
|
||||||
if (nd_type(head) != NODE_LIT) {
|
|
||||||
yyerror1(&head->nd_loc, "key must be symbol literal");
|
|
||||||
}
|
|
||||||
if (st_is_member(literal_keys, (key = head->nd_lit))) {
|
|
||||||
yyerror1(&head->nd_loc, "duplicated key name");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
|
|
||||||
}
|
|
||||||
hash = next;
|
|
||||||
}
|
}
|
||||||
st_free_table(literal_keys);
|
else if (st_is_member(p->pktbl, key)) {
|
||||||
return;
|
yyerror1(loc, "duplicated key name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
st_insert(p->pktbl, (st_data_t)key, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef RIPPER
|
||||||
static NODE *
|
static NODE *
|
||||||
new_unique_key_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
|
new_unique_key_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
if (hash) {
|
|
||||||
error_duplicate_keys(p, hash);
|
|
||||||
}
|
|
||||||
return NEW_HASH(hash, loc);
|
return NEW_HASH(hash, loc);
|
||||||
}
|
}
|
||||||
#endif /* !RIPPER */
|
#endif /* !RIPPER */
|
||||||
|
|
|
@ -438,6 +438,9 @@ eot
|
||||||
|
|
||||||
[__LINE__, %q{ case 0; in "A":; end }] =>
|
[__LINE__, %q{ case 0; in "A":; end }] =>
|
||||||
nil,
|
nil,
|
||||||
|
|
||||||
|
[__LINE__, %q{ case 0; in "a\x0":a1, "a\0":a2; end }] =>
|
||||||
|
nil, # duplicated key name
|
||||||
}
|
}
|
||||||
pattern_matching_data.each do |(i, src), expected|
|
pattern_matching_data.each do |(i, src), expected|
|
||||||
define_method(:"test_pattern_matching_#{i}") do
|
define_method(:"test_pattern_matching_#{i}") do
|
||||||
|
@ -445,4 +448,26 @@ eot
|
||||||
assert_equal expected, sexp && sexp[1][0], src
|
assert_equal expected, sexp && sexp[1][0], src
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_hshptn
|
||||||
|
parser = Class.new(Ripper::SexpBuilder) do
|
||||||
|
def on_label(token)
|
||||||
|
[:@label, token]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = parser.new("#{<<~"begin;"}#{<<~'end;'}").parse
|
||||||
|
begin;
|
||||||
|
case foo
|
||||||
|
in { a: 1 }
|
||||||
|
bar
|
||||||
|
else
|
||||||
|
baz
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
|
hshptn = result.dig(1, 2, 2, 1)
|
||||||
|
assert_equal(:hshptn, hshptn[0])
|
||||||
|
assert_equal([:@label, "a:"], hshptn.dig(2, 0, 0))
|
||||||
|
end
|
||||||
end if ripper_test
|
end if ripper_test
|
||||||
|
|
Loading…
Reference in a new issue