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

parse.y: duplicated when clause warning

* parse.y (case_args): moved "duplicated when clause" warning from
  compile phase, so that `ruby -wc` shows them.
This commit is contained in:
Nobuyoshi Nakada 2018-07-28 00:07:56 +09:00
parent 848edb03f8
commit ff21e75d32
No known key found for this signature in database
GPG key ID: 4BC7D6DF58D8DF60
4 changed files with 108 additions and 22 deletions

View file

@ -4073,8 +4073,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
return len;
}
static VALUE
case_when_optimizable_literal(const NODE *const node)
VALUE
rb_node_case_when_optimizable_literal(const NODE *const node)
{
switch (nd_type(node)) {
case NODE_LIT: {
@ -4107,20 +4107,13 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
{
while (vals) {
const NODE *val = vals->nd_head;
VALUE lit = case_when_optimizable_literal(val);
VALUE lit = rb_node_case_when_optimizable_literal(val);
if (lit == Qundef) {
only_special_literals = 0;
}
else {
if (rb_hash_lookup(literals, lit) != Qnil) {
VALUE file = rb_iseq_path(iseq);
rb_compile_warning(RSTRING_PTR(file), nd_line(val),
"duplicated when clause is ignored");
}
else {
rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
}
else if (NIL_P(rb_hash_lookup(literals, lit))) {
rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
}
ADD_INSN(cond_seq, nd_line(val), dup); /* dup target */

2
node.h
View file

@ -384,6 +384,8 @@ typedef struct RNode {
#define NODE_SPECIAL_NO_NAME_REST ((NODE *)-1)
#define NODE_NAMED_REST_P(node) ((node) != NODE_SPECIAL_NO_NAME_REST)
VALUE rb_node_case_when_optimizable_literal(const NODE *const node);
RUBY_SYMBOL_EXPORT_BEGIN
typedef struct node_buffer_struct node_buffer_t;

94
parse.y
View file

@ -236,6 +236,7 @@ struct parser_params {
VALUE ruby_sourcefile_string;
rb_encoding *enc;
token_info *token_info;
VALUE case_labels;
VALUE compile_option;
VALUE debug_buffer;
@ -475,6 +476,9 @@ static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, con
static int literal_concat0(struct parser_params *p, VALUE head, VALUE tail);
static NODE *heredoc_dedent(struct parser_params*,NODE*);
static void check_literal_when(struct parser_params *p, NODE *args, const YYLTYPE *loc);
#define get_id(id) (id)
#define get_value(val) (val)
#define get_num(num) (num)
@ -864,6 +868,7 @@ static ID id_warn, id_warning, id_gets, id_assoc, id_or;
# define WARN_S(s) STR_NEW2(s)
# define WARN_I(i) INT2NUM(i)
# define WARN_ID(i) rb_id2str(i)
# define WARN_IVAL(i) i
# define PRIsWARN "s"
# define WARN_ARGS(fmt,n) p->value, id_warn, n, rb_usascii_str_new_lit(fmt)
# define WARN_ARGS_L(l,fmt,n) WARN_ARGS(fmt,n)
@ -886,6 +891,7 @@ PRINTF_ARGS(static void ripper_compile_error(struct parser_params*, const char *
# define WARN_S(s) s
# define WARN_I(i) i
# define WARN_ID(i) rb_id2name(i)
# define WARN_IVAL(i) NUM2INT(i)
# define PRIsWARN PRIsVALUE
# define WARN_ARGS(fmt,n) WARN_ARGS_L(p->ruby_sourceline,fmt,n)
# define WARN_ARGS_L(l,fmt,n) p->ruby_sourcefile, (l), (fmt)
@ -995,7 +1001,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in
%type <node> top_compstmt top_stmts top_stmt begin_block
%type <node> bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call
%type <node> expr_value expr_value_do arg_value primary_value fcall rel_expr
%type <node> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
%type <node> if_tail opt_else case_body case_args cases opt_rescue exc_list exc_var opt_ensure
%type <node> args call_args opt_call_args
%type <node> paren_args opt_paren_args args_tail opt_args_tail block_args_tail opt_block_args_tail
%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
@ -2698,21 +2704,35 @@ primary : literal
/*% ripper: until!($2, $3) %*/
}
| k_case expr_value opt_terms
{
$<val>$ = p->case_labels;
p->case_labels = Qnil;
}
case_body
k_end
{
if (RTEST(p->case_labels)) rb_hash_clear(p->case_labels);
p->case_labels = $<val>4;
/*%%%*/
$$ = NEW_CASE($2, $4, &@$);
$$ = NEW_CASE($2, $5, &@$);
fixpos($$, $2);
/*% %*/
/*% ripper: case!($2, $4) %*/
/*% ripper: case!($2, $5) %*/
}
| k_case opt_terms case_body k_end
| k_case opt_terms
{
$<val>$ = p->case_labels;
p->case_labels = 0;
}
case_body
k_end
{
if (RTEST(p->case_labels)) rb_hash_clear(p->case_labels);
p->case_labels = $<val>3;
/*%%%*/
$$ = NEW_CASE2($3, &@$);
$$ = NEW_CASE2($4, &@$);
/*% %*/
/*% ripper: case!(Qnil, $3) %*/
/*% ripper: case!(Qnil, $4) %*/
}
| k_case expr_value opt_terms
p_case_body
@ -3583,7 +3603,39 @@ do_body : {$<vars>$ = dyna_push(p);}
}
;
case_body : k_when args then
case_args : arg_value
{
/*%%%*/
check_literal_when(p, $1, &@1);
$$ = NEW_LIST($1, &@$);
/*% %*/
/*% ripper: args_add!(args_new!, $1) %*/
}
| tSTAR arg_value
{
/*%%%*/
$$ = NEW_SPLAT($2, &@$);
/*% %*/
/*% ripper: args_add_star!(args_new!, $2) %*/
}
| case_args ',' arg_value
{
/*%%%*/
check_literal_when(p, $3, &@3);
$$ = last_arg_append(p, $1, $3, &@$);
/*% %*/
/*% ripper: args_add!($1, $3) %*/
}
| case_args ',' tSTAR arg_value
{
/*%%%*/
$$ = rest_arg_append(p, $1, $4, &@$);
/*% %*/
/*% ripper: args_add_star!($1, $4) %*/
}
;
case_body : k_when case_args then
compstmt
cases
{
@ -9780,6 +9832,33 @@ new_xstring(struct parser_params *p, NODE *node, const YYLTYPE *loc)
return node;
}
static void
check_literal_when(struct parser_params *p, NODE *arg, const YYLTYPE *loc)
{
VALUE lit;
if (!arg || !p->case_labels) return;
lit = rb_node_case_when_optimizable_literal(arg);
if (lit == Qundef) return;
if (nd_type(arg) == NODE_STR) {
arg->nd_lit = add_mark_object(p, lit);
}
if (NIL_P(p->case_labels)) {
p->case_labels = rb_obj_hide(rb_hash_new());
}
else {
VALUE line = rb_hash_lookup(p->case_labels, lit);
if (!NIL_P(line)) {
rb_warning1("duplicated `when' clause with line %d is ignored",
WARN_IVAL(line));
return;
}
}
rb_hash_aset(p->case_labels, lit, INT2NUM(p->ruby_sourceline));
}
#else /* !RIPPER */
static int
id_is_var(struct parser_params *p, ID id)
@ -11853,6 +11932,7 @@ parser_mark(void *ptr)
rb_gc_mark(p->ruby_sourcefile_string);
rb_gc_mark((VALUE)p->lex.strterm);
rb_gc_mark((VALUE)p->ast);
rb_gc_mark(p->case_labels);
#ifndef RIPPER
rb_gc_mark(p->debug_lines);
rb_gc_mark(p->compile_option);

View file

@ -515,8 +515,8 @@ WARN
end
def test_duplicated_when
w = 'warning: duplicated when clause is ignored'
assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
w = 'warning: duplicated `when\' clause with line 3 is ignored'
assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
eval %q{
case 1
when 1, 1
@ -525,7 +525,7 @@ WARN
end
}
}
assert_warning(/#{w}/){#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
assert_warning(/#{w}/) {#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
a = a = 1
eval %q{
case 1
@ -537,6 +537,17 @@ WARN
}
end
def test_duplicated_when_check_option
w = /duplicated `when\' clause with line 3 is ignored/
assert_in_out_err(%[-wc], "#{<<~"begin;"}\n#{<<~'end;'}", ["Syntax OK"], w)
begin;
case 1
when 1
when 1
end
end;
end
def test_invalid_break
assert_syntax_error("def m; break; end", /Invalid break/)
assert_syntax_error('/#{break}/', /Invalid break/)