mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
db0e0dad11
[Bug #18990]
1711 lines
28 KiB
Ruby
1711 lines
28 KiB
Ruby
# frozen_string_literal: true
|
|
require 'test/unit'
|
|
|
|
class TestPatternMatching < Test::Unit::TestCase
|
|
class NullFormatter
|
|
def message_for(corrections)
|
|
""
|
|
end
|
|
end
|
|
|
|
def setup
|
|
if defined?(DidYouMean)
|
|
@original_formatter = DidYouMean.formatter
|
|
DidYouMean.formatter = NullFormatter.new
|
|
end
|
|
end
|
|
|
|
def teardown
|
|
if defined?(DidYouMean)
|
|
DidYouMean.formatter = @original_formatter
|
|
end
|
|
end
|
|
|
|
class C
|
|
class << self
|
|
attr_accessor :keys
|
|
end
|
|
|
|
def initialize(obj)
|
|
@obj = obj
|
|
end
|
|
|
|
def deconstruct
|
|
@obj
|
|
end
|
|
|
|
def deconstruct_keys(keys)
|
|
C.keys = keys
|
|
@obj
|
|
end
|
|
end
|
|
|
|
def test_basic
|
|
assert_block do
|
|
case 0
|
|
in 0
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in 1
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_raise(NoMatchingPatternError) do
|
|
case 0
|
|
in 1
|
|
false
|
|
end
|
|
end
|
|
|
|
begin
|
|
o = [0]
|
|
case o
|
|
in 1
|
|
false
|
|
end
|
|
rescue => e
|
|
assert_match o.inspect, e.message
|
|
end
|
|
|
|
assert_block do
|
|
begin
|
|
true
|
|
ensure
|
|
case 0
|
|
in 0
|
|
false
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
begin
|
|
true
|
|
ensure
|
|
case 0
|
|
in 1
|
|
else
|
|
false
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_raise(NoMatchingPatternError) do
|
|
begin
|
|
ensure
|
|
case 0
|
|
in 1
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
# suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
|
|
experimental, Warning[:experimental] = Warning[:experimental], false
|
|
eval(%q{
|
|
case true
|
|
in a
|
|
a
|
|
end
|
|
})
|
|
ensure
|
|
Warning[:experimental] = experimental
|
|
end
|
|
|
|
assert_block do
|
|
tap do |a|
|
|
tap do
|
|
case true
|
|
in a
|
|
a
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_raise(NoMatchingPatternError) do
|
|
o = BasicObject.new
|
|
def o.match
|
|
case 0
|
|
in 1
|
|
end
|
|
end
|
|
o.match
|
|
end
|
|
end
|
|
|
|
def test_modifier
|
|
assert_block do
|
|
case 0
|
|
in a if a == 0
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in a if a != 0
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in a unless a != 0
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in a unless a == 0
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_as_pattern
|
|
assert_block do
|
|
case 0
|
|
in 0 => a
|
|
a == 0
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_alternative_pattern
|
|
assert_block do
|
|
[0, 1].all? do |i|
|
|
case i
|
|
in 0 | 1
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in _ | _a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a | 0
|
|
end
|
|
}, /illegal variable in alternative pattern/)
|
|
end
|
|
|
|
def test_var_pattern
|
|
# NODE_DASGN_CURR
|
|
assert_block do
|
|
case 0
|
|
in a
|
|
a == 0
|
|
end
|
|
end
|
|
|
|
# NODE_DASGN
|
|
b = 0
|
|
assert_block do
|
|
case 1
|
|
in b
|
|
b == 1
|
|
end
|
|
end
|
|
|
|
# NODE_LASGN
|
|
case 0
|
|
in c
|
|
assert_equal(0, c)
|
|
else
|
|
flunk
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in ^a
|
|
end
|
|
}, /no such local variable/)
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a, a
|
|
end
|
|
}, /duplicated variable name/)
|
|
|
|
assert_block do
|
|
case [0, 1, 2, 3]
|
|
in _, _, _a, _a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a, {a:}
|
|
end
|
|
}, /duplicated variable name/)
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a, {"a":}
|
|
end
|
|
}, /duplicated variable name/)
|
|
|
|
assert_block do
|
|
case [0, "1"]
|
|
in a, "#{case 1; in a; a; end}"
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case [0, "1"]
|
|
in a, "#{case 1; in a; a; end}", a
|
|
end
|
|
}, /duplicated variable name/)
|
|
|
|
assert_block do
|
|
case 0
|
|
in a
|
|
assert_equal(0, a)
|
|
true
|
|
in a
|
|
flunk
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
0 => [a, a]
|
|
}, /duplicated variable name/)
|
|
end
|
|
|
|
def test_literal_value_pattern
|
|
assert_block do
|
|
case [nil, self, true, false]
|
|
in [nil, self, true, false]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0d170, 0D170, 0xaa, 0xAa, 0xAA, 0Xaa, 0XAa, 0XaA, 0252, 0o252, 0O252]
|
|
in [0d170, 0D170, 0xaa, 0xAa, 0xAA, 0Xaa, 0XAa, 0XaA, 0252, 0o252, 0O252]
|
|
true
|
|
end
|
|
|
|
case [0b10101010, 0B10101010, 12r, 12.3r, 1i, 12.3ri]
|
|
in [0b10101010, 0B10101010, 12r, 12.3r, 1i, 12.3ri]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
x = 'x'
|
|
case ['a', 'a', x]
|
|
in ['a', "a", "#{x}"]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case ["a\n"]
|
|
in [<<END]
|
|
a
|
|
END
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [:a, :"a"]
|
|
in [:a, :"a"]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1, 2, 3, 4, 5]
|
|
in [0..1, 0...2, 0.., 0..., (...5), (..5)]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a..b
|
|
end
|
|
}, /unexpected/)
|
|
|
|
assert_block do
|
|
case 'abc'
|
|
in /a/
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in ->(i) { i == 0 }
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [%(a), %q(a), %Q(a), %w(a), %W(a), %i(a), %I(a), %s(a), %x(echo a), %(), %q(), %Q(), %w(), %W(), %i(), %I(), %s(), 'a']
|
|
in [%(a), %q(a), %Q(a), %w(a), %W(a), %i(a), %I(a), %s(a), %x(echo a), %(), %q(), %Q(), %w(), %W(), %i(), %I(), %s(), %r(a)]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [__FILE__, __LINE__ + 1, __ENCODING__]
|
|
in [__FILE__, __LINE__, __ENCODING__]
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_constant_value_pattern
|
|
assert_block do
|
|
case 0
|
|
in Integer
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in Object::Integer
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in ::Object::Integer
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_pin_operator_value_pattern
|
|
assert_block do
|
|
a = /a/
|
|
case 'abc'
|
|
in ^a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 0]
|
|
in a, ^a
|
|
a == 0
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
@a = /a/
|
|
case 'abc'
|
|
in ^@a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
@@TestPatternMatching = /a/
|
|
case 'abc'
|
|
in ^@@TestPatternMatching
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
$TestPatternMatching = /a/
|
|
case 'abc'
|
|
in ^$TestPatternMatching
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_pin_operator_expr_pattern
|
|
assert_block do
|
|
case 'abc'
|
|
in ^(/a/)
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {name: '2.6', released_at: Time.new(2018, 12, 25)}
|
|
in {released_at: ^(Time.new(2010)..Time.new(2020))}
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case 0
|
|
in ^(0+0)
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_array_pattern
|
|
assert_block do
|
|
[[0], C.new([0])].all? do |i|
|
|
case i
|
|
in 0,;
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1], C.new([0, 1])].all? do |i|
|
|
case i
|
|
in 0,;
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[], C.new([])].all? do |i|
|
|
case i
|
|
in 0,;
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1], C.new([0, 1])].all? do |i|
|
|
case i
|
|
in 0, 1
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0], C.new([0])].all? do |i|
|
|
case i
|
|
in 0, 1
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[], C.new([])].all? do |i|
|
|
case i
|
|
in *a
|
|
a == []
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0], C.new([0])].all? do |i|
|
|
case i
|
|
in *a
|
|
a == [0]
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0], C.new([0])].all? do |i|
|
|
case i
|
|
in *a, 0, 1
|
|
raise a # suppress "unused variable: a" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1], C.new([0, 1])].all? do |i|
|
|
case i
|
|
in *a, 0, 1
|
|
a == []
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1, 2], C.new([0, 1, 2])].all? do |i|
|
|
case i
|
|
in *a, 1, 2
|
|
a == [0]
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[], C.new([])].all? do |i|
|
|
case i
|
|
in *;
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0], C.new([0])].all? do |i|
|
|
case i
|
|
in *, 0, 1
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1], C.new([0, 1])].all? do |i|
|
|
case i
|
|
in *, 0, 1
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[[0, 1, 2], C.new([0, 1, 2])].all? do |i|
|
|
case i
|
|
in *, 1, 2
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([0])
|
|
in C(0)
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([0])
|
|
in Array(0)
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([])
|
|
in C()
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([])
|
|
in Array()
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([0])
|
|
in C[0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([0])
|
|
in Array[0]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([])
|
|
in C[]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([])
|
|
in Array[]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case []
|
|
in []
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([])
|
|
in []
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new([0])
|
|
in [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0,]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1]
|
|
in [0,]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case []
|
|
in [0, *a]
|
|
raise a # suppress "unused variable: a" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0, *a]
|
|
a == []
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0, *a, 1]
|
|
raise a # suppress "unused variable: a" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1]
|
|
in [0, *a, 1]
|
|
a == []
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in [0, *a, 2]
|
|
a == [1]
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case []
|
|
in [0, *]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0, *]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1]
|
|
in [0, *]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case []
|
|
in [0, *a]
|
|
raise a # suppress "unused variable: a" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0, *a]
|
|
a == []
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1]
|
|
in [0, *a]
|
|
a == [1]
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0]
|
|
in [0, *, 1]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1]
|
|
in [0, *, 1]
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_find_pattern
|
|
[0, 1, 2] => [*, 1 => a, *]
|
|
assert_equal(1, a)
|
|
|
|
[0, 1, 2] => [*a, 1 => b, *c]
|
|
assert_equal([0], a)
|
|
assert_equal(1, b)
|
|
assert_equal([2], c)
|
|
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in [*, 9, *]
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in [*, Integer, String, *]
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
[0, 1, 2] => [*a, 1 => b, 2 => c, *d]
|
|
assert_equal([0], a)
|
|
assert_equal(1, b)
|
|
assert_equal(2, c)
|
|
assert_equal([], d)
|
|
|
|
case [0, 1, 2]
|
|
in *, 1 => a, *;
|
|
assert_equal(1, a)
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in String(*, 1, *)
|
|
false
|
|
in Array(*, 1, *)
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in String[*, 1, *]
|
|
false
|
|
in Array[*, 1, *]
|
|
true
|
|
end
|
|
end
|
|
|
|
# https://bugs.ruby-lang.org/issues/17534
|
|
assert_block do
|
|
case [0, 1, 2]
|
|
in x
|
|
x = x # avoid a warning "assigned but unused variable - x"
|
|
true
|
|
in [*, 2, *]
|
|
false
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_hash_pattern
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in a: 0
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in a: 0
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in a: 0
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in a: 0, b: 1
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in a: 0, b: 1
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1, c: 2}, C.new({a: 0, b: 1, c: 2})].all? do |i|
|
|
case i
|
|
in a: 0, b: 1
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in a:
|
|
raise a # suppress "unused variable: a" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in a:
|
|
a == 0
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in a:
|
|
a == 0
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in "a": 0
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in "a":;
|
|
a == 0
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in **a
|
|
a == {}
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in **a
|
|
a == {a: 0}
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in **;
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in **;
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in a:, **b
|
|
raise a # suppress "unused variable: a" warning
|
|
raise b # suppress "unused variable: b" warning
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in a:, **b
|
|
a == 0 && b == {}
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in a:, **b
|
|
a == 0 && b == {b: 1}
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in **nil
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in **nil
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in a:, **nil
|
|
assert_equal(0, a)
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in a:, **nil
|
|
assert_equal(0, a)
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0})
|
|
in C(a: 0)
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {a: 0}
|
|
in C(a: 0)
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0})
|
|
in C[a: 0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {a: 0}
|
|
in C[a: 0]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in {a: 0}
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in {a: 0}
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
|
|
case i
|
|
in {a: 0}
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{}, C.new({})].all? do |i|
|
|
case i
|
|
in {}
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
[{a: 0}, C.new({a: 0})].all? do |i|
|
|
case i
|
|
in {}
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
bug18890 = assert_warning(/(?:.*:[47]: warning: unused literal ignored\n){2}/) do
|
|
eval("#{<<~';;;'}")
|
|
proc do |i|
|
|
case i
|
|
in a:
|
|
0 # line 4
|
|
a
|
|
in "b":
|
|
0 # line 7
|
|
b
|
|
else
|
|
false
|
|
end
|
|
end
|
|
;;;
|
|
end
|
|
[{a: 42}, {b: 42}].each do |i|
|
|
assert_block('newline should be significant after pattern label') do
|
|
bug18890.call(i)
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case _
|
|
in a:, a:
|
|
end
|
|
}, /duplicated key name/)
|
|
|
|
assert_syntax_error(%q{
|
|
case _
|
|
in a?:
|
|
end
|
|
}, /key must be valid as local variables/)
|
|
|
|
assert_block do
|
|
case {a?: true}
|
|
in a?: true
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {a: 0, b: 1}
|
|
in {a: 1,}
|
|
false
|
|
in {a:,}
|
|
_a = a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {a: 0}
|
|
in {a: 1
|
|
}
|
|
false
|
|
in {a:
|
|
2}
|
|
false
|
|
in a: {b:}, c:
|
|
_b = b
|
|
p c
|
|
in {a:
|
|
}
|
|
_a = a
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case _
|
|
in "a-b":
|
|
end
|
|
}, /key must be valid as local variables/)
|
|
|
|
assert_block do
|
|
case {"a-b": true}
|
|
in "a-b": true
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_syntax_error(%q{
|
|
case _
|
|
in "#{a}": a
|
|
end
|
|
}, /symbol literal with interpolation is not allowed/)
|
|
|
|
assert_syntax_error(%q{
|
|
case _
|
|
in "#{a}":
|
|
end
|
|
}, /symbol literal with interpolation is not allowed/)
|
|
end
|
|
|
|
def test_paren
|
|
assert_block do
|
|
case 0
|
|
in (0)
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_nomatchingpatternerror
|
|
assert_equal(StandardError, NoMatchingPatternError.superclass)
|
|
end
|
|
|
|
def test_invalid_syntax
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in a, b:
|
|
end
|
|
}, /unexpected/)
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in [a:]
|
|
end
|
|
}, /unexpected/)
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in {a}
|
|
end
|
|
}, /unexpected/)
|
|
|
|
assert_syntax_error(%q{
|
|
case 0
|
|
in {0 => a}
|
|
end
|
|
}, /unexpected/)
|
|
end
|
|
|
|
################################################################
|
|
|
|
class CTypeError
|
|
def deconstruct
|
|
nil
|
|
end
|
|
|
|
def deconstruct_keys(keys)
|
|
nil
|
|
end
|
|
end
|
|
|
|
def test_deconstruct
|
|
assert_raise(TypeError) do
|
|
case CTypeError.new
|
|
in []
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_deconstruct_keys
|
|
assert_raise(TypeError) do
|
|
case CTypeError.new
|
|
in {}
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {}
|
|
in {}
|
|
C.keys == nil
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0, b: 0, c: 0})
|
|
in {a: 0, b:}
|
|
assert_equal(0, b)
|
|
C.keys == [:a, :b]
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0, b: 0, c: 0})
|
|
in {a: 0, b:, **}
|
|
assert_equal(0, b)
|
|
C.keys == [:a, :b]
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0, b: 0, c: 0})
|
|
in {a: 0, b:, **r}
|
|
assert_equal(0, b)
|
|
assert_equal({c: 0}, r)
|
|
C.keys == nil
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0, b: 0, c: 0})
|
|
in {**}
|
|
C.keys == []
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C.new({a: 0, b: 0, c: 0})
|
|
in {**r}
|
|
assert_equal({a: 0, b: 0, c: 0}, r)
|
|
C.keys == nil
|
|
end
|
|
end
|
|
end
|
|
|
|
################################################################
|
|
|
|
class CDeconstructCache
|
|
def initialize(v)
|
|
@v = v
|
|
end
|
|
|
|
def deconstruct
|
|
@v.shift
|
|
end
|
|
end
|
|
|
|
def test_deconstruct_cache
|
|
assert_block do
|
|
case CDeconstructCache.new([[0]])
|
|
in [1]
|
|
in [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0, 1]])
|
|
in [1,]
|
|
in [0,]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[[0]]])
|
|
in [[1]]
|
|
in [[*a]]
|
|
a == [0]
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0]])
|
|
in [x] if x > 0
|
|
in [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0]])
|
|
in []
|
|
in [1] | [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0]])
|
|
in [1] => _
|
|
in [0] => _
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0]])
|
|
in C[0]
|
|
in CDeconstructCache[0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case [CDeconstructCache.new([[0], [1]])]
|
|
in [[1]]
|
|
false
|
|
in [[1]]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case CDeconstructCache.new([[0, :a, 1]])
|
|
in [*, String => x, *]
|
|
false
|
|
in [*, Symbol => x, *]
|
|
x == :a
|
|
end
|
|
end
|
|
end
|
|
|
|
################################################################
|
|
|
|
class TestPatternMatchingRefinements < Test::Unit::TestCase
|
|
class C1
|
|
def deconstruct
|
|
[:C1]
|
|
end
|
|
end
|
|
|
|
class C2
|
|
end
|
|
|
|
module M
|
|
refine Array do
|
|
def deconstruct
|
|
[0]
|
|
end
|
|
end
|
|
|
|
refine Hash do
|
|
def deconstruct_keys(_)
|
|
{a: 0}
|
|
end
|
|
end
|
|
|
|
refine C2.singleton_class do
|
|
def ===(obj)
|
|
obj.kind_of?(C1)
|
|
end
|
|
end
|
|
end
|
|
|
|
using M
|
|
|
|
def test_refinements
|
|
assert_block do
|
|
case []
|
|
in [0]
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case {}
|
|
in {a: 0}
|
|
true
|
|
end
|
|
end
|
|
|
|
assert_block do
|
|
case C1.new
|
|
in C2(:C1)
|
|
true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
################################################################
|
|
|
|
def test_struct
|
|
assert_block do
|
|
s = Struct.new(:a, :b)
|
|
case s[0, 1]
|
|
in 0, 1
|
|
true
|
|
end
|
|
end
|
|
|
|
s = Struct.new(:a, :b, keyword_init: true)
|
|
assert_block do
|
|
case s[a: 0, b: 1]
|
|
in **r
|
|
r == {a: 0, b: 1}
|
|
end
|
|
end
|
|
assert_block do
|
|
s = Struct.new(:a, :b, keyword_init: true)
|
|
case s[a: 0, b: 1]
|
|
in a:, b:
|
|
a == 0 && b == 1
|
|
end
|
|
end
|
|
assert_block do
|
|
s = Struct.new(:a, :b, keyword_init: true)
|
|
case s[a: 0, b: 1]
|
|
in a:, c:
|
|
raise a # suppress "unused variable: a" warning
|
|
raise c # suppress "unused variable: c" warning
|
|
flunk
|
|
in a:, b:, c:
|
|
flunk
|
|
in b:
|
|
b == 1
|
|
end
|
|
end
|
|
end
|
|
|
|
################################################################
|
|
|
|
def test_one_line
|
|
1 => a
|
|
assert_equal 1, a
|
|
assert_raise(NoMatchingPatternError) do
|
|
{a: 1} => {a: 0}
|
|
end
|
|
|
|
[1, 2] => a, b
|
|
assert_equal 1, a
|
|
assert_equal 2, b
|
|
|
|
{a: 1} => a:
|
|
assert_equal 1, a
|
|
|
|
assert_equal true, (1 in 1)
|
|
assert_equal false, (1 in 2)
|
|
end
|
|
|
|
def test_bug18990
|
|
{a: 0} => a:
|
|
assert_equal 0, a
|
|
{a: 0} => a:
|
|
assert_equal 0, a
|
|
|
|
{a: 0} in a:
|
|
assert_equal 0, a
|
|
{a: 0} in a:
|
|
assert_equal 0, a
|
|
end
|
|
|
|
################################################################
|
|
|
|
def test_single_pattern_error_value_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "0: 1 === 0 does not return true") do
|
|
0 => 1
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_array_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "[]: Hash === [] does not return true") do
|
|
[] => Hash[]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct") do
|
|
0 => []
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[0]: [0] length mismatch (given 1, expected 0)") do
|
|
[0] => []
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[]: [] length mismatch (given 0, expected 1+)") do
|
|
[] => [_, *]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[0, 0]: 1 === 0 does not return true") do
|
|
[0, 0] => [0, 1]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[0, 0]: 1 === 0 does not return true") do
|
|
[0, 0] => [*, 0, 1]
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_find_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "[]: Hash === [] does not return true") do
|
|
[] => Hash[*, _, *]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct") do
|
|
0 => [*, _, *]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[]: [] length mismatch (given 0, expected 1+)") do
|
|
[] => [*, _, *]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[0]: [0] does not match to find pattern") do
|
|
[0] => [*, 1, *]
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[0]: [0] does not match to find pattern") do
|
|
[0] => [*, {a:}, *]
|
|
raise a # suppress "unused variable: a" warning
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_hash_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "{}: Array === {} does not return true") do
|
|
{} => Array[a:]
|
|
raise a # suppress "unused variable: a" warning
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct_keys") do
|
|
0 => {a:}
|
|
raise a # suppress "unused variable: a" warning
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternKeyError, "{:a=>0}: key not found: :aa") do
|
|
{a: 0} => {aa:}
|
|
raise aa # suppress "unused variable: aa" warning
|
|
rescue NoMatchingPatternKeyError => e
|
|
assert_equal({a: 0}, e.matchee)
|
|
assert_equal(:aa, e.key)
|
|
raise e
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternKeyError, "{:a=>{:b=>0}}: key not found: :bb") do
|
|
{a: {b: 0}} => {a: {bb:}}
|
|
raise bb # suppress "unused variable: bb" warning
|
|
rescue NoMatchingPatternKeyError => e
|
|
assert_equal({b: 0}, e.matchee)
|
|
assert_equal(:bb, e.key)
|
|
raise e
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "{:a=>0}: 1 === 0 does not return true") do
|
|
{a: 0} => {a: 1}
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "{:a=>0}: {:a=>0} is not empty") do
|
|
{a: 0} => {}
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "[{:a=>0}]: rest of {:a=>0} is not empty") do
|
|
[{a: 0}] => [{**nil}]
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_as_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "[0]: 1 === 0 does not return true") do
|
|
case [0]
|
|
in [1] => _
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_alternative_pattern
|
|
assert_raise_with_message(NoMatchingPatternError, "0: 2 === 0 does not return true") do
|
|
0 => 1 | 2
|
|
end
|
|
end
|
|
|
|
def test_single_pattern_error_guard_clause
|
|
assert_raise_with_message(NoMatchingPatternError, "0: guard clause does not return true") do
|
|
case 0
|
|
in _ if false
|
|
end
|
|
end
|
|
|
|
assert_raise_with_message(NoMatchingPatternError, "0: guard clause does not return true") do
|
|
case 0
|
|
in _ unless true
|
|
end
|
|
end
|
|
end
|
|
end
|