mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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]
This commit is contained in:
parent
7cda7fbbdc
commit
c5475f4269
Notes:
git
2022-06-07 01:59:42 +09:00
2 changed files with 82 additions and 1 deletions
54
range.c
54
range.c
|
@ -1888,6 +1888,49 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
|
|||
* - 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)
|
|||
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;
|
||||
|
|
|
@ -670,6 +670,35 @@ class TestRange < Test::Unit::TestCase
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue