Refactor type error to be more consistent

[Bug #21864]

Co-Authored-By:  Benoit Daloze <eregontp@gmail.com>
This commit is contained in:
Jean Boussier 2026-02-05 20:38:00 +01:00 committed by Benoit Daloze
parent 96d0064097
commit d066b9e01c
Notes: git 2026-02-07 09:07:03 +00:00
36 changed files with 158 additions and 112 deletions

View File

@ -20,6 +20,7 @@
#include "internal/array.h"
#include "internal/class.h"
#include "internal/complex.h"
#include "internal/error.h"
#include "internal/math.h"
#include "internal/numeric.h"
#include "internal/object.h"
@ -2411,7 +2412,7 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
{
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
rb_raise(rb_eTypeError, "can't convert nil into Complex");
rb_cant_convert(Qnil, "Complex");
}
if (RB_TYPE_P(a1, T_STRING)) {

6
depend
View File

@ -1892,6 +1892,7 @@ complex.$(OBJEXT): $(top_srcdir)/internal/box.h
complex.$(OBJEXT): $(top_srcdir)/internal/class.h
complex.$(OBJEXT): $(top_srcdir)/internal/compilers.h
complex.$(OBJEXT): $(top_srcdir)/internal/complex.h
complex.$(OBJEXT): $(top_srcdir)/internal/error.h
complex.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
complex.$(OBJEXT): $(top_srcdir)/internal/gc.h
complex.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@ -9963,6 +9964,7 @@ numeric.$(OBJEXT): $(top_srcdir)/internal/class.h
numeric.$(OBJEXT): $(top_srcdir)/internal/compilers.h
numeric.$(OBJEXT): $(top_srcdir)/internal/complex.h
numeric.$(OBJEXT): $(top_srcdir)/internal/enumerator.h
numeric.$(OBJEXT): $(top_srcdir)/internal/error.h
numeric.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
numeric.$(OBJEXT): $(top_srcdir)/internal/gc.h
numeric.$(OBJEXT): $(top_srcdir)/internal/hash.h
@ -13221,6 +13223,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/box.h
rational.$(OBJEXT): $(top_srcdir)/internal/class.h
rational.$(OBJEXT): $(top_srcdir)/internal/compilers.h
rational.$(OBJEXT): $(top_srcdir)/internal/complex.h
rational.$(OBJEXT): $(top_srcdir)/internal/error.h
rational.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
rational.$(OBJEXT): $(top_srcdir)/internal/gc.h
rational.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@ -13231,6 +13234,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
rational.$(OBJEXT): $(top_srcdir)/internal/serial.h
rational.$(OBJEXT): $(top_srcdir)/internal/set_table.h
rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
rational.$(OBJEXT): $(top_srcdir)/internal/string.h
rational.$(OBJEXT): $(top_srcdir)/internal/variable.h
rational.$(OBJEXT): $(top_srcdir)/internal/vm.h
rational.$(OBJEXT): $(top_srcdir)/internal/warnings.h
@ -13248,6 +13252,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rational.$(OBJEXT): {$(VPATH)}config.h
rational.$(OBJEXT): {$(VPATH)}constant.h
rational.$(OBJEXT): {$(VPATH)}defines.h
rational.$(OBJEXT): {$(VPATH)}encindex.h
rational.$(OBJEXT): {$(VPATH)}encoding.h
rational.$(OBJEXT): {$(VPATH)}id.h
rational.$(OBJEXT): {$(VPATH)}id_table.h
@ -17669,6 +17674,7 @@ time.$(OBJEXT): $(top_srcdir)/internal/bits.h
time.$(OBJEXT): $(top_srcdir)/internal/box.h
time.$(OBJEXT): $(top_srcdir)/internal/compar.h
time.$(OBJEXT): $(top_srcdir)/internal/compilers.h
time.$(OBJEXT): $(top_srcdir)/internal/error.h
time.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
time.$(OBJEXT): $(top_srcdir)/internal/gc.h
time.$(OBJEXT): $(top_srcdir)/internal/hash.h

62
error.c
View File

@ -2758,6 +2758,68 @@ nometh_err_private_call_p(VALUE self)
return rb_attr_get(self, id_private_call_p);
}
static const char *
type_err_cname(VALUE val)
{
if (NIL_P(val)) {
return "nil";
}
else if (val == Qtrue) {
return "true";
}
else if (val == Qfalse) {
return "false";
}
return NULL;
}
NORETURN(static void type_err_raise(VALUE val, const char *tname, const char *msg));
static void
type_err_raise(VALUE val, const char *tname, const char *msg)
{
const char *cname = type_err_cname(val);
rb_encoding *enc = rb_utf8_encoding();
if (cname) {
rb_enc_raise(enc, rb_eTypeError, "%s %s into %s", msg, cname, tname);
}
rb_enc_raise(enc, rb_eTypeError, "%s %"PRIsVALUE" into %s", msg, rb_obj_class(val), tname);
}
NORETURN(void rb_no_implicit_conversion(VALUE val, const char *tname));
void
rb_no_implicit_conversion(VALUE val, const char *tname)
{
type_err_raise(val, tname, "no implicit conversion of");
}
NORETURN(void rb_cant_convert(VALUE val, const char *tname));
void
rb_cant_convert(VALUE val, const char *tname)
{
type_err_raise(val, tname, "can't convert");
}
NORETURN(void rb_cant_convert_invalid_return(VALUE val, const char *tname, const char *method_name, VALUE ret));
void
rb_cant_convert_invalid_return(VALUE val, const char *tname, const char *method_name, VALUE ret)
{
const char *cname = type_err_cname(val);
rb_encoding *enc = rb_utf8_encoding();
if (cname) {
rb_enc_raise(
enc, rb_eTypeError, "can't convert %s into %s (%s#%s gives %s)",
cname, tname, cname, method_name, type_err_cname(ret));
}
VALUE klass = rb_obj_class(val);
const char *retname = type_err_cname(ret);
if (!retname) {
retname = rb_obj_classname(ret);
}
rb_enc_raise(
enc, rb_eTypeError, "can't convert %"PRIsVALUE" into %s (%"PRIsVALUE"#%s gives %s)",
klass, tname, klass, method_name, retname);
}
void
rb_invalid_str(const char *str, const char *type)
{

View File

@ -186,6 +186,9 @@ static inline void Check_Type(VALUE v, enum ruby_value_type t);
static inline bool rb_typeddata_is_instance_of_inline(VALUE obj, const rb_data_type_t *data_type);
#define rb_typeddata_is_instance_of rb_typeddata_is_instance_of_inline
void rb_bug_without_die(const char *fmt, ...);
NORETURN(void rb_no_implicit_conversion(VALUE val, const char *tname));
NORETURN(void rb_cant_convert(VALUE val, const char *tname));
NORETURN(void rb_cant_convert_invalid_return(VALUE val, const char *tname, const char *method_name, VALUE ret));
RUBY_SYMBOL_EXPORT_BEGIN
/* error.c (export) */

View File

@ -30,6 +30,7 @@
#include "internal/compilers.h"
#include "internal/complex.h"
#include "internal/enumerator.h"
#include "internal/error.h"
#include "internal/gc.h"
#include "internal/hash.h"
#include "internal/numeric.h"
@ -3102,7 +3103,7 @@ rb_num2long(VALUE val)
{
again:
if (NIL_P(val)) {
rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
rb_no_implicit_conversion(val, "Integer");
}
if (FIXNUM_P(val)) return FIX2LONG(val);
@ -3130,7 +3131,7 @@ rb_num2ulong_internal(VALUE val, int *wrap_p)
{
again:
if (NIL_P(val)) {
rb_raise(rb_eTypeError, "no implicit conversion of nil into Integer");
rb_no_implicit_conversion(val, "Integer");
}
if (FIXNUM_P(val)) {
@ -3373,7 +3374,7 @@ LONG_LONG
rb_num2ll(VALUE val)
{
if (NIL_P(val)) {
rb_raise(rb_eTypeError, "no implicit conversion from nil");
rb_no_implicit_conversion(val, "Integer");
}
if (FIXNUM_P(val)) return (LONG_LONG)FIX2LONG(val);
@ -3390,11 +3391,8 @@ rb_num2ll(VALUE val)
else if (RB_BIGNUM_TYPE_P(val)) {
return rb_big2ll(val);
}
else if (RB_TYPE_P(val, T_STRING)) {
rb_raise(rb_eTypeError, "no implicit conversion from string");
}
else if (RB_TYPE_P(val, T_TRUE) || RB_TYPE_P(val, T_FALSE)) {
rb_raise(rb_eTypeError, "no implicit conversion from boolean");
else if (val == Qfalse || val == Qtrue || RB_TYPE_P(val, T_STRING)) {
rb_no_implicit_conversion(val, "Integer");
}
val = rb_to_int(val);
@ -3405,7 +3403,7 @@ unsigned LONG_LONG
rb_num2ull(VALUE val)
{
if (NIL_P(val)) {
rb_raise(rb_eTypeError, "no implicit conversion of nil into Integer");
rb_no_implicit_conversion(val, "Integer");
}
else if (FIXNUM_P(val)) {
return (LONG_LONG)FIX2LONG(val); /* this is FIX2LONG, intended */

View File

@ -3332,19 +3332,12 @@ convert_type_with_id(VALUE val, const char *tname, ID method, int raise, int ind
VALUE r = rb_check_funcall(val, method, 0, 0);
if (UNDEF_P(r)) {
if (raise) {
const char *msg =
((index < 0 ? conv_method_index(rb_id2name(method)) : index)
< IMPLICIT_CONVERSIONS) ?
"no implicit conversion of" : "can't convert";
const char *cname = NIL_P(val) ? "nil" :
val == Qtrue ? "true" :
val == Qfalse ? "false" :
NULL;
if (cname)
rb_raise(rb_eTypeError, "%s %s into %s", msg, cname, tname);
rb_raise(rb_eTypeError, "%s %"PRIsVALUE" into %s", msg,
rb_obj_class(val),
tname);
if ((index < 0 ? conv_method_index(rb_id2name(method)) : index) < IMPLICIT_CONVERSIONS) {
rb_no_implicit_conversion(val, tname);
}
else {
rb_cant_convert(val, tname);
}
}
return Qnil;
}
@ -3360,17 +3353,6 @@ convert_type(VALUE val, const char *tname, const char *method, int raise)
return convert_type_with_id(val, tname, m, raise, i);
}
/*! \private */
NORETURN(static void conversion_mismatch(VALUE, const char *, const char *, VALUE));
static void
conversion_mismatch(VALUE val, const char *tname, const char *method, VALUE result)
{
VALUE cname = rb_obj_class(val);
rb_raise(rb_eTypeError,
"can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
cname, tname, cname, method, rb_obj_class(result));
}
VALUE
rb_convert_type(VALUE val, int type, const char *tname, const char *method)
{
@ -3379,7 +3361,7 @@ rb_convert_type(VALUE val, int type, const char *tname, const char *method)
if (TYPE(val) == type) return val;
v = convert_type(val, tname, method, TRUE);
if (TYPE(v) != type) {
conversion_mismatch(val, tname, method, v);
rb_cant_convert_invalid_return(val, tname, method, v);
}
return v;
}
@ -3393,7 +3375,7 @@ rb_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
if (TYPE(val) == type) return val;
v = convert_type_with_id(val, tname, method, TRUE, -1);
if (TYPE(v) != type) {
conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
rb_cant_convert_invalid_return(val, tname, rb_id2name(method), v);
}
return v;
}
@ -3408,7 +3390,7 @@ rb_check_convert_type(VALUE val, int type, const char *tname, const char *method
v = convert_type(val, tname, method, FALSE);
if (NIL_P(v)) return Qnil;
if (TYPE(v) != type) {
conversion_mismatch(val, tname, method, v);
rb_cant_convert_invalid_return(val, tname, method, v);
}
return v;
}
@ -3424,7 +3406,7 @@ rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
v = convert_type_with_id(val, tname, method, FALSE, -1);
if (NIL_P(v)) return Qnil;
if (TYPE(v) != type) {
conversion_mismatch(val, tname, RSTRING_PTR(rb_id2str(method)), v);
rb_cant_convert_invalid_return(val, tname, rb_id2name(method), v);
}
return v;
}
@ -3450,7 +3432,7 @@ rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise
return Qnil;
}
if (!RB_INTEGER_TYPE_P(v)) {
conversion_mismatch(val, "Integer", method, v);
rb_cant_convert_invalid_return(val, "Integer", method, v);
}
GET_EC()->cfp = current_cfp;
return v;
@ -3527,7 +3509,7 @@ rb_convert_to_integer(VALUE val, int base, int raise_exception)
}
else if (NIL_P(val)) {
if (!raise_exception) return Qnil;
rb_raise(rb_eTypeError, "can't convert nil into Integer");
rb_cant_convert(val, "Integer");
}
tmp = rb_protect(rb_check_to_int, val, NULL);
@ -3821,18 +3803,6 @@ rat2dbl_without_to_f(VALUE x)
}
/*! \endcond */
static inline void
conversion_to_float(VALUE val)
{
special_const_to_float(val, "can't convert ", " into Float");
}
static inline void
implicit_conversion_to_float(VALUE val)
{
special_const_to_float(val, "no implicit conversion to float from ", "");
}
static int
to_float(VALUE *valp, int raise_exception)
{
@ -3846,7 +3816,7 @@ to_float(VALUE *valp, int raise_exception)
return T_FLOAT;
}
else if (raise_exception) {
conversion_to_float(val);
rb_cant_convert(val, "Float");
}
}
else {
@ -3926,8 +3896,7 @@ static VALUE
numeric_to_float(VALUE val)
{
if (!rb_obj_is_kind_of(val, rb_cNumeric)) {
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into Float",
rb_obj_class(val));
rb_cant_convert(val, "Float");
}
return rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
@ -3971,7 +3940,7 @@ rb_num_to_dbl(VALUE val)
return rb_float_flonum_value(val);
}
else {
conversion_to_float(val);
rb_cant_convert(val, "Float");
}
}
else {
@ -4005,7 +3974,7 @@ rb_num2dbl(VALUE val)
return rb_float_flonum_value(val);
}
else {
implicit_conversion_to_float(val);
rb_no_implicit_conversion(val, "Float");
}
}
else {
@ -4017,7 +3986,7 @@ rb_num2dbl(VALUE val)
case T_RATIONAL:
return rat2dbl_without_to_f(val);
case T_STRING:
rb_raise(rb_eTypeError, "no implicit conversion to float from string");
rb_no_implicit_conversion(val, "Float");
default:
break;
}
@ -4111,7 +4080,7 @@ rb_Hash(VALUE val)
if (NIL_P(tmp)) {
if (RB_TYPE_P(val, T_ARRAY) && RARRAY_LEN(val) == 0)
return rb_hash_new();
rb_raise(rb_eTypeError, "can't convert %s into Hash", rb_obj_classname(val));
rb_cant_convert(val, "Hash");
}
return tmp;
}

