From 04a92a6764bf678919cf4b68a27496a39d6b886a Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 19 Aug 2022 13:19:03 -0700 Subject: Raise TypeError for endless non-numeric range include? Beginless ranges previously raised TypeError for this case, except for string ranges, which had unexpected behavior: ('a'..'z').include?('ww') # false (..'z').include?('ww') # previously true, now TypeError Use of include? with endless ranges could previously result in an infinite loop. This splits off a range_string_cover_internal function from range_include_internal. Fixes [Bug #18580] --- range.c | 49 +++++++++++++++++++++++++++++++++++++------------ test/ruby/test_range.rb | 11 ++++++----- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/range.c b/range.c index 0c3dc51c73..94b6e5a560 100644 --- a/range.c +++ b/range.c @@ -1679,7 +1679,9 @@ range_inspect(VALUE range) return rb_exec_recursive(inspect_range, range, 0); } -static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover); +static VALUE range_include_internal(VALUE range, VALUE val); +static VALUE range_string_cover_internal(VALUE range, VALUE val); +VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive); /* * call-seq: @@ -1723,7 +1725,7 @@ static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover static VALUE range_eqq(VALUE range, VALUE val) { - VALUE ret = range_include_internal(range, val, 1); + VALUE ret = range_string_cover_internal(range, val); if (!UNDEF_P(ret)) return ret; return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val); } @@ -1763,13 +1765,13 @@ range_eqq(VALUE range, VALUE val) static VALUE range_include(VALUE range, VALUE val) { - VALUE ret = range_include_internal(range, val, 0); + VALUE ret = range_include_internal(range, val); if (!UNDEF_P(ret)) return ret; return rb_call_super(1, &val); } static VALUE -range_include_internal(VALUE range, VALUE val, int string_use_cover) +range_string_cover_internal(VALUE range, VALUE val) { VALUE beg = RANGE_BEG(range); VALUE end = RANGE_END(range); @@ -1783,15 +1785,9 @@ range_include_internal(VALUE range, VALUE val, int string_use_cover) } else if (RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING)) { if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) { - if (string_use_cover) { - return r_cover_p(range, beg, end, val); - } - else { - VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive); - return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range)); - } + return r_cover_p(range, beg, end, val); } - else if (NIL_P(beg)) { + if (NIL_P(beg)) { VALUE r = rb_funcall(val, id_cmp, 1, end); if (NIL_P(r)) return Qfalse; if (RANGE_EXCL(range)) { @@ -1805,6 +1801,35 @@ range_include_internal(VALUE range, VALUE val, int string_use_cover) return RBOOL(rb_cmpint(r, beg, val) <= 0); } } + + if (NIL_P(beg) || NIL_P(end)) { + rb_raise(rb_eTypeError, "cannot determine inclusion in beginless/endless ranges"); + } + + return Qundef; +} + +static VALUE +range_include_internal(VALUE range, VALUE val) +{ + VALUE beg = RANGE_BEG(range); + VALUE end = RANGE_END(range); + int nv = FIXNUM_P(beg) || FIXNUM_P(end) || + linear_object_p(beg) || linear_object_p(end); + + if (nv || + !NIL_P(rb_check_to_integer(beg, "to_int")) || + !NIL_P(rb_check_to_integer(end, "to_int"))) { + return r_cover_p(range, beg, end, val); + } + else if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) { + return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range)); + } + + if (NIL_P(beg) || NIL_P(end)) { + rb_raise(rb_eTypeError, "cannot determine inclusion in beginless/endless ranges"); + } + return Qundef; } diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb index 2c07cef96e..bb5ef6df8f 100644 --- a/test/ruby/test_range.rb +++ b/test/ruby/test_range.rb @@ -546,7 +546,7 @@ class TestRange < Test::Unit::TestCase assert_not_operator('A'..'Z', :===, 'ana') assert_operator('A'.., :===, 'ANA') assert_operator(..'Z', :===, 'ANA') - assert_operator(nil..nil, :===, 'ANA') + assert_raise(TypeError) {(nil..nil) === 'ANA'} end def test_eqq_time @@ -599,13 +599,14 @@ class TestRange < Test::Unit::TestCase assert_include("a"..."z", "y") assert_not_include("a"..."z", "z") assert_not_include("a".."z", "cc") - assert_include("a".., "c") - assert_not_include("a".., "5") + assert_raise(TypeError) {("a"..).include?("c")} + assert_raise(TypeError) {("a"..).include?("5")} + assert_include(0...10, 5) assert_include(5..., 10) assert_not_include(5..., 0) - assert_include(.."z", "z") - assert_not_include(..."z", "z") + assert_raise(TypeError) {(.."z").include?("z")} + assert_raise(TypeError) {(..."z").include?("z")} assert_include(..10, 10) assert_not_include(...10, 10) end -- cgit v1.2.1