[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]