View File

@ -27,6 +27,7 @@
#include "internal.h"
#include "internal/array.h"
#include "internal/complex.h"
#include "internal/error.h"
#include "internal/gc.h"
#include "internal/numeric.h"
#include "internal/object.h"
@ -2562,7 +2563,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
rb_raise(rb_eTypeError, "can't convert nil into Rational");
rb_cant_convert(Qnil, "Rational");
}
if (RB_TYPE_P(a1, T_COMPLEX)) {

View File

@ -90,4 +90,15 @@ module MSpecMatchers
private def raise_error(exception = Exception, message = nil, &block)
RaiseErrorMatcher.new(exception, message, &block)
end
# CRuby < 4.1 has inconsistent coercion errors:
# https://bugs.ruby-lang.org/issues/21864
# This matcher ignores the message on CRuby < 4.1
# and checks the message for all other cases, including other Rubies
private def raise_consistent_error(exception = Exception, message = nil, &block)
if RUBY_ENGINE == "ruby" and ruby_version_is ""..."4.1"
message = nil
end
RaiseErrorMatcher.new(exception, message, &block)
end
end

View File

@ -39,7 +39,7 @@ describe "Array.try_convert" do
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, "can't convert MockObject to Array (MockObject#to_ary gives Object)")
-> { Array.try_convert obj }.should raise_consistent_error(TypeError, "can't convert MockObject into Array (MockObject#to_ary gives Object)")
end
it "does not rescue exceptions raised by #to_ary" do

