From 37eb5e74395f148782f7d67b5218fb2e66b113d7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 3 Jun 2021 12:32:44 +0900 Subject: Warn more duplicate literal hash keys Following non-special_const literals: * T_BIGNUM * T_FLOAT (non-flonum) * T_RATIONAL * T_COMPLEX --- compile.c | 16 ++++++++-------- internal/compile.h | 2 ++ parse.y | 34 +++++++++++++++++++++++++++++++++- test/ruby/test_literal.rb | 4 ++++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/compile.c b/compile.c index aedc9c55b5..1b23d2133b 100644 --- a/compile.c +++ b/compile.c @@ -1967,8 +1967,8 @@ iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl) return COMPILE_OK; } -static int -cdhash_cmp(VALUE val, VALUE lit) +int +rb_iseq_cdhash_cmp(VALUE val, VALUE lit) { int tval, tlit; @@ -2004,20 +2004,20 @@ cdhash_cmp(VALUE val, VALUE lit) else if (tlit == T_RATIONAL) { const struct RRational *rat1 = RRATIONAL(val); const struct RRational *rat2 = RRATIONAL(lit); - return cdhash_cmp(rat1->num, rat2->num) || cdhash_cmp(rat1->den, rat2->den); + return rb_iseq_cdhash_cmp(rat1->num, rat2->num) || rb_iseq_cdhash_cmp(rat1->den, rat2->den); } else if (tlit == T_COMPLEX) { const struct RComplex *comp1 = RCOMPLEX(val); const struct RComplex *comp2 = RCOMPLEX(lit); - return cdhash_cmp(comp1->real, comp2->real) || cdhash_cmp(comp1->imag, comp2->imag); + return rb_iseq_cdhash_cmp(comp1->real, comp2->real) || rb_iseq_cdhash_cmp(comp1->imag, comp2->imag); } else { UNREACHABLE_RETURN(-1); } } -static st_index_t -cdhash_hash(VALUE a) +st_index_t +rb_iseq_cdhash_hash(VALUE a) { switch (OBJ_BUILTIN_TYPE(a)) { case -1: @@ -2039,8 +2039,8 @@ cdhash_hash(VALUE a) } static const struct st_hash_type cdhash_type = { - cdhash_cmp, - cdhash_hash, + rb_iseq_cdhash_cmp, + rb_iseq_cdhash_hash, }; struct cdhash_set_label_struct { diff --git a/internal/compile.h b/internal/compile.h index c1f2a36685..99c15d6a44 100644 --- a/internal/compile.h +++ b/internal/compile.h @@ -20,6 +20,8 @@ int rb_dvar_defined(ID, const struct rb_iseq_struct *); int rb_local_defined(ID, const struct rb_iseq_struct *); const char *rb_insns_name(int i); VALUE rb_insns_name_array(void); +int rb_iseq_cdhash_cmp(VALUE val, VALUE lit); +st_index_t rb_iseq_cdhash_hash(VALUE a); /* iseq.c */ int rb_vm_insn_addr2insn(const void *); diff --git a/parse.y b/parse.y index e669adbcd5..0b69bc1d7e 100644 --- a/parse.y +++ b/parse.y @@ -12184,10 +12184,42 @@ append_literal_keys(st_data_t k, st_data_t v, st_data_t h) return ST_CONTINUE; } +static bool +hash_literal_key_p(VALUE k) +{ + switch (OBJ_BUILTIN_TYPE(k)) { + case T_NODE: + case T_REGEXP: + return false; + default: + return true; + } +} + +static int +literal_cmp(VALUE val, VALUE lit) +{ + if (val == lit) return 0; + if (!hash_literal_key_p(val) || !hash_literal_key_p(lit)) return -1; + return rb_iseq_cdhash_cmp(val, lit); +} + +static st_index_t +literal_hash(VALUE a) +{ + if (!hash_literal_key_p(a)) return (st_index_t)a; + return rb_iseq_cdhash_hash(a); +} + +static const struct st_hash_type literal_type = { + literal_cmp, + literal_hash, +}; + static NODE * remove_duplicate_keys(struct parser_params *p, NODE *hash) { - st_table *literal_keys = st_init_numtable_with_size(hash->nd_alen / 2); + st_table *literal_keys = st_init_table_with_size(&literal_type, hash->nd_alen / 2); NODE *result = 0; rb_code_location_t loc = hash->nd_loc; while (hash && hash->nd_head && hash->nd_next) { diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb index 10cb09b833..ed93b74cff 100644 --- a/test/ruby/test_literal.rb +++ b/test/ruby/test_literal.rb @@ -480,6 +480,10 @@ class TestRubyLiteral < Test::Unit::TestCase '"a"', '1000', '1.0', + '1_000_000_000_000_000_000_000', + '1.0r', + '1.0i', + '1.72723e-77', ) do |key| assert_warning(/key #{Regexp.quote(eval(key).inspect)} is duplicated/) do eval("{#{key} => :bar, #{key} => :foo}") -- cgit v1.2.3