Skip to content

Commit 6752255

Browse files
committed
Fix assignment operators when argument is a splat operator
1 parent 920f770 commit 6752255

11 files changed

+1330
-16
lines changed

spec/ruby/language/assignments_spec.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ def a=(n) @a = n; 42 end
3434
end
3535

3636
describe 'using a #[]' do
37+
before do
38+
klass = Class.new do
39+
def [](k)
40+
@hash ||= {}
41+
@hash[k]
42+
end
43+
44+
def []=(k, v)
45+
@hash ||= {}
46+
@hash[k] = v
47+
7
48+
end
49+
end
50+
@b = klass.new
51+
end
52+
3753
it 'evaluates receiver only once when assigns' do
3854
ScratchPad.record []
3955
a = {k: 1}
@@ -56,6 +72,62 @@ def []=(k, v) @a[k] = v; 42 end
5672
a = klass_with_private_methods.new(k: 0)
5773
a.public_method(:k, 2).should == 2
5874
end
75+
76+
context 'splatted argument' do
77+
it 'correctly handles it' do
78+
@b[:m] = 10
79+
(@b[*[:m]] += 10).should == 20
80+
@b[:m].should == 20
81+
82+
@b[:n] = 10
83+
(@b[*(1; [:n])] += 10).should == 20
84+
@b[:n].should == 20
85+
86+
@b[:k] = 10
87+
(@b[*begin 1; [:k] end] += 10).should == 20
88+
@b[:k].should == 20
89+
end
90+
91+
it 'calls #to_a only once' do
92+
k = Object.new
93+
def k.to_a
94+
ScratchPad << :to_a
95+
[:k]
96+
end
97+
98+
ScratchPad.record []
99+
@b[:k] = 10
100+
(@b[*k] += 10).should == 20
101+
@b[:k].should == 20
102+
ScratchPad.recorded.should == [:to_a]
103+
end
104+
105+
it 'correctly handles a nested splatted argument' do
106+
@b[:k] = 10
107+
(@b[*[*[:k]]] += 10).should == 20
108+
@b[:k].should == 20
109+
end
110+
111+
it 'correctly handles multiple nested splatted arguments' do
112+
klass_with_multiple_parameters = Class.new do
113+
def [](k1, k2, k3)
114+
@hash ||= {}
115+
@hash[:"#{k1}#{k2}#{k3}"]
116+
end
117+
118+
def []=(k1, k2, k3, v)
119+
@hash ||= {}
120+
@hash[:"#{k1}#{k2}#{k3}"] = v
121+
7
122+
end
123+
end
124+
a = klass_with_multiple_parameters.new
125+
126+
a[:a, :b, :c] = 10
127+
(a[*[:a], *[:b], *[:c]] += 10).should == 20
128+
a[:a, :b, :c].should == 20
129+
end
130+
end
59131
end
60132

61133
describe 'using compounded constants' do

spec/ruby/language/optional_assignments_spec.rb

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,6 @@ def []=(k, v)
168168
(@b[:k] ||= 12).should == 12
169169
end
170170

171-
it 'correctly handles a splatted argument for the index' do
172-
(@b[*[:k]] ||= 12).should == 12
173-
end
174-
175171
it "evaluates the index precisely once" do
176172
ary = [:x, :y]
177173
@a[:x] = 15
@@ -228,6 +224,56 @@ def []=(k, v) @a[k] = v; 42 end
228224
a = klass_with_private_methods.new(k: false)
229225
a.public_method(:k, 10).should == 10
230226
end
227+
228+
context 'splatted argument' do
229+
it 'correctly handles it' do
230+
(@b[*[:m]] ||= 10).should == 10
231+
@b[:m].should == 10
232+
233+
(@b[*(1; [:n])] ||= 10).should == 10
234+
@b[:n].should == 10
235+
236+
(@b[*begin 1; [:k] end] ||= 10).should == 10
237+
@b[:k].should == 10
238+
end
239+
240+
it 'calls #to_a only once' do
241+
k = Object.new
242+
def k.to_a
243+
ScratchPad << :to_a
244+
[:k]
245+
end
246+
247+
ScratchPad.record []
248+
(@b[*k] ||= 20).should == 20
249+
@b[:k].should == 20
250+
ScratchPad.recorded.should == [:to_a]
251+
end
252+
253+
it 'correctly handles a nested splatted argument' do
254+
(@b[*[*[:k]]] ||= 20).should == 20
255+
@b[:k].should == 20
256+
end
257+
258+
it 'correctly handles multiple nested splatted arguments' do
259+
klass_with_multiple_parameters = Class.new do
260+
def [](k1, k2, k3)
261+
@hash ||= {}
262+
@hash[:"#{k1}#{k2}#{k3}"]
263+
end
264+
265+
def []=(k1, k2, k3, v)
266+
@hash ||= {}
267+
@hash[:"#{k1}#{k2}#{k3}"] = v
268+
7
269+
end
270+
end
271+
a = klass_with_multiple_parameters.new
272+
273+
(a[*[:a], *[:b], *[:c]] ||= 20).should == 20
274+
a[:a, :b, :c].should == 20
275+
end
276+
end
231277
end
232278
end
233279