View File

@ -284,7 +284,7 @@ describe "BasicObject#instance_eval" do
def source_code.to_str() :symbol end
a = BasicObject.new
-> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/)
-> { a.instance_eval(source_code) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
end
it "converts filename argument with #to_str method" do
@ -303,7 +303,7 @@ describe "BasicObject#instance_eval" do
filename = Object.new
def filename.to_str() :symbol end
-> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/)
-> { Object.new.instance_eval("raise", filename) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
end
it "converts lineno argument with #to_int method" do
@ -322,6 +322,6 @@ describe "BasicObject#instance_eval" do
lineno = Object.new
def lineno.to_int() :symbol end
-> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/)
-> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_consistent_error(TypeError, /can't convert Object into Integer/)
end
end

View File

@ -106,7 +106,7 @@ describe "Data#deconstruct_keys" do
-> {
d.deconstruct_keys([key])
}.should raise_error(TypeError, /can't convert MockObject to Integer/)
}.should raise_consistent_error(TypeError, /can't convert MockObject into Integer/)
end
it "raises TypeError if index is not a String, a Symbol and not convertible to Integer " do

View File

@ -54,7 +54,7 @@ platform_is_not :windows do
it "raises TypeError when value cannot be converted to Integer" do
-> {
Dir.for_fd(nil)
}.should raise_error(TypeError, "no implicit conversion from nil to integer")
}.should raise_consistent_error(TypeError, "no implicit conversion of nil into Integer")
end
it "raises a SystemCallError if the file descriptor given is not valid" do

View File

@ -39,7 +39,7 @@ describe "Hash.try_convert" do
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, "can't convert MockObject to Hash (MockObject#to_hash gives Object)")
-> { Hash.try_convert obj }.should raise_consistent_error(TypeError, "can't convert MockObject into Hash (MockObject#to_hash gives Object)")
end
it "does not rescue exceptions raised by #to_hash" do

