Skip to content

Commit 7380c2c

Browse files
committed
Fix Struct#initialize when mixed positional and keyword arguments
1 parent a65bde3 commit 7380c2c

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Compatibility:
2323
* Fix `Kernel#raise` and don't override `cause` at exception re-raising (#3831, @andrykonchin).
2424
* Return a pointer with `#type_size` of 1 for `Pointer#read_pointer` (@eregon).
2525
* Fix `rb_str_locktmp()` and `rb_str_unlocktmp()` to raise `FrozenError` when string argument is frozen (#3752, @andrykonchin).
26+
* Fix `Struct#initialize` when mixed positional and keyword arguments (#3855, @andrykonchin).
2627

2728
Performance:
2829

spec/ruby/core/struct/new_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,35 @@ def platform
164164
obj.args.should == 42
165165
obj2.args.should == 42
166166
end
167+
168+
context "given positional and keyword arguments" do
169+
it "treats keyword arguments as a positional parameter" do
170+
type = Struct.new(:a, :b)
171+
s = type.new("a", b: "b")
172+
s.a.should == "a"
173+
s.b.should == {b: "b"}
174+
175+
type = Struct.new(:a, :b, :c)
176+
s = type.new("a", b: "b", c: "c")
177+
s.a.should == "a"
178+
s.b.should == {b: "b", c: "c"}
179+
s.c.should == nil
180+
end
181+
182+
it "ignores empty keyword arguments" do
183+
type = Struct.new(:a, :b)
184+
h = {}
185+
s = type.new("a", **h)
186+
187+
s.a.should == "a"
188+
s.b.should == nil
189+
end
190+
191+
it "raises ArgumentError when all struct attribute values are specified" do
192+
type = Struct.new(:a, :b)
193+
-> { type.new("a", "b", c: "c") }.should raise_error(ArgumentError, "struct size differs")
194+
end
195+
end
167196
end
168197

169198
context "keyword_init: true option" do

src/main/ruby/truffleruby/core/struct.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ def initialize(*args, **kwargs)
180180
attrs.each_with_index do |attr, i|
181181
Primitive.object_hidden_var_set self, attr, args[i]
182182
end
183+
184+
# treat keyword arguments as a positional one
185+
if !kwargs.empty?
186+
if args.size < attrs.size
187+
Primitive.object_hidden_var_set self, attrs[args.size], kwargs
188+
else
189+
raise ArgumentError, 'struct size differs'
190+
end
191+
end
183192
end
184193
end
185194

0 commit comments

Comments
 (0)