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

ruby-changes:72074

From: Jeremy <ko1@a...>
Date: Tue, 7 Jun 2022 01:59:31 +0900 (JST)
Subject: [ruby-changes:72074] c5475f4269 (master): Fix Range#cover? returning true for beginless ranges of different types

https://git.ruby-lang.org/ruby.git/commit/?id=c5475f4269

From c5475f42694eff35465c3332e0182c0611ca5918 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 21 Apr 2022 19:46:17 -0700
Subject: Fix Range#cover? returning true for beginless ranges of different
 types

Previously `(2..).cover?("2"..)` was false, but
`(..2).cover?(.."2")` was true.  This changes it so both are false,
treating beginless ranges the same as endless ranges in regards to
type checks.

This also adds documentation to #cover? to describe behavior with
beginless and endless ranges, testing each documentation example,
which is how this bug was found.

Fixes [Bug #18155]
---
 range.c                 | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-
 test/ruby/test_range.rb | 29 ++++++++++++++++++++++++++
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/range.c b/range.c
index 44c43047fd..e45e405f5a 100644
--- a/range.c
+++ b/range.c
@@ -1888,6 +1888,49 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val); https://github.com/ruby/ruby/blob/trunk/range.c#L1888
  *  - An internal call to <tt><=></tt> returns +nil+;
  *    that is, the operands are not comparable.
  *
+ * Beginless ranges cover all values of the same type before the end,
+ * excluding the end for exclusive ranges. Beginless ranges cover
+ * ranges that end before the end of the beginless range, or at the
+ * end of the beginless range for inclusive ranges.
+ *
+ *    (..2).cover?(1)     # => true
+ *    (..2).cover?(2)     # => true
+ *    (..2).cover?(3)     # => false
+ *    (...2).cover?(2)    # => false
+ *    (..2).cover?("2")   # => false
+ *    (..2).cover?(..2)   # => true
+ *    (..2).cover?(...2)  # => true
+ *    (..2).cover?(.."2") # => false
+ *    (...2).cover?(..2)  # => false
+ *
+ * Endless ranges cover all values of the same type after the
+ * beginning. Endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ *    (2..).cover?(1)     # => false
+ *    (2..).cover?(3)     # => true
+ *    (2...).cover?(3)    # => true
+ *    (2..).cover?(2)     # => true
+ *    (2..).cover?("2")   # => false
+ *    (2..).cover?(2..)   # => true
+ *    (2..).cover?(2...)  # => true
+ *    (2..).cover?("2"..) # => false
+ *    (2...).cover?(2..)  # => false
+ *    (2...).cover?(3...) # => true
+ *    (2...).cover?(3..)  # => false
+ *    (3..).cover?(2..)   # => false
+ *
+ * Ranges that are both beginless and endless cover all values and
+ * ranges, and return true for all arguments, with the exception that
+ * beginless and endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ *    (nil...).cover?(Object.new) # => true
+ *    (nil...).cover?(nil...)     # => true
+ *    (nil..).cover?(nil...)      # => true
+ *    (nil...).cover?(nil..)      # => false
+ *    (nil...).cover?(1..)        # => false
+ *
  *  Related: Range#include?.
  *
  */
@@ -1926,7 +1969,16 @@ r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val) https://github.com/ruby/ruby/blob/trunk/range.c#L1969
     if (!NIL_P(val_beg) && !NIL_P(val_end) && r_less(val_beg, val_end) > (EXCL(val) ? -1 : 0)) return FALSE;
     if (!NIL_P(val_beg) && !r_cover_p(range, beg, end, val_beg)) return FALSE;
 
-    cmp_end = r_less(end, val_end);
+
+    if (!NIL_P(val_end) && !NIL_P(end)) {
+        VALUE r_cmp_end = rb_funcall(end, id_cmp, 1, val_end);
+        if (NIL_P(r_cmp_end)) return FALSE;
+        cmp_end = rb_cmpint(r_cmp_end, end, val_end);
+    }
+    else {
+        cmp_end = r_less(end, val_end);
+    }
+
 
     if (EXCL(range) == EXCL(val)) {
         return cmp_end >= 0;
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 8789eca749..0df0a985ad 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -670,6 +670,35 @@ class TestRange < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_range.rb#L670
     assert_not_operator(1..10, :cover?, 3...3)
     assert_not_operator('aa'..'zz', :cover?, 'aa'...'zzz')
     assert_not_operator(1..10, :cover?, 1...10.1)
+
+    assert_operator(..2, :cover?, 1)
+    assert_operator(..2, :cover?, 2)
+    assert_not_operator(..2, :cover?, 3)
+    assert_not_operator(...2, :cover?, 2)
+    assert_not_operator(..2, :cover?, "2")
+    assert_operator(..2, :cover?, ..2)
+    assert_operator(..2, :cover?, ...2)
+    assert_not_operator(..2, :cover?, .."2")
+    assert_not_operator(...2, :cover?, ..2)
+
+    assert_not_operator(2.., :cover?, 1)
+    assert_operator(2.., :cover?, 2)
+    assert_operator(2..., :cover?, 3)
+    assert_operator(2.., :cover?, 2)
+    assert_not_operator(2.., :cover?, "2")
+    assert_operator(2.., :cover?, 2..)
+    assert_operator(2.., :cover?, 2...)
+    assert_not_operator(2.., :cover?, "2"..)
+    assert_not_operator(2..., :cover?, 2..)
+    assert_operator(2..., :cover?, 3...)
+    assert_not_operator(2..., :cover?, 3..)
+    assert_not_operator(3.., :cover?, 2..)
+
+    assert_operator(nil..., :cover?, Object.new)
+    assert_operator(nil..., :cover?, nil...)
+    assert_operator(nil.., :cover?, nil...)
+    assert_not_operator(nil..., :cover?, nil..)
+    assert_not_operator(nil..., :cover?, 1..)
   end
 
   def test_beg_len
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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