View File

@ -29,7 +29,7 @@ describe "Integer.try_convert" do
obj.should_receive(:to_int).and_return(Object.new)
-> {
Integer.try_convert obj
}.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Object)")
}.should raise_consistent_error(TypeError, "can't convert MockObject into 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
@ -37,7 +37,7 @@ describe "Integer.try_convert" do
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)")
}.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives String)")
end
it "does not rescue exceptions raised by #to_int" do

View File

@ -96,7 +96,7 @@ describe "IO#lineno=" do
it "raises TypeError if cannot convert argument to Integer implicitly" do
-> { @io.lineno = "1" }.should raise_error(TypeError, 'no implicit conversion of String into Integer')
-> { @io.lineno = nil }.should raise_error(TypeError, 'no implicit conversion from nil to integer')
-> { @io.lineno = nil }.should raise_consistent_error(TypeError, 'no implicit conversion of nil into Integer')
end
it "does not accept Integers that don't fit in a C int" do

View File

@ -38,7 +38,7 @@ describe "IO.try_convert" do
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, "can't convert MockObject to IO (MockObject#to_io gives String)")
-> { IO.try_convert(obj) }.should raise_consistent_error(TypeError, "can't convert MockObject into IO (MockObject#to_io gives String)")
end
it "propagates an exception raised by #to_io" do

