ruby-changes:55660
From: Nobuyoshi <ko1@a...>
Date: Sun, 5 May 2019 11:13:55 +0900 (JST)
Subject: [ruby-changes:55660] Nobuyoshi Nakada: ff21e75d32 (trunk): parse.y: duplicated when clause warning
https://git.ruby-lang.org/ruby.git/commit/?id=ff21e75d32 From ff21e75d32e27a2b362ed53fb471828876b54418 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Sat, 28 Jul 2018 00:07:56 +0900 Subject: parse.y: duplicated when clause warning * parse.y (case_args): moved "duplicated when clause" warning from compile phase, so that `ruby -wc` shows them. diff --git a/compile.c b/compile.c index ca117c2..7dbf244 100644 --- a/compile.c +++ b/compile.c @@ -4073,8 +4073,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro https://github.com/ruby/ruby/blob/trunk/compile.c#L4073 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, https://github.com/ruby/ruby/blob/trunk/compile.c#L4107 { 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 */ diff --git a/node.h b/node.h index f70cdc9..57a0ea3 100644 --- a/node.h +++ b/node.h @@ -384,6 +384,8 @@ typedef struct RNode { https://github.com/ruby/ruby/blob/trunk/node.h#L384 #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; diff --git a/parse.y b/parse.y index a687163..af42513 100644 --- a/parse.y +++ b/parse.y @@ -236,6 +236,7 @@ struct parser_params { https://github.com/ruby/ruby/blob/trunk/parse.y#L236 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 https://github.com/ruby/ruby/blob/trunk/parse.y#L476 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; https://github.com/ruby/ruby/blob/trunk/parse.y#L868 # 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 * https://github.com/ruby/ruby/blob/trunk/parse.y#L891 # 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 https://github.com/ruby/ruby/blob/trunk/parse.y#L1001 %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 https://github.com/ruby/ruby/blob/trunk/parse.y#L2704 /*% 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);} https://github.com/ruby/ruby/blob/trunk/parse.y#L3603 } ; -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) https://github.com/ruby/ruby/blob/trunk/parse.y#L9832 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) https://github.com/ruby/ruby/blob/trunk/parse.y#L11932 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); diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 7717647..ff8c62f 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -515,8 +515,8 @@ WARN https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L515 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 https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L525 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 https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L537 } 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/) -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/