summaryrefslogtreecommitdiff
path: root/spec/ruby/language
diff options
context:
space:
mode:
authorBenoit Daloze <eregontp@gmail.com>2020-01-28 20:47:48 +0100
committerBenoit Daloze <eregontp@gmail.com>2020-01-28 20:47:48 +0100
commit809f0b8a1357f14f9645210d4812f4400c8d397e (patch)
tree7ce6192f94b1dc4b004798aa5d0c4d6bac02577f /spec/ruby/language
parented377cc9aaf1ccbede19ddc6c464f5fbf3cabc34 (diff)
Update to ruby/spec@f8a2d54
Diffstat (limited to 'spec/ruby/language')
-rw-r--r--spec/ruby/language/alias_spec.rb5
-rw-r--r--spec/ruby/language/comment_spec.rb15
-rw-r--r--spec/ruby/language/numbered_parameters_spec.rb87
-rw-r--r--spec/ruby/language/numbers_spec.rb4
-rw-r--r--spec/ruby/language/pattern_matching_spec.rb966
-rw-r--r--spec/ruby/language/predefined_spec.rb31
-rw-r--r--spec/ruby/language/range_spec.rb7
-rw-r--r--spec/ruby/language/rescue_spec.rb10
8 files changed, 1125 insertions, 0 deletions
diff --git a/spec/ruby/language/alias_spec.rb b/spec/ruby/language/alias_spec.rb
index 79348f70c3..13f5c49513 100644
--- a/spec/ruby/language/alias_spec.rb
+++ b/spec/ruby/language/alias_spec.rb
@@ -255,4 +255,9 @@ describe "The alias keyword" do
code = '$a = 1; $b = 2; alias $b $a; p [$a, $b]; $b = 3; p [$a, $b]'
ruby_exe(code).should == "[1, 1]\n[3, 3]\n"
end
+
+ it "supports aliasing twice the same global variables" do
+ code = '$a = 1; alias $b $a; alias $b $a; p [$a, $b]'
+ ruby_exe(code).should == "[1, 1]\n"
+ end
end
diff --git a/spec/ruby/language/comment_spec.rb b/spec/ruby/language/comment_spec.rb
new file mode 100644
index 0000000000..690e844dc0
--- /dev/null
+++ b/spec/ruby/language/comment_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe "The comment" do
+ ruby_version_is "2.7" do
+ it "can be placed between fluent dot now" do
+ code = <<~CODE
+ 10
+ # some comment
+ .to_s
+ CODE
+
+ eval(code).should == '10'
+ end
+ end
+end
diff --git a/spec/ruby/language/numbered_parameters_spec.rb b/spec/ruby/language/numbered_parameters_spec.rb
new file mode 100644
index 0000000000..e7b2204d4a
--- /dev/null
+++ b/spec/ruby/language/numbered_parameters_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Numbered parameters" do
+ it "provides default parameters _1, _2, ... in a block" do
+ -> { _1 }.call("a").should == "a"
+ proc { _1 }.call("a").should == "a"
+ lambda { _1 }.call("a").should == "a"
+ ["a"].map { _1 }.should == ["a"]
+ end
+
+ it "assigns nil to not passed parameters" do
+ proc { [_1, _2] }.call("a").should == ["a", nil]
+ proc { [_1, _2] }.call("a", "b").should == ["a", "b"]
+ end
+
+ it "supports variables _1-_9 only for the first 9 passed parameters" do
+ block = proc { [_1, _2, _3, _4, _5, _6, _7, _8, _9] }
+ result = block.call(1, 2, 3, 4, 5, 6, 7, 8, 9)
+ result.should == [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ end
+
+ it "does not support more than 9 parameters" do
+ -> {
+ proc { [_10] }.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ }.should raise_error(NameError, /undefined local variable or method `_10'/)
+ end
+
+ it "can not be used in both outer and nested blocks at the same time" do
+ -> {
+ eval("-> { _1; -> { _2 } }")
+ }.should raise_error(SyntaxError, /numbered parameter is already used in.+ outer block here/m)
+ end
+
+ it "can be overwritten with local variable" do
+ suppress_warning do
+ eval <<~CODE
+ _1 = 0
+ proc { _1 }.call("a").should == 0
+ CODE
+ end
+ end
+
+ it "warns when numbered parameter is overriten with local variable" do
+ -> {
+ eval("_1 = 0")
+ }.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
+ end
+
+ it "raises SyntaxError when block parameters are specified explicitly" do
+ -> { eval("-> () { _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ -> { eval("-> (x) { _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+
+ -> { eval("proc { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ -> { eval("proc { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+
+ -> { eval("lambda { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ -> { eval("lambda { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+
+ -> { eval("['a'].map { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ -> { eval("['a'].map { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ end
+
+ it "affects block arity" do
+ -> { _1 }.arity.should == 1
+ -> { _2 }.arity.should == 2
+ -> { _3 }.arity.should == 3
+ -> { _4 }.arity.should == 4
+ -> { _5 }.arity.should == 5
+ -> { _6 }.arity.should == 6
+ -> { _7 }.arity.should == 7
+ -> { _8 }.arity.should == 8
+ -> { _9 }.arity.should == 9
+
+ -> { _9 }.arity.should == 9
+ proc { _9 }.arity.should == 9
+ lambda { _9 }.arity.should == 9
+ end
+
+ it "does not work in methods" do
+ obj = Object.new
+ def obj.foo; _1 end
+
+ -> { obj.foo("a") }.should raise_error(ArgumentError, /wrong number of arguments/)
+ end
+ end
+end
diff --git a/spec/ruby/language/numbers_spec.rb b/spec/ruby/language/numbers_spec.rb
index c82a630632..418bc60fa6 100644
--- a/spec/ruby/language/numbers_spec.rb
+++ b/spec/ruby/language/numbers_spec.rb
@@ -45,6 +45,10 @@ describe "A number literal" do
eval('-3r').should == Rational(-3, 1)
end
+ it "can be a float literal with trailing 'r' to represent a Rational" do
+ eval('0.0174532925199432957r').should == Rational(174532925199432957, 10000000000000000000)
+ end
+
it "can be an bignum literal with trailing 'r' to represent a Rational" do
eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1)
eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1)
diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb
new file mode 100644
index 0000000000..6a6de506ff
--- /dev/null
+++ b/spec/ruby/language/pattern_matching_spec.rb
@@ -0,0 +1,966 @@
+require_relative '../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Pattern matching" do
+ # TODO: Remove excessive eval calls when support of previous version
+ # Ruby 2.6 will be dropped
+
+ before do
+ ScratchPad.record []
+ end
+
+ it "can be standalone in operator that deconstructs value" do
+ eval(<<-RUBY).should == [0, 1]
+ [0, 1] in [a, b]
+ [a, b]
+ RUBY
+ end
+
+ it "extends case expression with case/in construction" do
+ eval(<<~RUBY).should == :bar
+ case [0, 1]
+ in [0]
+ :foo
+ in [0, 1]
+ :bar
+ end
+ RUBY
+ end
+
+ it "allows using then operator" do
+ eval(<<~RUBY).should == :bar
+ case [0, 1]
+ in [0] then :foo
+ in [0, 1] then :bar
+ end
+ RUBY
+ end
+
+ it "warns about pattern matching is experimental feature" do
+ -> {
+ eval <<~RUBY
+ case 0
+ in 0
+ end
+ RUBY
+ }.should complain(/warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!/)
+ end
+
+ it "binds variables" do
+ eval(<<~RUBY).should == 1
+ case [0, 1]
+ in [0, a]
+ a
+ end
+ RUBY
+ end
+
+ it "cannot mix in and when operators" do
+ -> {
+ eval <<~RUBY
+ case []
+ when 1 == 1
+ in []
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /syntax error, unexpected `in'/)
+
+ -> {
+ eval <<~RUBY
+ case []
+ in []
+ when 1 == 1
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /syntax error, unexpected `when'/)
+ end
+
+ it "checks patterns until the first matching" do
+ eval(<<~RUBY).should == :bar
+ case [0, 1]
+ in [0]
+ :foo
+ in [0, 1]
+ :bar
+ in [0, 1]
+ :baz
+ end
+ RUBY
+ end
+
+ it "executes else clause if no pattern matches" do
+ eval(<<~RUBY).should == false
+ case [0, 1]
+ in [0]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "raises NoMatchingPatternError if no pattern matches and no else clause" do
+ -> {
+ eval <<~RUBY
+ case [0, 1]
+ in [0]
+ end
+ RUBY
+ }.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
+ end
+
+ it "does not allow calculation or method calls in a pattern" do
+ -> {
+ eval <<~RUBY
+ case 0
+ in 1 + 1
+ true
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /unexpected/)
+ end
+
+ describe "guards" do
+ it "supports if guard" do
+ eval(<<~RUBY).should == false
+ case 0
+ in 0 if false
+ true
+ else
+ false
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case 0
+ in 0 if true
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "supports unless guard" do
+ eval(<<~RUBY).should == false
+ case 0
+ in 0 unless true
+ true
+ else
+ false
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case 0
+ in 0 unless false
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "makes bound variables visible in guard" do
+ eval(<<~RUBY).should == true
+ case [0, 1]
+ in [a, 1] if a >= 0
+ true
+ end
+ RUBY
+ end
+
+ it "does not evaluate guard if pattern does not match" do
+ eval <<~RUBY
+ case 0
+ in 1 if (ScratchPad << :foo) || true
+ else
+ end
+ RUBY
+
+ ScratchPad.recorded.should == []
+ end
+
+ it "takes guards into account when there are several matching patterns" do
+ eval(<<~RUBY).should == :bar
+ case 0
+ in 0 if false
+ :foo
+ in 0 if true
+ :bar
+ end
+ RUBY
+ end
+
+ it "executes else clause if no guarded pattern matches" do
+ eval(<<~RUBY).should == false
+ case 0
+ in 0 if false
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "raises NoMatchingPatternError if no guarded pattern matches and no else clause" do
+ -> {
+ eval <<~RUBY
+ case [0, 1]
+ in [0, 1] if false
+ end
+ RUBY
+ }.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
+ end
+ end
+
+ describe "value pattern" do
+ it "matches an object such that pattern === object" do
+ eval(<<~RUBY).should == true
+ case 0
+ in 0
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case 0
+ in (-1..1)
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case 0
+ in Integer
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case "0"
+ in /0/
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case "0"
+ in ->(s) { s == "0" }
+ true
+ end
+ RUBY
+ end
+
+ it "allows string literal with interpolation" do
+ x = "x"
+
+ eval(<<~RUBY).should == true
+ case "x"
+ in "#{x + ""}"
+ true
+ end
+ RUBY
+ end
+ end
+
+ describe "variable pattern" do
+ it "matches a value and binds variable name to this value" do
+ eval(<<~RUBY).should == 0
+ case 0
+ in a
+ a
+ end
+ RUBY
+ end
+
+ it "makes bounded variable visible outside a case statement scope" do
+ eval(<<~RUBY).should == 0
+ case 0
+ in a
+ end
+
+ a
+ RUBY
+ end
+
+ it "create local variables even if a pattern doesn't match" do
+ eval(<<~RUBY).should == [0, nil, nil]
+ case 0
+ in a
+ in b
+ in c
+ end
+
+ [a, b, c]
+ RUBY
+ end
+
+ it "allow using _ name to drop values" do
+ eval(<<~RUBY).should == 0
+ case [0, 1]
+ in [a, _]
+ a
+ end
+ RUBY
+ end
+
+ it "supports using _ in a pattern several times" do
+ eval(<<~RUBY).should == 2
+ case [0, 1, 2]
+ in [0, _, _]
+ _
+ end
+ RUBY
+ end
+
+ it "supports using any name with _ at the beginning in a pattern several times" do
+ eval(<<~RUBY).should == 2
+ case [0, 1, 2]
+ in [0, _x, _x]
+ _x
+ end
+ RUBY
+
+ eval(<<~RUBY).should == 2
+ case {a: 0, b: 1, c: 2}
+ in {a: 0, b: _x, c: _x}
+ _x
+ end
+ RUBY
+ end
+
+ it "does not support using variable name (except _) several times" do
+ -> {
+ eval <<~RUBY
+ case [0]
+ in [a, a]
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /duplicated variable name/)
+ end
+
+ it "supports existing variables in a pattern specified with ^ operator" do
+ a = 0
+
+ eval(<<~RUBY).should == true
+ case 0
+ in ^a
+ true
+ end
+ RUBY
+ end
+
+ it "allows applying ^ operator to bound variables" do
+ eval(<<~RUBY).should == 1
+ case [1, 1]
+ in [n, ^n]
+ n
+ end
+ RUBY
+
+ eval(<<~RUBY).should == false
+ case [1, 2]
+ in [n, ^n]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "requires bound variable to be specified in a pattern before ^ operator when it relies on a bound variable" do
+ -> {
+ eval <<~RUBY
+ case [1, 2]
+ in [^n, n]
+ true
+ else
+ false
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /n: no such local variable/)
+ end
+ end
+
+ describe "alternative pattern" do
+ it "matches if any of patterns matches" do
+ eval(<<~RUBY).should == true
+ case 0
+ in 0 | 1 | 2
+ true
+ end
+ RUBY
+ end
+
+ it "does not support variable binding" do
+ -> {
+ eval <<~RUBY
+ case [0, 1]
+ in [0, 0] | [0, a]
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /illegal variable in alternative pattern/)
+ end
+ end
+
+ describe "AS pattern" do
+ it "binds a variable to a value if pattern matches" do
+ eval(<<~RUBY).should == 0
+ case 0
+ in Integer => n
+ n
+ end
+ RUBY
+ end
+
+ it "can be used as a nested pattern" do
+ eval(<<~RUBY).should == [2, 3]
+ case [1, [2, 3]]
+ in [1, Array => ary]
+ ary
+ end
+ RUBY
+ end
+ end
+
+ describe "Array pattern" do
+ it "supports form Constant(pat, pat, ...)" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in Array(0, 1, 2)
+ true
+ end
+ RUBY
+ end
+
+ it "supports form Constant[pat, pat, ...]" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in Array[0, 1, 2]
+ true
+ end
+ RUBY
+ end
+
+ it "supports form [pat, pat, ...]" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in [0, 1, 2]
+ true
+ end
+ RUBY
+ end
+
+ it "supports form pat, pat, ..." do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in 0, 1, 2
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == 1
+ case [0, 1, 2]
+ in 0, a, 2
+ a
+ end
+ RUBY
+
+ eval(<<~RUBY).should == [1, 2]
+ case [0, 1, 2]
+ in 0, *rest
+ rest
+ end
+ RUBY
+ end
+
+ it "matches an object with #deconstruct method which returns an array and each element in array matches element in pattern" do
+ obj = Object.new
+ def obj.deconstruct; [0, 1] end
+
+ eval(<<~RUBY).should == true
+ case obj
+ in [Integer, Integer]
+ true
+ end
+ RUBY
+ end
+
+ it "does not match object if Constant === object returns false" do
+ eval(<<~RUBY).should == false
+ case [0, 1, 2]
+ in String[0, 1, 2]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "does not match object without #deconstruct method" do
+ obj = Object.new
+
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "raises TypeError if #deconstruct method does not return array" do
+ obj = Object.new
+ def obj.deconstruct; "" end
+
+ -> {
+ eval <<~RUBY
+ case obj
+ in Object[]
+ else
+ end
+ RUBY
+ }.should raise_error(TypeError, /deconstruct must return Array/)
+ end
+
+ it "does not match object if elements of array returned by #deconstruct method does not match elements in pattern" do
+ obj = Object.new
+ def obj.deconstruct; [1] end
+
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[0]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "binds variables" do
+ eval(<<~RUBY).should == [0, 1, 2]
+ case [0, 1, 2]
+ in [a, b, c]
+ [a, b, c]
+ end
+ RUBY
+ end
+
+ it "binds variable even if patter matches only partially" do
+ a = nil
+
+ eval(<<~RUBY).should == 0
+ case [0, 1, 2]
+ in [a, 1, 3]
+ else
+ end
+
+ a
+ RUBY
+ end
+
+ it "supports splat operator *rest" do
+ eval(<<~RUBY).should == [1, 2]
+ case [0, 1, 2]
+ in [0, *rest]
+ rest
+ end
+ RUBY
+ end
+
+ it "does not match partially by default" do
+ eval(<<~RUBY).should == false
+ case [0, 1, 2, 3]
+ in [1, 2]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "does match partially from the array beginning if list + , syntax used" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2, 3]
+ in [0, 1,]
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case [0, 1, 2, 3]
+ in 0, 1,;
+ true
+ end
+ RUBY
+ end
+
+ it "matches [] with []" do
+ eval(<<~RUBY).should == true
+ case []
+ in []
+ true
+ end
+ RUBY
+ end
+
+ it "matches anything with *" do
+ eval(<<~RUBY).should == true
+ case [0, 1]
+ in *;
+ true
+ end
+ RUBY
+ end
+ end
+
+ describe "Hash pattern" do
+ it "supports form Constant(id: pat, id: pat, ...)" do
+ eval(<<~RUBY).should == true
+ case {a: 0, b: 1}
+ in Hash(a: 0, b: 1)
+ true
+ end
+ RUBY
+ end
+
+ it "supports form Constant[id: pat, id: pat, ...]" do
+ eval(<<~RUBY).should == true
+ case {a: 0, b: 1}
+ in Hash[a: 0, b: 1]
+ true
+ end
+ RUBY
+ end
+
+ it "supports form {id: pat, id: pat, ...}" do
+ eval(<<~RUBY).should == true
+ case {a: 0, b: 1}
+ in {a: 0, b: 1}
+ true
+ end
+ RUBY
+ end
+
+ it "supports form id: pat, id: pat, ..." do
+ eval(<<~RUBY).should == true
+ case {a: 0, b: 1}
+ in a: 0, b: 1
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in a: a, b: b
+ [a, b]
+ end
+ RUBY
+
+ eval(<<~RUBY).should == { b: 1, c: 2 }
+ case {a: 0, b: 1, c: 2}
+ in a: 0, **rest
+ rest
+ end
+ RUBY
+ end
+
+ it "supports a: which means a: a" do
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in Hash(a:, b:)
+ [a, b]
+ end
+ RUBY
+
+ a = b = nil
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in Hash[a:, b:]
+ [a, b]
+ end
+ RUBY
+
+ a = b = nil
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in {a:, b:}
+ [a, b]
+ end
+ RUBY
+
+ a = nil
+ eval(<<~RUBY).should == [0, {b: 1, c: 2}]
+ case {a: 0, b: 1, c: 2}
+ in {a:, **rest}
+ [a, rest]
+ end
+ RUBY
+
+ a = b = nil
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in a:, b:
+ [a, b]
+ end
+ RUBY
+ end
+
+ it "can mix key (a:) and key-value (a: b) declarations" do
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in Hash(a:, b: x)
+ [a, x]
+ end
+ RUBY
+ end
+
+ it "supports 'string': key literal" do
+ eval(<<~RUBY).should == true
+ case {a: 0}
+ in {"a": 0}
+ true
+ end
+ RUBY
+ end
+
+ it "does not support non-symbol keys" do
+ -> {
+ eval <<~RUBY
+ case {a: 1}
+ in {"a" => 1}
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /unexpected/)
+ end
+
+ it "does not support string interpolation in keys" do
+ x = "a"
+
+ -> {
+ eval <<~'RUBY'
+ case {a: 1}
+ in {"#{x}": 1}
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /symbol literal with interpolation is not allowed/)
+ end
+
+ it "raise SyntaxError when keys duplicate in pattern" do
+ -> {
+ eval <<~RUBY
+ case {a: 1}
+ in {a: 1, b: 2, a: 3}
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /duplicated key name/)
+ end
+
+ it "matches an object with #deconstruct_keys method which returns a Hash with equal keys and each value in Hash matches value in pattern" do
+ obj = Object.new
+ def obj.deconstruct_keys(*); {a: 1} end
+
+ eval(<<~RUBY).should == true
+ case obj
+ in {a: 1}
+ true
+ end
+ RUBY
+ end
+
+ it "does not match object if Constant === object returns false" do
+ eval(<<~RUBY).should == false
+ case {a: 1}
+ in String[a: 1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "does not match object without #deconstruct_keys method" do
+ obj = Object.new
+
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "does not match object if #deconstruct_keys method does not return Hash" do
+ obj = Object.new
+ def obj.deconstruct_keys(*); "" end
+
+ -> {
+ eval <<~RUBY
+ case obj
+ in Object[a: 1]
+ end
+ RUBY
+ }.should raise_error(TypeError, /deconstruct_keys must return Hash/)
+ end
+
+ it "does not match object if #deconstruct_keys method returns Hash with non-symbol keys" do
+ obj = Object.new
+ def obj.deconstruct_keys(*); {"a" => 1} end
+
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "does not match object if elements of Hash returned by #deconstruct_keys method does not match values in pattern" do
+ obj = Object.new
+ def obj.deconstruct_keys(*); {a: 1} end
+
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 2]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "passes keys specified in pattern as arguments to #deconstruct_keys method" do
+ obj = Object.new
+
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2, c: 3}
+ end
+
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, b: 2, c: 3]
+ end
+ RUBY
+
+ ScratchPad.recorded.should == [[[:a, :b, :c]]]
+ end
+
+ it "passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" do
+ obj = Object.new
+
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2, c: 3}
+ end
+
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, b: 2, **]
+ end
+ RUBY
+
+ ScratchPad.recorded.should == [[[:a, :b]]]
+ end
+
+ it "passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" do
+ obj = Object.new
+
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2}
+ end
+
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, **rest]
+ end
+ RUBY
+
+ ScratchPad.recorded.should == [[nil]]
+ end
+
+ it "binds variables" do
+ eval(<<~RUBY).should == [0, 1, 2]
+ case {a: 0, b: 1, c: 2}
+ in {a: x, b: y, c: z}
+ [x, y, z]
+ end
+ RUBY
+ end
+
+ it "binds variable even if pattern matches only partially" do
+ x = nil
+
+ eval(<<~RUBY).should == 0
+ case {a: 0, b: 1}
+ in {a: x, b: 2}
+ else
+ end
+
+ x
+ RUBY
+ end
+
+ it "supports double splat operator **rest" do
+ eval(<<~RUBY).should == {b: 1, c: 2}
+ case {a: 0, b: 1, c: 2}
+ in {a: 0, **rest}
+ rest
+ end
+ RUBY
+ end
+
+ it "treats **nil like there should not be any other keys in a matched Hash" do
+ eval(<<~RUBY).should == true
+ case {a: 1, b: 2}
+ in {a: 1, b: 2, **nil}
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == false
+ case {a: 1, b: 2}
+ in {a: 1, **nil}
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "can match partially" do
+ eval(<<~RUBY).should == true
+ case {a: 1, b: 2}
+ in {a: 1}
+ true
+ end
+ RUBY
+ end
+
+ it "matches {} with {}" do
+ eval(<<~RUBY).should == true
+ case {}
+ in {}
+ true
+ end
+ RUBY
+ end
+
+ it "matches anything with **" do
+ eval(<<~RUBY).should == true
+ case {a: 1}
+ in **;
+ true
+ end
+ RUBY
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb
index cec6bc852c..6f902eb822 100644
--- a/spec/ruby/language/predefined_spec.rb
+++ b/spec/ruby/language/predefined_spec.rb
@@ -398,6 +398,19 @@ describe "Predefined global $!" do
$!.should == nil
end
+ it "should be cleared when an exception is rescued even when a non-local return from block" do
+ [ 1 ].each do
+ begin
+ raise StandardError.new('err')
+ rescue => e
+ $!.should == e
+ return
+ end
+ end
+
+ $!.should == nil
+ end
+
it "should not be cleared when an exception is not rescued" do
e = StandardError.new
begin
@@ -633,6 +646,12 @@ describe "Predefined global $," do
it "raises TypeError if assigned a non-String" do
-> { $, = Object.new }.should raise_error(TypeError)
end
+
+ ruby_version_is "2.7" do
+ it "warns if assigned non-nil" do
+ -> { $, = "_" }.should complain(/warning: `\$,' is deprecated/)
+ end
+ end
end
describe "Predefined global $." do
@@ -662,6 +681,18 @@ describe "Predefined global $." do
end
end
+describe "Predefined global $;" do
+ after :each do
+ $; = nil
+ end
+
+ ruby_version_is "2.7" do
+ it "warns if assigned non-nil" do
+ -> { $; = "_" }.should complain(/warning: `\$;' is deprecated/)
+ end
+ end
+end
+
describe "Predefined global $_" do
it "is set to the last line read by e.g. StringIO#gets" do
stdin = StringIO.new("foo\nbar\n", "r")
diff --git a/spec/ruby/language/range_spec.rb b/spec/ruby/language/range_spec.rb
index c720c5b98e..c0f90f84d6 100644
--- a/spec/ruby/language/range_spec.rb
+++ b/spec/ruby/language/range_spec.rb
@@ -16,4 +16,11 @@ describe "Literal Ranges" do
eval("(1...)").should == Range.new(1, nil, true)
end
end
+
+ ruby_version_is "2.7" do
+ it "creates beginless ranges" do
+ eval("(..1)").should == Range.new(nil, 1)
+ eval("(...1)").should == Range.new(nil, 1, true)
+ end
+ end
end
diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb
index a912e17431..aae16bbefb 100644
--- a/spec/ruby/language/rescue_spec.rb
+++ b/spec/ruby/language/rescue_spec.rb
@@ -481,5 +481,15 @@ describe "The rescue keyword" do
a = raise(Exception) rescue 1
}.should raise_error(Exception)
end
+
+ ruby_version_is "2.7" do
+ it "rescues with multiple assignment" do
+
+ a, b = raise rescue [1, 2]
+
+ a.should == 1
+ b.should == 2
+ end
+ end
end
end