diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2026-02-05 20:38:00 +0100 |
|---|---|---|
| committer | Benoit Daloze <eregontp@gmail.com> | 2026-02-07 10:06:36 +0100 |
| commit | d066b9e01ccb2260fac8b2580c10e73335c7c7db (patch) | |
| tree | ece08ae6b0309149e09a640a3b41537a6485ee13 | |
| parent | 96d00640978d78ede1f5b2b63e422cfd1e849891 (diff) | |
Refactor type error to be more consistent
[Bug #21864]
Co-Authored-By: Benoit Daloze <eregontp@gmail.com>
36 files changed, 158 insertions, 112 deletions
@@ -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)) { @@ -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 @@ -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) { diff --git a/internal/error.h b/internal/error.h index ae9a13fcec..fead2aee95 100644 --- a/internal/error.h +++ b/internal/error.h @@ -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) */ @@ -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 */ @@ -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; } diff --git a/rational.c b/rational.c index 51078f81ad..fba93a5c3f 100644 --- a/rational.c +++ b/rational.c @@ -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)) { diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb index 54378bb34c..2aa3038ed1 100644 --- a/spec/mspec/lib/mspec/matchers/raise_error.rb +++ b/spec/mspec/lib/mspec/matchers/raise_error.rb @@ -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 diff --git a/spec/ruby/core/array/try_convert_spec.rb b/spec/ruby/core/array/try_convert_spec.rb index bea8815006..2d5e14375f 100644 --- a/spec/ruby/core/array/try_convert_spec.rb +++ b/spec/ruby/core/array/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb index f8d9d75059..475910e56e 100644 --- a/spec/ruby/core/basicobject/instance_eval_spec.rb +++ b/spec/ruby/core/basicobject/instance_eval_spec.rb @@ -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 diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb index df378f8b98..979d136893 100644 --- a/spec/ruby/core/data/deconstruct_keys_spec.rb +++ b/spec/ruby/core/data/deconstruct_keys_spec.rb @@ -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 diff --git a/spec/ruby/core/dir/for_fd_spec.rb b/spec/ruby/core/dir/for_fd_spec.rb index 1559e1baa4..be80a2c1fe 100644 --- a/spec/ruby/core/dir/for_fd_spec.rb +++ b/spec/ruby/core/dir/for_fd_spec.rb @@ -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 diff --git a/spec/ruby/core/hash/try_convert_spec.rb b/spec/ruby/core/hash/try_convert_spec.rb index d359ae49d8..3932c8cdfd 100644 --- a/spec/ruby/core/hash/try_convert_spec.rb +++ b/spec/ruby/core/hash/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/integer/try_convert_spec.rb b/spec/ruby/core/integer/try_convert_spec.rb index 8a0ca671a9..ba14a5aa7e 100644 --- a/spec/ruby/core/integer/try_convert_spec.rb +++ b/spec/ruby/core/integer/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb index e82cdd9f17..3439a97729 100644 --- a/spec/ruby/core/io/lineno_spec.rb +++ b/spec/ruby/core/io/lineno_spec.rb @@ -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 diff --git a/spec/ruby/core/io/try_convert_spec.rb b/spec/ruby/core/io/try_convert_spec.rb index a9e99de7aa..820b13ab50 100644 --- a/spec/ruby/core/io/try_convert_spec.rb +++ b/spec/ruby/core/io/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/kernel/Rational_spec.rb b/spec/ruby/core/kernel/Rational_spec.rb index cc11a35451..7e63e0dd57 100644 --- a/spec/ruby/core/kernel/Rational_spec.rb +++ b/spec/ruby/core/kernel/Rational_spec.rb @@ -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 diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb index 2b2c6c9b63..5ffe118035 100644 --- a/spec/ruby/core/kernel/shared/sprintf.rb +++ b/spec/ruby/core/kernel/shared/sprintf.rb @@ -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 diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb index c5dfc53764..a4fe4fad18 100644 --- a/spec/ruby/core/module/define_method_spec.rb +++ b/spec/ruby/core/module/define_method_spec.rb @@ -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 diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb index 182cdf5c54..c6bc201520 100644 --- a/spec/ruby/core/module/instance_method_spec.rb +++ b/spec/ruby/core/module/instance_method_spec.rb @@ -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 diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb index 526d0a2036..b6e653a2ac 100644 --- a/spec/ruby/core/module/shared/class_eval.rb +++ b/spec/ruby/core/module/shared/class_eval.rb @@ -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 diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb index f13bda1f5d..f5f3b263f5 100644 --- a/spec/ruby/core/process/detach_spec.rb +++ b/spec/ruby/core/process/detach_spec.rb @@ -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 diff --git a/spec/ruby/core/queue/initialize_spec.rb b/spec/ruby/core/queue/initialize_spec.rb index 592fbe2487..6b9e6b7fa9 100644 --- a/spec/ruby/core/queue/initialize_spec.rb +++ b/spec/ruby/core/queue/initialize_spec.rb @@ -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 diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb index 12c3d7c9c2..7b0f12d623 100644 --- a/spec/ruby/core/regexp/shared/new.rb +++ b/spec/ruby/core/regexp/shared/new.rb @@ -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 diff --git a/spec/ruby/core/regexp/try_convert_spec.rb b/spec/ruby/core/regexp/try_convert_spec.rb index e775dbe971..44a6fead83 100644 --- a/spec/ruby/core/regexp/try_convert_spec.rb +++ b/spec/ruby/core/regexp/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/string/try_convert_spec.rb b/spec/ruby/core/string/try_convert_spec.rb index 72ce5dd8b2..7019f1fc2a 100644 --- a/spec/ruby/core/string/try_convert_spec.rb +++ b/spec/ruby/core/string/try_convert_spec.rb @@ -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 diff --git a/spec/ruby/core/struct/deconstruct_keys_spec.rb b/spec/ruby/core/struct/deconstruct_keys_spec.rb index e16b50f930..0360a4b194 100644 --- a/spec/ruby/core/struct/deconstruct_keys_spec.rb +++ b/spec/ruby/core/struct/deconstruct_keys_spec.rb @@ -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 diff --git a/spec/ruby/language/send_spec.rb b/spec/ruby/language/send_spec.rb index 5d6340ffc5..c60381cf55 100644 --- a/spec/ruby/language/send_spec.rb +++ b/spec/ruby/language/send_spec.rb @@ -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 diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb index a154da6274..fbd00143f8 100644 --- a/spec/ruby/shared/queue/deque.rb +++ b/spec/ruby/shared/queue/deque.rb @@ -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 diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb index 6804af3fb3..bab4c407e1 100644 --- a/spec/ruby/shared/sizedqueue/enque.rb +++ b/spec/ruby/shared/sizedqueue/enque.rb @@ -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 diff --git a/spec/ruby/shared/types/rb_num2dbl_fails.rb b/spec/ruby/shared/types/rb_num2dbl_fails.rb index ec7cc11986..4d4856fa6c 100644 --- a/spec/ruby/shared/types/rb_num2dbl_fails.rb +++ b/spec/ruby/shared/types/rb_num2dbl_fails.rb @@ -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 diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb index ec39b1c4db..5f664de502 100644 --- a/test/error_highlight/test_error_highlight.rb +++ b/test/error_highlight/test_error_highlight.rb @@ -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 ^^^^^^^^^^ diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 31e5aa9f6b..4365150a13 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -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 diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index f9bf4fa20c..c3d9d311c8 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -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 @@ -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; @@ -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; |
