merge revision(s) c5475f4269: [Backport #18748]

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(-)
This commit is contained in:
nagachika 2023-03-25 10:31:31 +09:00
parent 02bee9d4d4
commit 477ab3f6c7
3 changed files with 83 additions and 2 deletions

54
range.c
View file

@ -1885,6 +1885,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?.
*
*/
@ -1923,7 +1966,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;