diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf7c14cf4a6..39f420a93cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ Compatibility: * Modify `Struct#{inspect,to_s}` to match MRI when the struct is nested inside of an anonymous class or module (@st0012, @nirvdrum). * `Fiber.current` and `Fiber#transfer` are available without `require 'fiber'` like in CRuby 3.1 (#2733, @eregon). * Add `freeze` keyword argument to `Marshal.load` (#2733, @andrykonchin). +* Add `Integer.try_convert` (#2733, @moste00, @eregon). Performance: diff --git a/spec/ruby/core/array/try_convert_spec.rb b/spec/ruby/core/array/try_convert_spec.rb index 47b4722d803a..bea881500658 100644 --- a/spec/ruby/core/array/try_convert_spec.rb +++ b/spec/ruby/core/array/try_convert_spec.rb @@ -39,7 +39,7 @@ it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do obj = mock("to_ary") obj.should_receive(:to_ary).and_return(Object.new) - -> { Array.try_convert obj }.should raise_error(TypeError) + -> { Array.try_convert obj }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_ary gives Object)") end it "does not rescue exceptions raised by #to_ary" do diff --git a/spec/ruby/core/hash/try_convert_spec.rb b/spec/ruby/core/hash/try_convert_spec.rb index 44195c501089..d359ae49d8cd 100644 --- a/spec/ruby/core/hash/try_convert_spec.rb +++ b/spec/ruby/core/hash/try_convert_spec.rb @@ -39,7 +39,7 @@ it "sends #to_hash to the argument and raises TypeError if it's not a kind of Hash" do obj = mock("to_hash") obj.should_receive(:to_hash).and_return(Object.new) - -> { Hash.try_convert obj }.should raise_error(TypeError) + -> { Hash.try_convert obj }.should raise_error(TypeError, "can't convert MockObject to Hash (MockObject#to_hash gives Object)") end it "does not rescue exceptions raised by #to_hash" do diff --git a/spec/ruby/core/integer/try_convert_spec.rb b/spec/ruby/core/integer/try_convert_spec.rb index 45c66eec7944..4bc7d3851a0b 100644 --- a/spec/ruby/core/integer/try_convert_spec.rb +++ b/spec/ruby/core/integer/try_convert_spec.rb @@ -28,7 +28,17 @@ it "sends #to_int to the argument and raises TypeError if it's not a kind of Integer" do obj = mock("to_int") obj.should_receive(:to_int).and_return(Object.new) - -> { Integer.try_convert obj }.should raise_error(TypeError) + -> { + Integer.try_convert obj + }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Object)") + end + + it "responds with a different error message when it raises a TypeError, depending on the type of the non-Integer object :to_int returns" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return("A String") + -> { + Integer.try_convert obj + }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives String)") end it "does not rescue exceptions raised by #to_int" do diff --git a/spec/ruby/core/io/try_convert_spec.rb b/spec/ruby/core/io/try_convert_spec.rb index 5fbd10b6faf3..a9e99de7aaa9 100644 --- a/spec/ruby/core/io/try_convert_spec.rb +++ b/spec/ruby/core/io/try_convert_spec.rb @@ -38,7 +38,7 @@ it "raises a TypeError if the object does not return an IO from #to_io" do obj = mock("io") obj.should_receive(:to_io).and_return("io") - -> { IO.try_convert(obj) }.should raise_error(TypeError) + -> { IO.try_convert(obj) }.should raise_error(TypeError, "can't convert MockObject to IO (MockObject#to_io gives String)") end it "propagates an exception raised by #to_io" do diff --git a/spec/ruby/core/regexp/try_convert_spec.rb b/spec/ruby/core/regexp/try_convert_spec.rb index be567e2130da..e775dbe97160 100644 --- a/spec/ruby/core/regexp/try_convert_spec.rb +++ b/spec/ruby/core/regexp/try_convert_spec.rb @@ -18,4 +18,10 @@ rex.should_receive(:to_regexp).and_return(/(p(a)t[e]rn)/) Regexp.try_convert(rex).should == /(p(a)t[e]rn)/ end + + it "raises a TypeError if the object does not return an Regexp from #to_regexp" do + obj = mock("regexp") + obj.should_receive(:to_regexp).and_return("string") + -> { Regexp.try_convert(obj) }.should raise_error(TypeError, "can't convert MockObject to Regexp (MockObject#to_regexp gives String)") + end end diff --git a/spec/ruby/core/string/try_convert_spec.rb b/spec/ruby/core/string/try_convert_spec.rb index 84415c4a7577..72ce5dd8b23a 100644 --- a/spec/ruby/core/string/try_convert_spec.rb +++ b/spec/ruby/core/string/try_convert_spec.rb @@ -39,7 +39,7 @@ it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do obj = mock("to_str") obj.should_receive(:to_str).and_return(Object.new) - -> { String.try_convert obj }.should raise_error(TypeError) + -> { String.try_convert obj }.should raise_error(TypeError, "can't convert MockObject to String (MockObject#to_str gives Object)") end it "does not rescue exceptions raised by #to_str" do diff --git a/spec/tags/core/integer/try_convert_tags.txt b/spec/tags/core/integer/try_convert_tags.txt deleted file mode 100644 index 335849e4e5ca..000000000000 --- a/spec/tags/core/integer/try_convert_tags.txt +++ /dev/null @@ -1,6 +0,0 @@ -fails:Integer.try_convert returns the argument if it's an Integer -fails:Integer.try_convert returns nil when the argument does not respond to #to_int -fails:Integer.try_convert sends #to_int to the argument and returns the result if it's nil -fails:Integer.try_convert sends #to_int to the argument and returns the result if it's an Integer -fails:Integer.try_convert sends #to_int to the argument and raises TypeError if it's not a kind of Integer -fails:Integer.try_convert does not rescue exceptions raised by #to_int diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 5acfc97ffd01..ca167fdac4d3 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -332,6 +332,10 @@ def zero? self == 0 end + def self.try_convert(obj) + Truffle::Type.try_convert(obj, Integer, :to_int) + end + def self.sqrt(n) n = Primitive.rb_to_int(n) raise Math::DomainError if n.negative? diff --git a/src/main/ruby/truffleruby/core/type.rb b/src/main/ruby/truffleruby/core/type.rb index 19805c47de55..98ab8b5ae201 100644 --- a/src/main/ruby/truffleruby/core/type.rb +++ b/src/main/ruby/truffleruby/core/type.rb @@ -367,7 +367,7 @@ def self.execute_try_convert(obj, cls, meth) if Primitive.nil?(ret) || Primitive.object_kind_of?(ret, cls) ret else - raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{Primitive.object_class(ret)})" + conversion_mismatch(obj, cls, meth, ret) end end