-
-
Notifications
You must be signed in to change notification settings - Fork 939
Description
Environment Information
- JRuby 10.0.4.0 (Ruby 3.4.5 compat)
- macOS arm64-darwin
Expected Behavior
Issue #9242 was reported as fixed, but the fix seems to be incomplete. Using def initialize(...) + super(...) in a module prepended/included on a Struct subclass should not produce any warnings. CRuby 3.4.4 produces no warnings:
Given the script: jruby_9242_struct_initialize.rb:
#!/usr/bin/env ruby
# frozen_string_literal: true
# JRuby #9242: def initialize(...) + super(...) in a module prepended on a
# Struct subclass incorrectly warns about keyword arguments (3+ members)
# or crashes with ClassCastException (1 member).
#
# Run: ruby jruby_9242_struct_initialize.rb 2>&1
require "stringio"
puts "Ruby: #{RUBY_DESCRIPTION}"
puts
old_stderr = $stderr
$stderr = StringIO.new
mod = Module.new do
def initialize(...)
super(...)
end
end
# Test 1: 3-member Struct (triggers spurious keyword warning)
puts "1. Struct with 3 members + prepended initialize(...):"
S3 = Struct.new(:a, :b, :c) { prepend mod }
obj3 = S3.new(1, 2, 3)
puts " a=#{obj3.a} b=#{obj3.b} c=#{obj3.c}"
stderr_output = $stderr.string
$stderr = StringIO.new
if stderr_output.include?("keyword")
puts " FAIL: spurious warning: #{stderr_output.strip}"
else
puts " PASS: no spurious warning"
end
puts
# Test 2: 1-member Struct (used to crash with ClassCastException)
puts "2. Struct with 1 member + prepended initialize(...):"
begin
S1 = Struct.new(:x) { prepend mod }
obj1 = S1.new(1)
stderr_output = $stderr.string
$stderr = StringIO.new
if stderr_output.include?("keyword")
puts " x=#{obj1.x}"
puts " FAIL: spurious warning: #{stderr_output.strip}"
else
puts " x=#{obj1.x}"
puts " PASS: no crash, no warning"
end
rescue => e
puts " FAIL: #{e.class}: #{e.message}"
end
puts
# Test 3: Same but with include instead of prepend
puts "3. Struct with 3 members + included initialize(...):"
$stderr = StringIO.new
S3i = Struct.new(:a, :b, :c) { include mod }
obj3i = S3i.new(1, 2, 3)
puts " a=#{obj3i.a} b=#{obj3i.b} c=#{obj3i.c}"
stderr_output = $stderr.string
if stderr_output.include?("keyword")
puts " FAIL: spurious warning: #{stderr_output.strip}"
else
puts " PASS: no spurious warning"
end
$stderr = old_stderrOn Ruby: ruby 3.4.4
-
Struct with 3 members + prepended initialize(...):
a=1 b=2 c=3
PASS: no spurious warning -
Struct with 1 member + prepended initialize(...):
x=1
PASS: no crash, no warning -
Struct with 3 members + included initialize(...):
a=1 b=2 c=3
PASS: no spurious warning
On JRuby 10.0.4.0
The 1-member Struct crash (ClassCastException) is fixed, but Struct subclasses with 3+ members still produce a spurious warning:
-
Struct with 3 members + prepended initialize(...):
a=1 b=2 c=3
FAIL: spurious warning: Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2. Please use a Hash literal like .new({k: v}) instead of .new(k: v). -
Struct with 1 member + prepended initialize(...):
x=1
PASS: no crash, no warning -
Struct with 3 members + included initialize(...):
a=1 b=2 c=3
FAIL: spurious warning: ...
The warning is incorrect — S3.new(1, 2, 3) passes positional arguments, not keyword arguments. The warning is triggered because the module's initialize(...) + super(...) forwarding pattern is misinterpreted by JRuby as keyword argument passing.
Our workaround is to use ruby2_keywords to avoid separate **kwargs forwarding in the affected modules.