View File

@ -131,7 +131,7 @@ describe "Kernel.Rational" do
obj = Object.new
def obj.to_r; []; end
-> { Rational(obj) }.should raise_error(TypeError, "can't convert Object to Rational (Object#to_r gives Array)")
-> { Rational(obj) }.should raise_consistent_error(TypeError, "can't convert Object into Rational (Object#to_r gives Array)")
end
end

View File

@ -306,13 +306,13 @@ describe :kernel_sprintf, shared: true do
it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
-> {
@method.call("%c", [])
}.should raise_error(TypeError, /no implicit conversion of Array into Integer/)
}.should raise_consistent_error(TypeError, /no implicit conversion of Array into Integer/)
end
it "raises TypeError if argument is nil" do
-> {
@method.call("%c", nil)
}.should raise_error(TypeError, /no implicit conversion from nil to integer/)
}.should raise_consistent_error(TypeError, /no implicit conversion of nil into Integer/)
end
it "tries to convert argument to String with to_str" do
@ -341,7 +341,7 @@ describe :kernel_sprintf, shared: true do
-> {
@method.call("%c", obj)
}.should raise_error(TypeError, /can't convert BasicObject to String/)
}.should raise_consistent_error(TypeError, /can't convert BasicObject into String/)
end
it "raises TypeError if converting to Integer with to_int returns non-Integer" do
@ -352,7 +352,7 @@ describe :kernel_sprintf, shared: true do
-> {
@method.call("%c", obj)
}.should raise_error(TypeError, /can't convert BasicObject to Integer/)
}.should raise_consistent_error(TypeError, /can't convert BasicObject into Integer/)
end
end

View File

@ -254,7 +254,7 @@ describe "Module#define_method" do
-> {
Class.new { define_method(obj, -> {}) }
}.should raise_error(TypeError, /can't convert Object to String/)
}.should raise_consistent_error(TypeError, /can't convert Object into String/)
end
it "raises a TypeError when the given method is no Method/Proc" do

View File

@ -79,7 +79,7 @@ describe "Module#instance_method" do
obj = Object.new
def obj.to_str() [] end
-> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_error(TypeError, /can't convert Object to String/)
-> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
end
it "raises a NameError if the method has been undefined" do

View File

@ -66,7 +66,7 @@ describe :module_class_eval, shared: true do
it "raises a TypeError when the given filename can't be converted to string using to_str" do
(file = mock('123')).should_receive(:to_str).and_return(123)
-> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError, /can't convert MockObject to String/)
-> { ModuleSpecs.send(@method, "1+1", file) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
end
it "converts non string eval-string to string using to_str" do
@ -85,7 +85,7 @@ describe :module_class_eval, shared: true do
-> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, "no implicit conversion of MockObject into String")
(o = mock('123')).should_receive(:to_str).and_return(123)
-> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, /can't convert MockObject to String/)
-> { ModuleSpecs.send(@method, o) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
end
it "raises an ArgumentError when no arguments and no block are given" do

View File

@ -75,7 +75,7 @@ describe "Process.detach" do
pid = MockObject.new('mock-enumerable')
pid.should_receive(:to_int).and_return(:symbol)
-> { Process.detach(pid) }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Symbol)")
-> { Process.detach(pid) }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives Symbol)")
end
end
end

View File

@ -55,6 +55,6 @@ describe "Queue#initialize" do
enumerable = MockObject.new('mock-enumerable')
enumerable.should_receive(:to_a).and_return("string")
-> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_a gives String)")
-> { Queue.new(enumerable) }.should raise_consistent_error(TypeError, "can't convert MockObject into Array (MockObject#to_a gives String)")
end
end

View File

@ -46,7 +46,7 @@ describe :regexp_new_non_string_or_regexp, shared: true do
obj = Object.new
def obj.to_str() [] end
-> { Regexp.send(@method, obj) }.should raise_error(TypeError, /can't convert Object to String/)
-> { Regexp.send(@method, obj) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
end
end

View File

@ -22,6 +22,6 @@ describe "Regexp.try_convert" do
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)")
-> { Regexp.try_convert(obj) }.should raise_consistent_error(TypeError, "can't convert MockObject into Regexp (MockObject#to_regexp gives String)")
end
end

View File

@ -39,7 +39,7 @@ describe "String.try_convert" do
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, "can't convert MockObject to String (MockObject#to_str gives Object)")
-> { String.try_convert obj }.should raise_consistent_error(TypeError, "can't convert MockObject into String (MockObject#to_str gives Object)")
end
it "does not rescue exceptions raised by #to_str" do

View File

@ -106,7 +106,7 @@ describe "Struct#deconstruct_keys" do
-> {
s.deconstruct_keys([key])
}.should raise_error(TypeError, /can't convert MockObject to Integer/)
}.should raise_consistent_error(TypeError, /can't convert MockObject into Integer/)
end
it "raises TypeError if index is not a String, a Symbol and not convertible to Integer" do

View File

@ -112,7 +112,7 @@ describe "Invoking a method" do
-> {
specs.makeproc(&o)
}.should raise_error(TypeError, "can't convert LangSendSpecs::RawToProc to Proc (LangSendSpecs::RawToProc#to_proc gives Integer)")
}.should raise_consistent_error(TypeError, "can't convert LangSendSpecs::RawToProc into Proc (LangSendSpecs::RawToProc#to_proc gives Integer)")
end
it "raises TypeError if block object isn't a Proc and doesn't respond to `to_proc`" do

View File

@ -105,11 +105,11 @@ describe :queue_deq, shared: true do
q = @object.call
-> {
q.send(@method, timeout: "1")
}.should raise_error(TypeError, "no implicit conversion to float from string")
}.should raise_consistent_error(TypeError, "no implicit conversion of String into Float")
-> {
q.send(@method, timeout: false)
}.should raise_error(TypeError, "no implicit conversion to float from false")
}.should raise_consistent_error(TypeError, "no implicit conversion of false into Float")
end
it "raise ArgumentError if non_block = true is passed too" do

