summaryrefslogtreecommitdiff
path: root/test/ruby/test_call.rb
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-12-12 07:34:53 -0800
committerGitHub <noreply@github.com>2023-12-12 07:34:53 -0800
commit2f1d6da8c45590bf3461ed4bf051a4e1009eaf85 (patch)
tree8e868e9b6a71fe74c323dfa2e81422d08beadf6f /test/ruby/test_call.rb
parentf671c5d1791d14a44902cfe672ae6b457d1592e9 (diff)
Fix op asgn calls with keywords
Examples of such calls: ```ruby obj[kw: 1] += fo obj[**kw] &&= bar ``` Before this patch, literal keywords would segfault in the compiler, and keyword splat usage would result in TypeError. This handles all cases I can think of: * literal keywords * keyword splats * combined with positional arguments * combined with regular splats * both with and without blocks * both popped and non-popped cases This also makes sure that to_hash is only called once on the keyword splat argument, instead of twice, and make sure it is called before calling to_proc on a passed block. Fixes [Bug #20051] Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Diffstat (limited to 'test/ruby/test_call.rb')
-rw-r--r--test/ruby/test_call.rb160
1 files changed, 160 insertions, 0 deletions
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index 92c5e604b5..f17d5848b9 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -121,6 +121,166 @@ class TestCall < Test::Unit::TestCase
assert_equal([10], a(10))
end
+ def test_call_op_asgn_keywords
+ h = Class.new do
+ attr_reader :get, :set
+ def v; yield; [*@get, *@set] end
+ def [](*a, **b, &c) @get = [a, b, c]; @set = []; 3 end
+ def []=(*a, **b, &c) @set = [a, b, c] end
+ end.new
+
+ a = []
+ kw = {}
+ b = lambda{}
+
+ # +=, without block, non-popped
+ assert_equal([[], {}, nil, [4], {}, nil], h.v{h[**kw] += 1})
+ assert_equal([[0], {}, nil, [0, 4], {}, nil], h.v{h[0, **kw] += 1})
+ assert_equal([[0], {}, nil, [0, 4], {}, nil], h.v{h[0, *a, **kw] += 1})
+ assert_equal([[], {kw: 5}, nil, [4], {kw: 5}, nil], h.v{h[kw: 5] += 1})
+ assert_equal([[], {kw: 5, a: 2}, nil, [4], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] += 1})
+ assert_equal([[], {kw: 5, a: 2}, nil, [4], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] += 1})
+ assert_equal([[0], {kw: 5, a: 2}, nil, [0, 4], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] += 1})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil, [0, 4], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] += 1})
+
+ # +=, with block, non-popped
+ assert_equal([[], {}, b, [4], {}, b], h.v{h[**kw, &b] += 1})
+ assert_equal([[0], {}, b, [0, 4], {}, b], h.v{h[0, **kw, &b] += 1})
+ assert_equal([[0], {}, b, [0, 4], {}, b], h.v{h[0, *a, **kw, &b] += 1})
+ assert_equal([[], {kw: 5}, b, [4], {kw: 5}, b], h.v{h[kw: 5, &b] += 1})
+ assert_equal([[], {kw: 5, a: 2}, b, [4], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] += 1})
+ assert_equal([[], {kw: 5, a: 2}, b, [4], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] += 1})
+ assert_equal([[0], {kw: 5, a: 2}, b, [0, 4], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] += 1})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b, [0, 4], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1})
+
+ # +=, without block, popped
+ assert_equal([[], {}, nil, [4], {}, nil], h.v{h[**kw] += 1; nil})
+ assert_equal([[0], {}, nil, [0, 4], {}, nil], h.v{h[0, **kw] += 1; nil})
+ assert_equal([[0], {}, nil, [0, 4], {}, nil], h.v{h[0, *a, **kw] += 1; nil})
+ assert_equal([[], {kw: 5}, nil, [4], {kw: 5}, nil], h.v{h[kw: 5] += 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil, [4], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] += 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil, [4], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] += 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, nil, [0, 4], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] += 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil, [0, 4], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] += 1; nil})
+
+ # +=, with block, popped
+ assert_equal([[], {}, b, [4], {}, b], h.v{h[**kw, &b] += 1; nil})
+ assert_equal([[0], {}, b, [0, 4], {}, b], h.v{h[0, **kw, &b] += 1; nil})
+ assert_equal([[0], {}, b, [0, 4], {}, b], h.v{h[0, *a, **kw, &b] += 1; nil})
+ assert_equal([[], {kw: 5}, b, [4], {kw: 5}, b], h.v{h[kw: 5, &b] += 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b, [4], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] += 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b, [4], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] += 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, b, [0, 4], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] += 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b, [0, 4], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1; nil})
+
+ # &&=, without block, non-popped
+ assert_equal([[], {}, nil, [1], {}, nil], h.v{h[**kw] &&= 1})
+ assert_equal([[0], {}, nil, [0, 1], {}, nil], h.v{h[0, **kw] &&= 1})
+ assert_equal([[0], {}, nil, [0, 1], {}, nil], h.v{h[0, *a, **kw] &&= 1})
+ assert_equal([[], {kw: 5}, nil, [1], {kw: 5}, nil], h.v{h[kw: 5] &&= 1})
+ assert_equal([[], {kw: 5, a: 2}, nil, [1], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] &&= 1})
+ assert_equal([[], {kw: 5, a: 2}, nil, [1], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] &&= 1})
+ assert_equal([[0], {kw: 5, a: 2}, nil, [0, 1], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] &&= 1})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil, [0, 1], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1})
+
+ # &&=, with block, non-popped
+ assert_equal([[], {}, b, [1], {}, b], h.v{h[**kw, &b] &&= 1})
+ assert_equal([[0], {}, b, [0, 1], {}, b], h.v{h[0, **kw, &b] &&= 1})
+ assert_equal([[0], {}, b, [0, 1], {}, b], h.v{h[0, *a, **kw, &b] &&= 1})
+ assert_equal([[], {kw: 5}, b, [1], {kw: 5}, b], h.v{h[kw: 5, &b] &&= 1})
+ assert_equal([[], {kw: 5, a: 2}, b, [1], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] &&= 1})
+ assert_equal([[], {kw: 5, a: 2}, b, [1], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] &&= 1})
+ assert_equal([[0], {kw: 5, a: 2}, b, [0, 1], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] &&= 1})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b, [0, 1], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1})
+
+ # &&=, without block, popped
+ assert_equal([[], {}, nil, [1], {}, nil], h.v{h[**kw] &&= 1; nil})
+ assert_equal([[0], {}, nil, [0, 1], {}, nil], h.v{h[0, **kw] &&= 1; nil})
+ assert_equal([[0], {}, nil, [0, 1], {}, nil], h.v{h[0, *a, **kw] &&= 1; nil})
+ assert_equal([[], {kw: 5}, nil, [1], {kw: 5}, nil], h.v{h[kw: 5] &&= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil, [1], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] &&= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil, [1], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] &&= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, nil, [0, 1], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] &&= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil, [0, 1], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1; nil})
+
+ # &&=, with block, popped
+ assert_equal([[], {}, b, [1], {}, b], h.v{h[**kw, &b] &&= 1; nil})
+ assert_equal([[0], {}, b, [0, 1], {}, b], h.v{h[0, **kw, &b] &&= 1; nil})
+ assert_equal([[0], {}, b, [0, 1], {}, b], h.v{h[0, *a, **kw, &b] &&= 1; nil})
+ assert_equal([[], {kw: 5}, b, [1], {kw: 5}, b], h.v{h[kw: 5, &b] &&= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b, [1], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] &&= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b, [1], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] &&= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, b, [0, 1], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] &&= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b, [0, 1], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1; nil})
+
+ # ||=, without block, non-popped
+ assert_equal([[], {}, nil], h.v{h[**kw] ||= 1})
+ assert_equal([[0], {}, nil], h.v{h[0, **kw] ||= 1})
+ assert_equal([[0], {}, nil], h.v{h[0, *a, **kw] ||= 1})
+ assert_equal([[], {kw: 5}, nil], h.v{h[kw: 5] ||= 1})
+ assert_equal([[], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] ||= 1})
+ assert_equal([[], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] ||= 1})
+ assert_equal([[0], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] ||= 1})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1})
+
+ # ||=, with block, non-popped
+ assert_equal([[], {}, b], h.v{h[**kw, &b] ||= 1})
+ assert_equal([[0], {}, b], h.v{h[0, **kw, &b] ||= 1})
+ assert_equal([[0], {}, b], h.v{h[0, *a, **kw, &b] ||= 1})
+ assert_equal([[], {kw: 5}, b], h.v{h[kw: 5, &b] ||= 1})
+ assert_equal([[], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] ||= 1})
+ assert_equal([[], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] ||= 1})
+ assert_equal([[0], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] ||= 1})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1})
+
+ # ||=, without block, popped
+ assert_equal([[], {}, nil], h.v{h[**kw] ||= 1; nil})
+ assert_equal([[0], {}, nil], h.v{h[0, **kw] ||= 1; nil})
+ assert_equal([[0], {}, nil], h.v{h[0, *a, **kw] ||= 1; nil})
+ assert_equal([[], {kw: 5}, nil], h.v{h[kw: 5] ||= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] ||= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, nil], h.v{h[kw: 5, a: 2] ||= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, nil], h.v{h[0, kw: 5, a: 2] ||= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, nil: 3}, nil], h.v{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1; nil})
+
+ # ||=, with block, popped
+ assert_equal([[], {}, b], h.v{h[**kw, &b] ||= 1; nil})
+ assert_equal([[0], {}, b], h.v{h[0, **kw, &b] ||= 1; nil})
+ assert_equal([[0], {}, b], h.v{h[0, *a, **kw, &b] ||= 1; nil})
+ assert_equal([[], {kw: 5}, b], h.v{h[kw: 5, &b] ||= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] ||= 1; nil})
+ assert_equal([[], {kw: 5, a: 2}, b], h.v{h[kw: 5, a: 2, &b] ||= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2}, b], h.v{h[0, kw: 5, a: 2, &b] ||= 1; nil})
+ assert_equal([[0], {kw: 5, a: 2, b: 3}, b], h.v{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1; nil})
+
+ end
+
+ def test_kwsplat_block_order_op_asgn
+ o = Object.new
+ ary = []
+ o.define_singleton_method(:to_a) {ary << :to_a; []}
+ o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
+ o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
+
+ def o.[](...) 2 end
+ def o.[]=(...) end
+
+ o[kw: 1] += 1
+ assert_equal([], ary)
+
+ o[**o] += 1
+ assert_equal([:to_hash], ary)
+
+ ary.clear
+ o[**o, &o] += 1
+ # to_proc called twice because no VM instruction for coercing to proc
+ assert_equal([:to_hash, :to_proc, :to_proc], ary)
+
+ ary.clear
+ o[*o, **o, &o] += 1
+ assert_equal([:to_a, :to_hash, :to_proc, :to_proc], ary)
+ end
+
def test_call_splat_order
bug12860 = '[ruby-core:77701] [Bug# 12860]'
ary = [1, 2]