summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--range.c18
-rw-r--r--spec/ruby/core/range/size_spec.rb62
-rw-r--r--test/ruby/test_range.rb50
3 files changed, 92 insertions, 38 deletions
diff --git a/range.c b/range.c
index e9073e53c4..a6bf0fca51 100644
--- a/range.c
+++ b/range.c
@@ -827,7 +827,12 @@ sym_each_i(VALUE v, VALUE arg)
* (1..4).size # => 4
* (1...4).size # => 3
* (1..).size # => Infinity
- * ('a'..'z').size #=> nil
+ * ('a'..'z').size # => nil
+ *
+ * If +self+ is not iterable, raises an exception:
+ *
+ * (0.5..2.5).size # TypeError
+ * (..1).size # TypeError
*
* Related: Range#count.
*/
@@ -836,7 +841,8 @@ static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
- if (rb_obj_is_kind_of(b, rb_cNumeric)) {
+
+ if (RB_INTEGER_TYPE_P(b)) {
if (rb_obj_is_kind_of(e, rb_cNumeric)) {
return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
}
@@ -844,10 +850,10 @@ range_size(VALUE range)
return DBL2NUM(HUGE_VAL);
}
}
- else if (NIL_P(b)) {
- if (rb_obj_is_kind_of(e, rb_cNumeric)) {
- return DBL2NUM(HUGE_VAL);
- }
+
+ if (!discrete_object_p(b)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(b));
}
return Qnil;
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 81ea5a3846..a1fe3ce17d 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -4,34 +4,22 @@ describe "Range#size" do
it "returns the number of elements in the range" do
(1..16).size.should == 16
(1...16).size.should == 15
-
- (1.0..16.0).size.should == 16
- (1.0...16.0).size.should == 15
- (1.0..15.9).size.should == 15
- (1.1..16.0).size.should == 15
- (1.1..15.9).size.should == 15
end
it "returns 0 if last is less than first" do
(16..0).size.should == 0
- (16.0..0.0).size.should == 0
- (Float::INFINITY..0).size.should == 0
end
it 'returns Float::INFINITY for increasing, infinite ranges' do
(0..Float::INFINITY).size.should == Float::INFINITY
- (-Float::INFINITY..0).size.should == Float::INFINITY
- (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
end
it 'returns Float::INFINITY for endless ranges if the start is numeric' do
eval("(1..)").size.should == Float::INFINITY
- eval("(0.5...)").size.should == Float::INFINITY
end
it 'returns nil for endless ranges if the start is not numeric' do
eval("('z'..)").size.should == nil
- eval("([]...)").size.should == nil
end
ruby_version_is ""..."3.2" do
@@ -43,7 +31,7 @@ describe "Range#size" do
end
end
- ruby_version_is "3.2" do
+ ruby_version_is "3.2"..."3.4" do
it 'returns Float::INFINITY for all beginless ranges if the end is numeric' do
(..1).size.should == Float::INFINITY
(...0.5).size.should == Float::INFINITY
@@ -58,6 +46,54 @@ describe "Range#size" do
end
end
+ ruby_version_is ""..."3.4" do
+ it "returns the number of elements in the range" do
+ (1.0..16.0).size.should == 16
+ (1.0...16.0).size.should == 15
+ (1.0..15.9).size.should == 15
+ (1.1..16.0).size.should == 15
+ (1.1..15.9).size.should == 15
+ end
+
+ it "returns 0 if last is less than first" do
+ (16.0..0.0).size.should == 0
+ (Float::INFINITY..0).size.should == 0
+ end
+
+ it 'returns Float::INFINITY for increasing, infinite ranges' do
+ (-Float::INFINITY..0).size.should == Float::INFINITY
+ (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
+ end
+
+ it 'returns Float::INFINITY for endless ranges if the start is numeric' do
+ eval("(0.5...)").size.should == Float::INFINITY
+ end
+
+ it 'returns nil for endless ranges if the start is not numeric' do
+ eval("([]...)").size.should == nil
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it 'raises TypeError if a range is not iterable' do
+ -> { (1.0..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0...16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (16.0..0.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..Float::INFINITY).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..1).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...0.5).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..nil).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...'o').size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("(0.5...)").size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("([]...)").size }.should raise_error(TypeError, /can't iterate from/)
+ end
+ end
+
it "returns nil if first and last are not Numeric" do
(:a..:z).size.should be_nil
('a'..'z').size.should be_nil
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 2aa69dc6a4..84b3b205f0 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -983,26 +983,38 @@ class TestRange < Test::Unit::TestCase
end
def test_size
- assert_equal 42, (1..42).size
- assert_equal 41, (1...42).size
- assert_equal 6, (1...6.3).size
- assert_equal 5, (1.1...6).size
- assert_equal 3, (1..3r).size
- assert_equal 2, (1...3r).size
- assert_equal 3, (1..3.1r).size
- assert_equal 3, (1...3.1r).size
- assert_equal 42, (1..42).each.size
+ Enumerator.product([:to_i, :to_f, :to_r].repeated_permutation(2), [1, 10], [5, 5.5], [true, false]) do |(m1, m2), beg, ende, exclude_end|
+ r = Range.new(beg.send(m1), ende.send(m2), exclude_end)
+ iterable = true
+ yielded = []
+ begin
+ r.each { yielded << _1 }
+ rescue TypeError
+ iterable = false
+ end
+
+ if iterable
+ assert_equal(yielded.size, r.size, "failed on #{r}")
+ assert_equal(yielded.size, r.each.size, "failed on #{r}")
+ else
+ assert_raise(TypeError, "failed on #{r}") { r.size }
+ assert_raise(TypeError, "failed on #{r}") { r.each.size }
+ end
+ end
+
assert_nil ("a"..."z").size
- assert_nil ("a"...).size
- assert_nil (..."z").size # [Bug #18983]
- assert_nil (nil...nil).size # [Bug #18983]
-
- assert_equal Float::INFINITY, (1...).size
- assert_equal Float::INFINITY, (1.0...).size
- assert_equal Float::INFINITY, (...1).size
- assert_equal Float::INFINITY, (...1.0).size
- assert_nil ("a"...).size
- assert_nil (..."z").size
+
+ assert_equal Float::INFINITY, (1..).size
+ assert_raise(TypeError) { (1.0..).size }
+ assert_raise(TypeError) { (1r..).size }
+ assert_nil ("a"..).size
+
+ assert_raise(TypeError) { (..1).size }
+ assert_raise(TypeError) { (..1.0).size }
+ assert_raise(TypeError) { (..1r).size }
+ assert_raise(TypeError) { (..'z').size }
+
+ assert_raise(TypeError) { (nil...nil).size }
end
def test_bsearch_typechecks_return_values