View File

@ -90,11 +90,11 @@ describe :sizedqueue_enq, shared: true do
q = @object.call(1)
-> {
q.send(@method, 2, timeout: "1")
}.should raise_error(TypeError, "no implicit conversion to float from string")
}.should raise_consistent_error(TypeError, "no implicit conversion of String into Float")
-> {
q.send(@method, 2, timeout: false)
}.should raise_error(TypeError, "no implicit conversion to float from false")
}.should raise_consistent_error(TypeError, "no implicit conversion of false into Float")
end
it "raise ArgumentError if non_block = true is passed too" do

View File

@ -7,11 +7,11 @@
describe :rb_num2dbl_fails, shared: true do
it "fails if string is provided" do
-> { @object.call("123") }.should raise_error(TypeError, "no implicit conversion to float from string")
-> { @object.call("123") }.should raise_consistent_error(TypeError, "no implicit conversion of String into Float")
end
it "fails if boolean is provided" do
-> { @object.call(true) }.should raise_error(TypeError, "no implicit conversion to float from true")
-> { @object.call(false) }.should raise_error(TypeError, "no implicit conversion to float from false")
-> { @object.call(true) }.should raise_consistent_error(TypeError, "no implicit conversion of true into Float")
-> { @object.call(false) }.should raise_consistent_error(TypeError, "no implicit conversion of false into Float")
end
end

