summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2018-07-28 00:07:56 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2019-05-05 00:29:12 +0900
commitff21e75d32e27a2b362ed53fb471828876b54418 (patch)
treef32f96d4bfb0ecf917428a893d9aff9bb84a47cc
parent848edb03f8d3e5f5e97d3ea45fec592d87d73b05 (diff)
parse.y: duplicated when clause warning
* parse.y (case_args): moved "duplicated when clause" warning from compile phase, so that `ruby -wc` shows them.
-rw-r--r--compile.c17
-rw-r--r--node.h2
-rw-r--r--parse.y94
-rw-r--r--test/ruby/test_syntax.rb17
4 files changed, 108 insertions, 22 deletions
diff --git a/compile.c b/compile.c
index ca117c2853..7dbf244f36 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
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 */
diff --git a/node.h b/node.h
index f70cdc959a..57a0ea3932 100644
--- a/node.h
+++ b/node.h
@@ -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;
diff --git a/parse.y b/parse.y
index a687163aa9..af425133c9 100644
--- a/parse.y
+++ b/parse.y
@@ -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);
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 771764720b..ff8c62f6a1 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -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/)