Make invalid & operator type error message consistent with */**

If #to_proc is defined, this uses the following error message format,
matching the error message format used for * when to_a returns non-Array
and for ** when to_hash returns non-Hash:

```
can't convert ClassName to Proc (ClassName#to_proc gives OtherClassName)
```

If #to_proc is not defined, this uses the following error message format,
matching the error message format used when ** is called on a non-Hash
not implementing to_hash.

```
no implicit conversion of ClassName into Proc
```

There isn't a similar error for * when called on a non-Array not
implementing to_a, as Ruby does not raise for that case.

Fixes [Bug #21563]
This commit is contained in:
Jeremy Evans 2025-09-05 22:29:43 -07:00
parent 5c875519f3
commit 953e1ef992
2 changed files with 38 additions and 3 deletions

View File

@ -1525,4 +1525,31 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
assert_in_out_err(%W[-r#{lib} #{main}], "", [], [:*, "\n""path=#{main}\n", :*])
end
end
class Ex; end
def test_exception_message_for_unexpected_implicit_conversion_type
a = Ex.new
def self.x(a) = nil
assert_raise_with_message(TypeError, "no implicit conversion of TestException::Ex into Hash") do
x(**a)
end
assert_raise_with_message(TypeError, "no implicit conversion of TestException::Ex into Proc") do
x(&a)
end
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
x(*a)
end
assert_raise_with_message(TypeError, "can't convert TestException::Ex to 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
x(&a)
end
end
end

View File

@ -1045,9 +1045,17 @@ vm_to_proc(VALUE proc)
}
if (NIL_P(b) || !rb_obj_is_proc(b)) {
rb_raise(rb_eTypeError,
"wrong argument type %s (expected Proc)",
rb_obj_classname(proc));
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));
}
else {
rb_raise(rb_eTypeError,
"no implicit conversion of %s into Proc",
rb_obj_classname(proc));
}
}
return b;
}