View File

@ -1085,10 +1085,11 @@ nil can't be coerced into Integer (TypeError)
end
end
OF_NIL_INTO_INTEGER = RUBY_VERSION < "4.1." ? "from nil to integer" : "of nil into Integer"
def test_args_CALL_2
v = []
assert_error_message(TypeError, <<~END) do
no implicit conversion from nil to integer (TypeError)
no implicit conversion #{OF_NIL_INTO_INTEGER} (TypeError)
v[nil]
^^^
@ -1115,7 +1116,7 @@ undefined method `[]=' for #{ recv }
def test_args_ATTRASGN_2
v = []
assert_error_message(TypeError, <<~END) do
no implicit conversion from nil to integer (TypeError)
no implicit conversion #{OF_NIL_INTO_INTEGER} (TypeError)
v [nil] = 1
^^^^^^^^
@ -1177,7 +1178,7 @@ no implicit conversion of Symbol into String (TypeError)
v = []
assert_error_message(TypeError, <<~END) do
no implicit conversion from nil to integer (TypeError)
no implicit conversion #{OF_NIL_INTO_INTEGER} (TypeError)
v [nil] += 42
^^^^^^^^^^

View File

@ -1542,13 +1542,13 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def a.to_a = 1
def a.to_hash = 1
def a.to_proc = 1
assert_raise_with_message(TypeError, "can't convert TestException::Ex to Array (TestException::Ex#to_a gives Integer)") do
assert_raise_with_message(TypeError, "can't convert TestException::Ex into Array (TestException::Ex#to_a gives Integer)") do
x(*a)
end
assert_raise_with_message(TypeError, "can't convert TestException::Ex to Hash (TestException::Ex#to_hash gives Integer)") do
assert_raise_with_message(TypeError, "can't convert TestException::Ex into Hash (TestException::Ex#to_hash gives Integer)") do
x(**a)
end
assert_raise_with_message(TypeError, "can't convert TestException::Ex to Proc (TestException::Ex#to_proc gives Integer)") do
assert_raise_with_message(TypeError, "can't convert TestException::Ex into Proc (TestException::Ex#to_proc gives Integer)") do
x(&a)
end
end

View File

@ -751,7 +751,7 @@ class TestInteger < Test::Unit::TestCase
o = Object.new
def o.to_int; Object.new; end
assert_raise_with_message(TypeError, /can't convert Object to Integer/) {Integer.try_convert(o)}
assert_raise_with_message(TypeError, /can't convert Object into Integer/) {Integer.try_convert(o)}
end
def test_ceildiv

7
time.c
View File

@ -36,6 +36,7 @@
#include "internal/array.h"
#include "internal/hash.h"
#include "internal/compar.h"
#include "internal/error.h"
#include "internal/numeric.h"
#include "internal/rational.h"
#include "internal/string.h"
@ -564,8 +565,7 @@ NORETURN(static void num_exact_fail(VALUE v));
static void
num_exact_fail(VALUE v)
{
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
rb_obj_class(v));
rb_cant_convert(v, "an exact number");
}
static VALUE
@ -2924,8 +2924,7 @@ time_timespec(VALUE num, int interval)
t.tv_nsec = NUM2LONG(f);
}
else {
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
rb_obj_class(num), tstr);
rb_cant_convert(num, tstr);
}
}
return t;

View File

@ -1052,15 +1052,10 @@ vm_to_proc(VALUE proc)
if (NIL_P(b) || !rb_obj_is_proc(b)) {
if (me) {
VALUE cname = rb_obj_class(proc);
rb_raise(rb_eTypeError,
"can't convert %"PRIsVALUE" to Proc (%"PRIsVALUE"#to_proc gives %"PRIsVALUE")",
cname, cname, rb_obj_class(b));
rb_cant_convert_invalid_return(proc, "Proc", "to_proc", b);
}
else {
rb_raise(rb_eTypeError,
"no implicit conversion of %s into Proc",
rb_obj_classname(proc));
rb_no_implicit_conversion(proc, "Proc");
}
}
return b;