From de1feea72ebee7a96931d44c7d3dd21e23ebfcf2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Sep 2025 21:22:17 +0900 Subject: [PATCH 1/4] Increase the rehearsal count --- test/ruby/test_struct.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index db591c306e556e..01e5cc68f6eadc 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -544,7 +544,7 @@ def test_named_structs_are_not_rooted Struct.send(:remove_const, :A) end - 1_000.times(&code) + 10_000.times(&code) PREP 50_000.times(&code) CODE From 1a8536cce9bae7144e9d4c5118dd3df59b2efbe6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Sep 2025 22:42:27 +0900 Subject: [PATCH 2/4] Transform the manpages using the given substitution --- tool/rbinstall.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb index 24c6234d843a6b..df15c65b54b11a 100755 --- a/tool/rbinstall.rb +++ b/tool/rbinstall.rb @@ -1021,7 +1021,6 @@ def (bins = []).add(name) prepare "manpages", mandir, ([] | mdocs.collect {|mdoc| mdoc[/\d+$/]}).sort.collect {|sec| "man#{sec}"} mantype, suffix, compress = Compressors.for($mantype) - mandir = File.join(mandir, "man") has_goruby = File.exist?(goruby_install_name+exeext) require File.join(srcdir, "tool/mdoc2man.rb") if /\Adoc\b/ !~ mantype mdocs.each do |mdoc| @@ -1031,8 +1030,8 @@ def (bins = []).add(name) next unless has_goruby end - destdir = mandir + (section = mdoc[/\d+$/]) - destname = ruby_install_name.sub(/ruby/, base.chomp(".#{section}")) + destdir = File.join(mandir, "man" + (section = mdoc[/\d+$/])) + destname = $script_installer.transform(base.chomp(".#{section}")) destfile = File.join(destdir, "#{destname}.#{section}") if /\Adoc\b/ =~ mantype or !mdoc_file?(mdoc) From 5c875519f3c78f9d3ea470f1c8593a6026af93eb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Sep 2025 10:05:00 +0900 Subject: [PATCH 3/4] Refine use of `Pathname::SEPARATOR_PAT` - Remove unnecessary string interpolations. - `/#{SEPARATOR_PAT}/o` is always same as `SEPARATOR_PAT` Regexp. --- pathname_builtin.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 486e49d0920212..52b7003a64b8bf 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -312,12 +312,12 @@ def sub_ext(repl) if File::ALT_SEPARATOR # Separator list string. - SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}" + SEPARATOR_LIST = Regexp.quote "#{File::ALT_SEPARATOR}#{File::SEPARATOR}" # Regexp that matches a separator. SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/ else - SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}" - SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ + SEPARATOR_LIST = Regexp.quote File::SEPARATOR + SEPARATOR_PAT = /#{SEPARATOR_LIST}/ end if File.dirname('A:') == 'A:.' # DOSish drive letter @@ -383,7 +383,7 @@ def split_names(path) # :nodoc: def prepend_prefix(prefix, relpath) # :nodoc: if relpath.empty? File.dirname(prefix) - elsif /#{SEPARATOR_PAT}/o.match?(prefix) + elsif SEPARATOR_PAT.match?(prefix) prefix = File.dirname(prefix) prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a' prefix + relpath @@ -434,7 +434,7 @@ def cleanpath_aggressive # :nodoc: end end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR - if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) + if SEPARATOR_PAT.match?(File.basename(pre)) names.shift while names[0] == '..' end self.class.new(prepend_prefix(pre, File.join(*names))) @@ -483,7 +483,7 @@ def cleanpath_conservative # :nodoc: names.unshift base if base != '.' end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR - if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) + if SEPARATOR_PAT.match?(File.basename(pre)) names.shift while names[0] == '..' end if names.empty? @@ -528,7 +528,7 @@ def mountpoint? # pathnames which points to roots such as /usr/... # def root? - chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path) + chop_basename(@path) == nil && SEPARATOR_PAT.match?(@path) end # Predicate method for testing whether a path is absolute. @@ -698,7 +698,7 @@ def plus(path1, path2) # -> path # :nodoc: basename_list2.shift end r1 = chop_basename(prefix1) - if !r1 && (r1 = /#{SEPARATOR_PAT}/o.match?(File.basename(prefix1))) + if !r1 && (r1 = SEPARATOR_PAT.match?(File.basename(prefix1))) while !basename_list2.empty? && basename_list2.first == '..' index_list2.shift basename_list2.shift From 953e1ef99283d8563ff655ee6b8fcd681af79c1c Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 5 Sep 2025 22:29:43 -0700 Subject: [PATCH 4/4] 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] --- test/ruby/test_exception.rb | 27 +++++++++++++++++++++++++++ vm_args.c | 14 +++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 84581180b60958..17ff5a2e82996b 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -1525,4 +1525,31 @@ def detailed_message(**) 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 diff --git a/vm_args.c b/vm_args.c index 44be6f54c5cf79..777bcd21b33edc 100644 --- a/vm_args.c +++ b/vm_args.c @@ -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; }