Skip to content

Commit 50995e1

Browse files
committed
Add specs for missing-ruby2_keywords-in-between delegation
* Which works due to https://bugs.ruby-lang.org/issues/18625
1 parent 3aa5de6 commit 50995e1

File tree

2 files changed

+110
-30
lines changed

2 files changed

+110
-30
lines changed

spec/ruby/core/module/ruby2_keywords_spec.rb

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33

44
ruby_version_is "2.7" do
55
describe "Module#ruby2_keywords" do
6-
it "marks the final hash argument as keyword hash" do
7-
obj = Object.new
8-
9-
obj.singleton_class.class_exec do
10-
def foo(*a) a.last end
11-
ruby2_keywords :foo
6+
class << self
7+
ruby2_keywords def mark(*args)
8+
args
129
end
10+
end
1311

14-
last = obj.foo(1, 2, a: "a")
12+
it "marks the final hash argument as keyword hash" do
13+
last = mark(1, 2, a: "a").last
1514
Hash.ruby2_keywords_hash?(last).should == true
1615
end
1716

@@ -21,74 +20,121 @@ def foo(*a) a.last end
2120
def regular(*args)
2221
args.last
2322
end
24-
25-
ruby2_keywords def foo(*args)
26-
args.last
27-
end
2823
end
2924

3025
h = {a: 1}
3126
ruby_version_is "3.0" do
3227
obj.regular(**h).should.equal?(h)
3328
end
3429

35-
last = obj.foo(**h)
30+
last = mark(**h).last
3631
Hash.ruby2_keywords_hash?(last).should == true
3732
Hash.ruby2_keywords_hash?(h).should == false
3833

39-
last2 = obj.foo(**last) # last is already marked
34+
last2 = mark(**last).last # last is already marked
4035
Hash.ruby2_keywords_hash?(last2).should == true
4136
Hash.ruby2_keywords_hash?(last).should == true
4237
last2.should_not.equal?(last)
4338
Hash.ruby2_keywords_hash?(h).should == false
4439
end
4540

46-
it "makes a copy and unmark at the call site when calling with marked *args" do
41+
it "makes a copy and unmark the Hash when calling a method taking (arg)" do
4742
obj = Object.new
4843
obj.singleton_class.class_exec do
49-
ruby2_keywords def foo(*args)
50-
args
51-
end
52-
5344
def single(arg)
5445
arg
5546
end
47+
end
5648

57-
def splat(*args)
58-
args.last
59-
end
49+
h = { a: 1 }
50+
args = mark(**h)
51+
marked = args.last
52+
Hash.ruby2_keywords_hash?(marked).should == true
53+
54+
after_usage = obj.single(*args)
55+
after_usage.should == h
56+
after_usage.should_not.equal?(h)
57+
after_usage.should_not.equal?(marked)
58+
Hash.ruby2_keywords_hash?(after_usage).should == false
59+
Hash.ruby2_keywords_hash?(marked).should == true
60+
end
6061

62+
it "makes a copy and unmark the Hash when calling a method taking (**kw)" do
63+
obj = Object.new
64+
obj.singleton_class.class_exec do
6165
def kwargs(**kw)
6266
kw
6367
end
6468
end
6569

6670
h = { a: 1 }
67-
args = obj.foo(**h)
71+
args = mark(**h)
6872
marked = args.last
6973
Hash.ruby2_keywords_hash?(marked).should == true
7074

71-
after_usage = obj.single(*args)
75+
after_usage = obj.kwargs(*args)
7276
after_usage.should == h
7377
after_usage.should_not.equal?(h)
7478
after_usage.should_not.equal?(marked)
7579
Hash.ruby2_keywords_hash?(after_usage).should == false
7680
Hash.ruby2_keywords_hash?(marked).should == true
81+
end
82+
83+
# https://bugs.ruby-lang.org/issues/18625
84+
it "does NOT copy the Hash when calling a method taking (*args)" do
85+
obj = Object.new
86+
obj.singleton_class.class_exec do
87+
def splat(*args)
88+
args.last
89+
end
90+
91+
def splat1(arg, *args)
92+
args.last
93+
end
94+
95+
def proc_call(*args)
96+
-> *args { args.last }.call(*args)
97+
end
98+
end
99+
100+
h = { a: 1 }
101+
args = mark(**h)
102+
marked = args.last
103+
Hash.ruby2_keywords_hash?(marked).should == true
77104

78105
after_usage = obj.splat(*args)
79106
after_usage.should == h
80107
after_usage.should_not.equal?(h)
81-
after_usage.should_not.equal?(marked)
82-
ruby_bug "#18625", ""..."3.3" do # might be fixed in 3.2
83-
Hash.ruby2_keywords_hash?(after_usage).should == false
84-
end
108+
after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625
109+
Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625
85110
Hash.ruby2_keywords_hash?(marked).should == true
86111

87-
after_usage = obj.kwargs(*args)
112+
args = mark(1, **h)
113+
marked = args.last
114+
after_usage = obj.splat1(*args)
88115
after_usage.should == h
89116
after_usage.should_not.equal?(h)
90-
after_usage.should_not.equal?(marked)
91-
Hash.ruby2_keywords_hash?(after_usage).should == false
117+
after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625
118+
Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625
119+
Hash.ruby2_keywords_hash?(marked).should == true
120+
121+
args = mark(**h)
122+
marked = args.last
123+
after_usage = obj.proc_call(*args)
124+
after_usage.should == h
125+
after_usage.should_not.equal?(h)
126+
after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625
127+
Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625
128+
Hash.ruby2_keywords_hash?(marked).should == true
129+
130+
args = mark(**h)
131+
marked = args.last
132+
after_usage = obj.send(:splat, *args)
133+
after_usage.should == h
134+
after_usage.should_not.equal?(h)
135+
send_copies = RUBY_ENGINE == "ruby" # inconsistent with Proc#call above for CRuby
136+
after_usage.equal?(marked).should == !send_copies
137+
Hash.ruby2_keywords_hash?(after_usage).should == !send_copies
92138
Hash.ruby2_keywords_hash?(marked).should == true
93139
end
94140

spec/ruby/language/keyword_arguments_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,40 @@ def m(*args)
302302
m(a: 1).should == [[{a: 1}], {}]
303303
m({a: 1}).should == [[{a: 1}], {}]
304304
end
305+
306+
# https://bugs.ruby-lang.org/issues/18625
307+
it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
308+
class << self
309+
def n(*args) # Note the missing ruby2_keywords here
310+
target(*args)
311+
end
312+
313+
ruby2_keywords def m(*args)
314+
n(*args)
315+
end
316+
end
317+
318+
empty = {}
319+
m(**empty).should == [[], {}]
320+
Hash.ruby2_keywords_hash?(empty).should == false
321+
m(empty).should == [[{}], {}]
322+
Hash.ruby2_keywords_hash?(empty).should == false
323+
324+
m(a: 1).should == [[], {a: 1}]
325+
m({a: 1}).should == [[{a: 1}], {}]
326+
327+
kw = {a: 1}
328+
329+
m(**kw).should == [[], {a: 1}]
330+
m(**kw)[1].should == kw
331+
m(**kw)[1].should_not.equal?(kw)
332+
Hash.ruby2_keywords_hash?(kw).should == false
333+
Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
334+
335+
m(kw).should == [[{a: 1}], {}]
336+
m(kw)[0][0].should.equal?(kw)
337+
Hash.ruby2_keywords_hash?(kw).should == false
338+
end
305339
end
306340
end
307341
end

0 commit comments

Comments
 (0)