@@ -407,12 +453,6 @@ def []=(k, v)
407453
(@b[:k] &&= 12).should == 12
408454
end
409455

410-
it 'correctly handles a splatted argument for the index' do
411-
@b[:k] = 10
412-
(@b[*[:k]] &&= 12).should == 12
413-
@b[:k].should == 12
414-
end
415-
416456
it "evaluates the index precisely once" do
417457
ary = [:x, :y]
418458
@a[:x] = 15
@@ -475,6 +515,62 @@ def []=(k, v) @a[k] = v; 42 end
475515
a = klass_with_private_methods.new(k: true)
476516
a.public_method(:k, 10).should == 10
477517
end
518+
519+
context 'splatted argument' do
520+
it 'correctly handles it' do
521+
@b[:m] = 0
522+
(@b[*[:m]] &&= 10).should == 10
523+
@b[:m].should == 10
524+
525+
@b[:n] = 0
526+
(@b[*(1; [:n])] &&= 10).should == 10
527+
@b[:n].should == 10
528+
529+
@b[:k] = 0
530+
(@b[*begin 1; [:k] end] &&= 10).should == 10
531+
@b[:k].should == 10
532+
end
533+
534+
it 'calls #to_a only once' do
535+
k = Object.new
536+
def k.to_a
537+
ScratchPad << :to_a
538+
[:k]
539+
end
540+
541+
ScratchPad.record []
542+
@b[:k] = 10
543+
(@b[*k] &&= 20).should == 20
544+
@b[:k].should == 20
545+
ScratchPad.recorded.should == [:to_a]
546+
end
547+
548+
it 'correctly handles a nested splatted argument' do
549+
@b[:k] = 10
550+
(@b[*[*[:k]]] &&= 20).should == 20
551+
@b[:k].should == 20
552+
end
553+
554+
it 'correctly handles multiple nested splatted arguments' do
555+
klass_with_multiple_parameters = Class.new do
556+
def [](k1, k2, k3)
557+
@hash ||= {}
558+
@hash[:"#{k1}#{k2}#{k3}"]
559+
end
560+
561+
def []=(k1, k2, k3, v)
562+
@hash ||= {}
563+
@hash[:"#{k1}#{k2}#{k3}"] = v
564+
7
565+
end
566+
end
567+
a = klass_with_multiple_parameters.new
568+
569+
a[:a, :b, :c] = 10
570+
(a[*[:a], *[:b], *[:c]] &&= 20).should == 20
571+
a[:a, :b, :c].should == 20
572+
end
573+
end
478574
end
479575
end
480576

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
fails:Assignments using += using compounded constants causes side-effects of the module part to be applied only once (when assigns)
22
fails:Assignments using += using an accessor ignores method visibility when receiver is self
33
fails:Assignments using += using a #[] ignores method visibility when receiver is self
4+
fails:Assignments using += using a #[] splatted argument calls #to_a only once
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
fails:Optional constant assignment with &&= causes side-effects of the module part to be applied only once (when assigns)
22
fails:Optional variable assignments using ||= using a #[] ignores method visibility when receiver is self
33
fails:Optional variable assignments using &&= using a #[] ignores method visibility when receiver is self
4+
fails:Optional variable assignments using ||= using a #[] splatted argument calls #to_a only once
5+
fails:Optional variable assignments using &&= using a #[] splatted argument calls #to_a only once

0 commit comments

Comments
 (0)