summaryrefslogtreecommitdiff
path: root/spec/ruby/language
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/language')
-rw-r--r--spec/ruby/language/END_spec.rb26
-rw-r--r--spec/ruby/language/alias_spec.rb33
-rw-r--r--spec/ruby/language/block_spec.rb344
-rw-r--r--spec/ruby/language/case_spec.rb117
-rw-r--r--spec/ruby/language/class_spec.rb31
-rw-r--r--spec/ruby/language/class_variable_spec.rb56
-rw-r--r--spec/ruby/language/comment_spec.rb16
-rw-r--r--spec/ruby/language/constants_spec.rb86
-rw-r--r--spec/ruby/language/def_spec.rb33
-rw-r--r--spec/ruby/language/defined_spec.rb40
-rw-r--r--spec/ruby/language/delegation_spec.rb99
-rw-r--r--spec/ruby/language/file_spec.rb22
-rw-r--r--spec/ruby/language/fixtures/defined.rb3
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rbbin120 -> 181 bytes
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb2
-rw-r--r--spec/ruby/language/fixtures/super.rb14
-rw-r--r--spec/ruby/language/fixtures/variables.rb72
-rw-r--r--spec/ruby/language/hash_spec.rb63
-rw-r--r--spec/ruby/language/heredoc_spec.rb18
-rw-r--r--spec/ruby/language/if_spec.rb43
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb398
-rw-r--r--spec/ruby/language/lambda_spec.rb132
-rw-r--r--spec/ruby/language/method_spec.rb995
-rw-r--r--spec/ruby/language/module_spec.rb33
-rw-r--r--spec/ruby/language/numbered_parameters_spec.rb186
-rw-r--r--spec/ruby/language/numbers_spec.rb2
-rw-r--r--spec/ruby/language/optional_assignments_spec.rb38
-rw-r--r--spec/ruby/language/pattern_matching_spec.rb2179
-rw-r--r--spec/ruby/language/precedence_spec.rb78
-rw-r--r--spec/ruby/language/predefined_spec.rb484
-rw-r--r--spec/ruby/language/proc_spec.rb35
-rw-r--r--spec/ruby/language/range_spec.rb22
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb11
-rw-r--r--spec/ruby/language/regexp/escapes_spec.rb84
-rw-r--r--spec/ruby/language/regexp/repetition_spec.rb8
-rw-r--r--spec/ruby/language/regexp_spec.rb29
-rw-r--r--spec/ruby/language/rescue_spec.rb22
-rw-r--r--spec/ruby/language/return_spec.rb61
-rw-r--r--spec/ruby/language/safe_spec.rb120
-rw-r--r--spec/ruby/language/send_spec.rb54
-rw-r--r--spec/ruby/language/singleton_class_spec.rb24
-rw-r--r--spec/ruby/language/source_encoding_spec.rb2
-rw-r--r--spec/ruby/language/string_spec.rb46
-rw-r--r--spec/ruby/language/super_spec.rb23
-rw-r--r--spec/ruby/language/symbol_spec.rb4
-rw-r--r--spec/ruby/language/undef_spec.rb9
-rw-r--r--spec/ruby/language/variables_spec.rb138
-rw-r--r--spec/ruby/language/yield_spec.rb33
48 files changed, 3080 insertions, 3288 deletions
diff --git a/spec/ruby/language/END_spec.rb b/spec/ruby/language/END_spec.rb
index c84f0cc9ac..762a8db0c0 100644
--- a/spec/ruby/language/END_spec.rb
+++ b/spec/ruby/language/END_spec.rb
@@ -1,33 +1,15 @@
require_relative '../spec_helper'
-require_relative '../shared/kernel/at_exit'
describe "The END keyword" do
- it_behaves_like :kernel_at_exit, :END
-
it "runs only once for multiple calls" do
ruby_exe("10.times { END { puts 'foo' }; } ").should == "foo\n"
end
- it "is affected by the toplevel assignment" do
- ruby_exe("foo = 'foo'; END { puts foo }").should == "foo\n"
- end
-
- it "warns when END is used in a method" do
- ruby_exe(<<~ruby, args: "2>&1").should =~ /warning: END in method; use at_exit/
- def foo
- END { }
- end
- ruby
+ it "runs last in a given code unit" do
+ ruby_exe("END { puts 'bar' }; puts'foo'; ").should == "foo\nbar\n"
end
- context "END blocks and at_exit callbacks are mixed" do
- it "runs them all in reverse order of registration" do
- ruby_exe(<<~ruby).should == "at_exit#2\nEND#2\nat_exit#1\nEND#1\n"
- END { puts 'END#1' }
- at_exit { puts 'at_exit#1' }
- END { puts 'END#2' }
- at_exit { puts 'at_exit#2' }
- ruby
- end
+ it "runs multiple ends in LIFO order" do
+ ruby_exe("END { puts 'foo' }; END { puts 'bar' }").should == "bar\nfoo\n"
end
end
diff --git a/spec/ruby/language/alias_spec.rb b/spec/ruby/language/alias_spec.rb
index 61fddb0184..d1d06e3fac 100644
--- a/spec/ruby/language/alias_spec.rb
+++ b/spec/ruby/language/alias_spec.rb
@@ -52,15 +52,6 @@ describe "The alias keyword" do
@obj.a.should == 5
end
- it "works with an interpolated symbol with non-literal embedded expression on the left-hand side" do
- @meta.class_eval do
- eval %Q{
- alias :"#{'a' + ''.to_s}" value
- }
- end
- @obj.a.should == 5
- end
-
it "works with a simple symbol on the right-hand side" do
@meta.class_eval do
alias a :value
@@ -89,15 +80,6 @@ describe "The alias keyword" do
@obj.a.should == 5
end
- it "works with an interpolated symbol with non-literal embedded expression on the right-hand side" do
- @meta.class_eval do
- eval %Q{
- alias a :"#{'value' + ''.to_s}"
- }
- end
- @obj.a.should == 5
- end
-
it "adds the new method to the list of methods" do
original_methods = @obj.methods
@meta.class_eval do
@@ -252,7 +234,7 @@ describe "The alias keyword" do
it "on top level defines the alias on Object" do
# because it defines on the default definee / current module
- ruby_exe("def foo; end; alias bla foo; print method(:bla).owner").should == "Object"
+ ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object"
end
it "raises a NameError when passed a missing name" do
@@ -261,19 +243,6 @@ describe "The alias keyword" do
e.class.should == NameError
}
end
-
- it "defines the method on the aliased class when the original method is from a parent class" do
- parent = Class.new do
- def parent_method
- end
- end
- child = Class.new(parent) do
- alias parent_method_alias parent_method
- end
-
- child.instance_method(:parent_method_alias).owner.should == child
- child.instance_methods(false).should include(:parent_method_alias)
- end
end
describe "The alias keyword" do
diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb
index 90329e2f6f..b2a3cc84c4 100644
--- a/spec/ruby/language/block_spec.rb
+++ b/spec/ruby/language/block_spec.rb
@@ -40,31 +40,80 @@ describe "A block yielded a single" do
m([1, 2]) { |a=5, b, c, d| [a, b, c, d] }.should == [5, 1, 2, nil]
end
- ruby_version_is "3.2" do
- it "does not autosplat single argument to required arguments when a keyword rest argument is present" do
- m([1, 2]) { |a, **k| [a, k] }.should == [[1, 2], {}]
+ it "assigns elements to required arguments when a keyword rest argument is present" do
+ m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
+ end
+
+ ruby_version_is ''..."3.0" do
+ it "assigns elements to mixed argument types" do
+ suppress_keyword_warning do
+ result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
+ result.should == [1, 2, [], 3, 2, {x: 9}]
+ end
+ end
+
+ it "assigns symbol keys from a Hash to keyword arguments" do
+ suppress_keyword_warning do
+ result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1}, a: 10]
+ end
+ end
+
+ it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do
+ suppress_keyword_warning do
+ obj = mock("coerce block keyword arguments")
+ obj.should_receive(:to_hash).and_return({"a" => 1, b: 2})
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1}, b: 2]
+ end
end
end
- ruby_version_is ''..."3.2" do
- # https://bugs.ruby-lang.org/issues/18633
- it "autosplats single argument to required arguments when a keyword rest argument is present" do
- m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
+ ruby_version_is "3.0" do
+ it "assigns elements to mixed argument types" do
+ result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
+ result.should == [1, 2, [3], {x: 9}, 2, {}]
+ end
+
+ it "does not treat final Hash as keyword arguments and does not autosplat" do
+ result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
+ result.should == [[{"a" => 1, a: 10}], {}]
+ end
+
+ it "does not call #to_hash on final argument to get keyword arguments and does not autosplat" do
+ suppress_keyword_warning do
+ obj = mock("coerce block keyword arguments")
+ obj.should_not_receive(:to_hash)
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [[obj], {}]
+ end
end
end
- it "assigns elements to mixed argument types" do
- result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
- result.should == [1, 2, [3], {x: 9}, 2, {}]
+ ruby_version_is ""..."2.7" do
+ it "calls #to_hash on the argument and uses resulting hash as first argument when optional argument and keyword argument accepted" do
+ obj = mock("coerce block keyword arguments")
+ obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1, "b" => 2}, {}]
+ end
end
- it "does not treat final Hash as keyword arguments and does not autosplat" do
- result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
- result.should == [[{"a" => 1, a: 10}], {}]
+ ruby_version_is "2.7"...'3.0' do
+ it "calls #to_hash on the argument but ignores result when optional argument and keyword argument accepted" do
+ obj = mock("coerce block keyword arguments")
+ obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [obj, {}]
+ end
end
- it "does not call #to_hash on final argument to get keyword arguments and does not autosplat" do
- suppress_keyword_warning do
+ ruby_version_is "3.0" do
+ it "does not call #to_hash on the argument when optional argument and keyword argument accepted and does not autosplat" do
obj = mock("coerce block keyword arguments")
obj.should_not_receive(:to_hash)
@@ -73,42 +122,102 @@ describe "A block yielded a single" do
end
end
- it "does not call #to_hash on the argument when optional argument and keyword argument accepted and does not autosplat" do
- obj = mock("coerce block keyword arguments")
- obj.should_not_receive(:to_hash)
+ describe "when non-symbol keys are in a keyword arguments Hash" do
+ ruby_version_is ""..."3.0" do
+ it "separates non-symbol keys and symbol keys" do
+ suppress_keyword_warning do
+ result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 10}, {b: 2}]
+ end
+ end
+ end
+ ruby_version_is "3.0" do
+ it "does not separate non-symbol keys and symbol keys and does not autosplat" do
+ suppress_keyword_warning do
+ result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
+ result.should == [[{"a" => 10, b: 2}], {}]
+ end
+ end
+ end
+ end
- result = m([obj]) { |a=nil, **b| [a, b] }
- result.should == [[obj], {}]
+ ruby_version_is ""..."3.0" do
+ it "does not treat hashes with string keys as keyword arguments" do
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ result.should == [{"a" => 10}, {}]
+ end
end
- describe "when non-symbol keys are in a keyword arguments Hash" do
- it "does not separate non-symbol keys and symbol keys and does not autosplat" do
+ ruby_version_is "3.0" do
+ it "does not treat hashes with string keys as keyword arguments and does not autosplat" do
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ result.should == [[{"a" => 10}], {}]
+ end
+ end
+
+ ruby_version_is ''...'3.0' do
+ it "calls #to_hash on the last element if keyword arguments are present" do
suppress_keyword_warning do
- result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
- result.should == [[{"a" => 10, b: 2}], {}]
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return({x: 9})
+
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2], 3, {x: 9}]
end
end
- end
- it "does not treat hashes with string keys as keyword arguments and does not autosplat" do
- result = m(["a" => 10]) { |a = nil, **b| [a, b] }
- result.should == [[{"a" => 10}], {}]
- end
+ it "assigns the last element to a non-keyword argument if #to_hash returns nil" do
+ suppress_keyword_warning do
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return(nil)
- it "does not call #to_hash on the last element if keyword arguments are present" do
- obj = mock("destructure block keyword arguments")
- obj.should_not_receive(:to_hash)
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2, 3], obj, {}]
+ end
+ end
+
+ it "calls #to_hash on the last element when there are more arguments than parameters" do
+ suppress_keyword_warning do
+ x = mock("destructure matching block keyword argument")
+ x.should_receive(:to_hash).and_return({x: 9})
+
+ result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
+ result.should == [1, 2, 3, {x: 9}]
+ end
+ end
- result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
- result.should == [1, [2, 3], obj, {}]
+ it "raises a TypeError if #to_hash does not return a Hash" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return(1)
+
+ -> { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(TypeError)
+ end
+
+ it "raises the error raised inside #to_hash" do
+ obj = mock("destructure block keyword arguments")
+ error = RuntimeError.new("error while converting to a hash")
+ obj.should_receive(:to_hash).and_raise(error)
+
+ -> { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(error)
+ end
end
- it "does not call #to_hash on the last element when there are more arguments than parameters" do
- x = mock("destructure matching block keyword argument")
- x.should_not_receive(:to_hash)
+ ruby_version_is '3.0' do
+ it "does not call #to_hash on the last element if keyword arguments are present" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_not_receive(:to_hash)
+
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2, 3], obj, {}]
+ end
+
+ it "does not call #to_hash on the last element when there are more arguments than parameters" do
+ x = mock("destructure matching block keyword argument")
+ x.should_not_receive(:to_hash)
- result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
- result.should == [1, 2, 3, {}]
+ result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
+ result.should == [1, 2, 3, {}]
+ end
end
it "does not call #to_ary on the Array" do
@@ -155,55 +264,12 @@ describe "A block yielded a single" do
m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
end
- it "receives the object if it does not respond to #to_ary" do
- obj = Object.new
-
- m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
- end
-
- it "calls #respond_to? to check if object has method #to_ary" do
- obj = mock("destructure block arguments")
- obj.should_receive(:respond_to?).with(:to_ary, true).and_return(true)
- obj.should_receive(:to_ary).and_return([1, 2])
-
- m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
- end
-
- it "receives the object if it does not respond to #respond_to?" do
- obj = BasicObject.new
-
- m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
- end
-
- it "calls #to_ary on the object when it is defined dynamically" do
- obj = Object.new
- def obj.method_missing(name, *args, &block)
- if name == :to_ary
- [1, 2]
- else
- super
- end
- end
- def obj.respond_to_missing?(name, include_private)
- name == :to_ary
- end
-
- m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
- end
-
it "raises a TypeError if #to_ary does not return an Array" do
obj = mock("destructure block arguments")
obj.should_receive(:to_ary).and_return(1)
-> { m(obj) { |a, b| } }.should raise_error(TypeError)
end
-
- it "raises error transparently if #to_ary raises error on its own" do
- obj = Object.new
- def obj.to_ary; raise "Exception raised in #to_ary" end
-
- -> { m(obj) { |a, b| } }.should raise_error(RuntimeError, "Exception raised in #to_ary")
- end
end
end
@@ -883,18 +949,38 @@ describe "Post-args" do
end
describe "with a circular argument reference" do
- it "raises a SyntaxError if using an existing local with the same name as the argument" do
- a = 1
- -> {
- @proc = eval "proc { |a=a| a }"
- }.should raise_error(SyntaxError)
+ ruby_version_is ''...'2.7' do
+ it "warns and uses a nil value when there is an existing local variable with same name" do
+ a = 1
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "warns and uses a nil value when there is an existing method with same name" do
+ def a; 1; end
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
end
- it "raises a SyntaxError if there is an existing method with the same name as the argument" do
- def a; 1; end
- -> {
- @proc = eval "proc { |a=a| a }"
- }.should raise_error(SyntaxError)
+ ruby_version_is '2.7' do
+ it "raises a SyntaxError if using an existing local with the same name as the argument" do
+ a = 1
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should raise_error(SyntaxError)
+ end
+
+ it "raises a SyntaxError if there is an existing method with the same name as the argument" do
+ def a; 1; end
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should raise_error(SyntaxError)
+ end
end
it "calls an existing method with the same name as the argument if explicitly using ()" do
@@ -918,77 +1004,3 @@ describe "Post-args" do
end
end
end
-
-describe "Anonymous block forwarding" do
- ruby_version_is "3.1" do
- it "forwards blocks to other functions that formally declare anonymous blocks" do
- eval <<-EOF
- def b(&); c(&) end
- def c(&); yield :non_null end
- EOF
-
- b { |c| c }.should == :non_null
- end
-
- it "requires the anonymous block parameter to be declared if directly passing a block" do
- -> { eval "def a; b(&); end; def b; end" }.should raise_error(SyntaxError)
- end
-
- it "works when it's the only declared parameter" do
- eval <<-EOF
- def inner; yield end
- def block_only(&); inner(&) end
- EOF
-
- block_only { 1 }.should == 1
- end
-
- it "works alongside positional parameters" do
- eval <<-EOF
- def inner; yield end
- def pos(arg1, &); inner(&) end
- EOF
-
- pos(:a) { 1 }.should == 1
- end
-
- it "works alongside positional arguments and splatted keyword arguments" do
- eval <<-EOF
- def inner; yield end
- def pos_kwrest(arg1, **kw, &); inner(&) end
- EOF
-
- pos_kwrest(:a, arg: 3) { 1 }.should == 1
- end
-
- it "works alongside positional arguments and disallowed keyword arguments" do
- eval <<-EOF
- def inner; yield end
- def no_kw(arg1, **nil, &); inner(&) end
- EOF
-
- no_kw(:a) { 1 }.should == 1
- end
- end
-
- ruby_version_is "3.2" do
- it "works alongside explicit keyword arguments" do
- eval <<-EOF
- def inner; yield end
- def rest_kw(*a, kwarg: 1, &); inner(&) end
- def kw(kwarg: 1, &); inner(&) end
- def pos_kw_kwrest(arg1, kwarg: 1, **kw, &); inner(&) end
- def pos_rkw(arg1, kwarg1:, &); inner(&) end
- def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &); inner(&) end
- def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &); inner(&) end
- EOF
-
- rest_kw { 1 }.should == 1
- kw { 1 }.should == 1
- pos_kw_kwrest(:a) { 1 }.should == 1
- pos_rkw(:a, kwarg1: 3) { 1 }.should == 1
- all(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
- all_kwrest(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
- end
- end
-end
diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb
index 1a3925c9c6..410cb9afb1 100644
--- a/spec/ruby/language/case_spec.rb
+++ b/spec/ruby/language/case_spec.rb
@@ -103,7 +103,7 @@ describe "The 'case'-construct" do
$1.should == "42"
end
- it "tests with a string interpolated in a regexp" do
+ it "tests with a regexp interpolated within another regexp" do
digits = '\d+'
case "foo44"
when /oo(#{digits})/
@@ -116,7 +116,7 @@ describe "The 'case'-construct" do
$1.should == "44"
end
- it "tests with a regexp interpolated within another regexp" do
+ it "tests with a string interpolated in a regexp" do
digits_regexp = /\d+/
case "foo43"
when /oo(#{digits_regexp})/
@@ -156,15 +156,6 @@ describe "The 'case'-construct" do
end.should == "foo"
end
- it "tests an empty array" do
- case []
- when []
- 'foo'
- else
- 'bar'
- end.should == 'foo'
- end
-
it "expands arrays to lists of values" do
case 'z'
when *['a', 'b', 'c', 'd']
@@ -329,6 +320,49 @@ describe "The 'case'-construct" do
100
end.should == 100
end
+end
+
+describe "The 'case'-construct with no target expression" do
+ it "evaluates the body of the first clause when at least one of its condition expressions is true" do
+ case
+ when true, false; 'foo'
+ end.should == 'foo'
+ end
+
+ it "evaluates the body of the first when clause that is not false/nil" do
+ case
+ when false; 'foo'
+ when 2; 'bar'
+ when 1 == 1; 'baz'
+ end.should == 'bar'
+
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 1; 'bar'
+ end.should == 'bar'
+ end
+
+ it "evaluates the body of the else clause if all when clauses are false/nil" do
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 2; 'bar'
+ else 'baz'
+ end.should == 'baz'
+ end
+
+ it "evaluates multiple conditional expressions as a boolean disjunction" do
+ case
+ when true, false; 'foo'
+ else 'bar'
+ end.should == 'foo'
+
+ case
+ when false, true; 'foo'
+ else 'bar'
+ end.should == 'foo'
+ end
it "evaluates true as only 'true' when true is the first clause" do
case 1
@@ -391,58 +425,6 @@ describe "The 'case'-construct" do
end.should == :called
end
- it "only matches last value in complex expressions within ()" do
- case 'a'
- when ('a'; 'b')
- :wrong_called
- when ('b'; 'a')
- :called
- end.should == :called
- end
-end
-
-describe "The 'case'-construct with no target expression" do
- it "evaluates the body of the first clause when at least one of its condition expressions is true" do
- case
- when true, false; 'foo'
- end.should == 'foo'
- end
-
- it "evaluates the body of the first when clause that is not false/nil" do
- case
- when false; 'foo'
- when 2; 'bar'
- when 1 == 1; 'baz'
- end.should == 'bar'
-
- case
- when false; 'foo'
- when nil; 'foo'
- when 1 == 1; 'bar'
- end.should == 'bar'
- end
-
- it "evaluates the body of the else clause if all when clauses are false/nil" do
- case
- when false; 'foo'
- when nil; 'foo'
- when 1 == 2; 'bar'
- else 'baz'
- end.should == 'baz'
- end
-
- it "evaluates multiple conditional expressions as a boolean disjunction" do
- case
- when true, false; 'foo'
- else 'bar'
- end.should == 'foo'
-
- case
- when false, true; 'foo'
- else 'bar'
- end.should == 'foo'
- end
-
# Homogeneous cases are often optimized to avoid === using a jump table, and should be tested separately.
# See https://github.com/jruby/jruby/issues/6440
it "handles homogeneous cases" do
@@ -451,13 +433,4 @@ describe "The 'case'-construct with no target expression" do
when 2; 'bar'
end.should == 'foo'
end
-
- it "expands arrays to lists of values" do
- case
- when *[false]
- "foo"
- when *[true]
- "bar"
- end.should == "bar"
- end
end
diff --git a/spec/ruby/language/class_spec.rb b/spec/ruby/language/class_spec.rb
index eab3cd0651..83db164e1a 100644
--- a/spec/ruby/language/class_spec.rb
+++ b/spec/ruby/language/class_spec.rb
@@ -17,19 +17,6 @@ describe "The class keyword" do
eval "class ClassSpecsKeywordWithoutSemicolon end"
ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class)
end
-
- it "can redefine a class when called from a block" do
- ClassSpecs::DEFINE_CLASS.call
- A.should be_an_instance_of(Class)
-
- Object.send(:remove_const, :A)
- defined?(A).should be_nil
-
- ClassSpecs::DEFINE_CLASS.call
- A.should be_an_instance_of(Class)
- ensure
- Object.send(:remove_const, :A) if defined?(::A)
- end
end
describe "A class definition" do
@@ -308,10 +295,20 @@ describe "A class definition extending an object (sclass)" do
-> { class TestClass < BasicObject.new; end }.should raise_error(TypeError, error_msg)
end
- it "does not allow accessing the block of the original scope" do
- -> {
- ClassSpecs.sclass_with_block { 123 }
- }.should raise_error(SyntaxError)
+ ruby_version_is ""..."3.0" do
+ it "allows accessing the block of the original scope" do
+ suppress_warning do
+ ClassSpecs.sclass_with_block { 123 }.should == 123
+ end
+ end
+ end
+
+ ruby_version_is "3.0" do
+ it "does not allow accessing the block of the original scope" do
+ -> {
+ ClassSpecs.sclass_with_block { 123 }
+ }.should raise_error(SyntaxError)
+ end
end
it "can use return to cause the enclosing method to return" do
diff --git a/spec/ruby/language/class_variable_spec.rb b/spec/ruby/language/class_variable_spec.rb
index a26a3fb8de..f98deaa081 100644
--- a/spec/ruby/language/class_variable_spec.rb
+++ b/spec/ruby/language/class_variable_spec.rb
@@ -83,32 +83,34 @@ describe 'A class variable definition' do
end
end
-describe 'Accessing a class variable' do
- it "raises a RuntimeError when accessed from the toplevel scope (not in some module or class)" do
- -> {
- eval "@@cvar_toplevel1"
- }.should raise_error(RuntimeError, 'class variable access from toplevel')
- -> {
- eval "@@cvar_toplevel2 = 2"
- }.should raise_error(RuntimeError, 'class variable access from toplevel')
- end
-
- it "does not raise an error when checking if defined from the toplevel scope" do
- -> {
- eval "defined?(@@cvar_toplevel1)"
- }.should_not raise_error
- end
-
- it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do
- parent = Class.new()
- subclass = Class.new(parent)
- subclass.class_variable_set(:@@cvar_overtaken, :subclass)
- parent.class_variable_set(:@@cvar_overtaken, :parent)
-
- -> {
- subclass.class_variable_get(:@@cvar_overtaken)
- }.should raise_error(RuntimeError, /class variable @@cvar_overtaken of .+ is overtaken by .+/)
-
- parent.class_variable_get(:@@cvar_overtaken).should == :parent
+ruby_version_is "3.0" do
+ describe 'Accessing a class variable' do
+ it "raises a RuntimeError when accessed from the toplevel scope (not in some module or class)" do
+ -> {
+ eval "@@cvar_toplevel1"
+ }.should raise_error(RuntimeError, 'class variable access from toplevel')
+ -> {
+ eval "@@cvar_toplevel2 = 2"
+ }.should raise_error(RuntimeError, 'class variable access from toplevel')
+ end
+
+ it "does not raise an error when checking if defined from the toplevel scope" do
+ -> {
+ eval "defined?(@@cvar_toplevel1)"
+ }.should_not raise_error
+ end
+
+ it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do
+ parent = Class.new()
+ subclass = Class.new(parent)
+ subclass.class_variable_set(:@@cvar_overtaken, :subclass)
+ parent.class_variable_set(:@@cvar_overtaken, :parent)
+
+ -> {
+ subclass.class_variable_get(:@@cvar_overtaken)
+ }.should raise_error(RuntimeError, /class variable @@cvar_overtaken of .+ is overtaken by .+/)
+
+ parent.class_variable_get(:@@cvar_overtaken).should == :parent
+ end
end
end
diff --git a/spec/ruby/language/comment_spec.rb b/spec/ruby/language/comment_spec.rb
index dd788e681c..690e844dc0 100644
--- a/spec/ruby/language/comment_spec.rb
+++ b/spec/ruby/language/comment_spec.rb
@@ -1,13 +1,15 @@
require_relative '../spec_helper'
describe "The comment" do
- it "can be placed between fluent dot now" do
- code = <<~CODE
- 10
- # some comment
- .to_s
- CODE
+ 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'
+ eval(code).should == '10'
+ end
end
end
diff --git a/spec/ruby/language/constants_spec.rb b/spec/ruby/language/constants_spec.rb
index 08c534487e..03f1272401 100644
--- a/spec/ruby/language/constants_spec.rb
+++ b/spec/ruby/language/constants_spec.rb
@@ -135,34 +135,18 @@ describe "Literal (A::X) constant resolution" do
ConstantSpecs::ClassB::CS_CONST109.should == :const109_2
end
- ruby_version_is "3.2" do
- it "evaluates left-to-right" do
- mod = Module.new
+ it "evaluates the right hand side before evaluating a constant path" do
+ mod = Module.new
- mod.module_eval <<-EOC
- order = []
- ConstantSpecsRHS = Module.new
- (order << :lhs; ConstantSpecsRHS)::B = (order << :rhs)
- EOC
+ mod.module_eval <<-EOC
+ ConstantSpecsRHS::B = begin
+ module ConstantSpecsRHS; end
- mod::ConstantSpecsRHS::B.should == [:lhs, :rhs]
- end
- end
-
- ruby_version_is ""..."3.2" do
- it "evaluates the right hand side before evaluating a constant path" do
- mod = Module.new
-
- mod.module_eval <<-EOC
- ConstantSpecsRHS::B = begin
- module ConstantSpecsRHS; end
-
- "hello"
- end
- EOC
+ "hello"
+ end
+ EOC
- mod::ConstantSpecsRHS::B.should == 'hello'
- end
+ mod::ConstantSpecsRHS::B.should == 'hello'
end
end
@@ -170,32 +154,34 @@ describe "Literal (A::X) constant resolution" do
-> { ConstantSpecs::ParentA::CS_CONSTX }.should raise_error(NameError)
end
- it "uses the module or class #name to craft the error message" do
- mod = Module.new do
- def self.name
- "ModuleName"
- end
+ ruby_version_is "3.0" do
+ it "uses the module or class #name to craft the error message" do
+ mod = Module.new do
+ def self.name
+ "ModuleName"
+ end
- def self.inspect
- "<unusable info>"
+ def self.inspect
+ "<unusable info>"
+ end
end
+
+ -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant ModuleName::DOES_NOT_EXIST/)
end
- -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant ModuleName::DOES_NOT_EXIST/)
- end
+ it "uses the module or class #inspect to craft the error message if they are anonymous" do
+ mod = Module.new do
+ def self.name
+ nil
+ end
- it "uses the module or class #inspect to craft the error message if they are anonymous" do
- mod = Module.new do
- def self.name
- nil
+ def self.inspect
+ "<unusable info>"
+ end
end
- def self.inspect
- "<unusable info>"
- end
+ -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant <unusable info>::DOES_NOT_EXIST/)
end
-
- -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant <unusable info>::DOES_NOT_EXIST/)
end
it "sends #const_missing to the original class or module scope" do
@@ -732,17 +718,3 @@ describe 'Allowed characters' do
eval("mod::ἍBB").should == 1
end
end
-
-describe 'Assignment' do
- context 'dynamic assignment' do
- it 'raises SyntaxError' do
- -> do
- eval <<-CODE
- def test
- B = 1
- end
- CODE
- end.should raise_error(SyntaxError, /dynamic constant assignment/)
- end
- end
-end
diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb
index c8531343c0..6b0be19d90 100644
--- a/spec/ruby/language/def_spec.rb
+++ b/spec/ruby/language/def_spec.rb
@@ -197,15 +197,32 @@ describe "An instance method with a default argument" do
foo(2,3,3).should == [2,3,[3]]
end
- it "raises a SyntaxError when there is an existing method with the same name as the local variable" do
- def bar
- 1
+ ruby_version_is ''...'2.7' do
+ it "warns and uses a nil value when there is an existing local method with same name" do
+ def bar
+ 1
+ end
+ -> {
+ eval "def foo(bar = bar)
+ bar
+ end"
+ }.should complain(/circular argument reference/)
+ foo.should == nil
+ foo(2).should == 2
+ end
+ end
+
+ ruby_version_is '2.7' do
+ it "raises a syntaxError an existing method with the same name as the local variable" do
+ def bar
+ 1
+ end
+ -> {
+ eval "def foo(bar = bar)
+ bar
+ end"
+ }.should raise_error(SyntaxError)
end
- -> {
- eval "def foo(bar = bar)
- bar
- end"
- }.should raise_error(SyntaxError)
end
it "calls a method with the same name as the local when explicitly using ()" do
diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb
index b84fe9394a..38345c3727 100644
--- a/spec/ruby/language/defined_spec.rb
+++ b/spec/ruby/language/defined_spec.rb
@@ -48,20 +48,6 @@ describe "The defined? keyword for literals" do
end
describe "The defined? keyword when called with a method name" do
- before :each do
- ScratchPad.clear
- end
-
- it "does not call the method" do
- defined?(DefinedSpecs.side_effects).should == "method"
- ScratchPad.recorded.should != :defined_specs_side_effects
- end
-
- it "does not execute the arguments" do
- defined?(DefinedSpecs.any_args(DefinedSpecs.side_effects)).should == "method"
- ScratchPad.recorded.should != :defined_specs_side_effects
- end
-
describe "without a receiver" do
it "returns 'method' if the method is defined" do
ret = defined?(puts)
@@ -180,32 +166,6 @@ describe "The defined? keyword when called with a method name" do
ScratchPad.recorded.should == :defined_specs_fixnum_method
end
end
-
- describe "having a throw in the receiver" do
- it "escapes defined? and performs the throw semantics as normal" do
- defined_returned = false
- catch(:out) {
- # NOTE: defined? behaves differently if it is called in a void context, see below
- defined?(throw(:out, 42).foo).should == :unreachable
- defined_returned = true
- }.should == 42
- defined_returned.should == false
- end
- end
-
- describe "in a void context" do
- it "does not execute the receiver" do
- ScratchPad.record :not_executed
- defined?(DefinedSpecs.side_effects / 2)
- ScratchPad.recorded.should == :not_executed
- end
-
- it "warns about the void context when parsing it" do
- -> {
- eval "defined?(DefinedSpecs.side_effects / 2); 42"
- }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true)
- end
- end
end
describe "The defined? keyword for an expression" do
diff --git a/spec/ruby/language/delegation_spec.rb b/spec/ruby/language/delegation_spec.rb
index 020787aff6..8e4183cbcc 100644
--- a/spec/ruby/language/delegation_spec.rb
+++ b/spec/ruby/language/delegation_spec.rb
@@ -1,63 +1,68 @@
require_relative '../spec_helper'
require_relative 'fixtures/delegation'
-describe "delegation with def(...)" do
- it "delegates rest and kwargs" do
- a = Class.new(DelegationSpecs::Target)
- a.class_eval(<<-RUBY)
- def delegate(...)
- target(...)
- end
- RUBY
-
- a.new.delegate(1, b: 2).should == [[1], {b: 2}]
- end
-
- it "delegates block" do
- a = Class.new(DelegationSpecs::Target)
- a.class_eval(<<-RUBY)
- def delegate_block(...)
- target_block(...)
- end
- RUBY
+ruby_version_is "2.7" do
+ describe "delegation with def(...)" do
+ it "delegates rest and kwargs" do
+ a = Class.new(DelegationSpecs::Target)
+ a.class_eval(<<-RUBY)
+ def delegate(...)
+ target(...)
+ end
+ RUBY
- a.new.delegate_block(1, b: 2) { |x| x }.should == [{b: 2}, [1]]
- end
+ a.new.delegate(1, b: 2).should == [[1], {b: 2}]
+ end
- it "parses as open endless Range when brackets are omitted" do
- a = Class.new(DelegationSpecs::Target)
- suppress_warning do
+ it "delegates block" do
+ a = Class.new(DelegationSpecs::Target)
a.class_eval(<<-RUBY)
- def delegate(...)
- target ...
+ def delegate_block(...)
+ target_block(...)
end
- RUBY
- end
+ RUBY
+
+ a.new.delegate_block(1, b: 2) { |x| x }.should == [{b: 2}, [1]]
+ end
- a.new.delegate(1, b: 2).should == Range.new([[], {}], nil, true)
+ it "parses as open endless Range when brackets are omitted" do
+ a = Class.new(DelegationSpecs::Target)
+ suppress_warning do
+ a.class_eval(<<-RUBY)
+ def delegate(...)
+ target ...
+ end
+ RUBY
+ end
+
+ a.new.delegate(1, b: 2).should == Range.new([[], {}], nil, true)
+ end
end
end
-describe "delegation with def(x, ...)" do
- it "delegates rest and kwargs" do
- a = Class.new(DelegationSpecs::Target)
- a.class_eval(<<-RUBY)
- def delegate(x, ...)
- target(...)
- end
- RUBY
+ruby_version_is "2.7.3" do
+ describe "delegation with def(x, ...)" do
+ it "delegates rest and kwargs" do
+ a = Class.new(DelegationSpecs::Target)
+ a.class_eval(<<-RUBY)
+ def delegate(x, ...)
+ target(...)
+ end
+ RUBY
- a.new.delegate(0, 1, b: 2).should == [[1], {b: 2}]
- end
+ a.new.delegate(0, 1, b: 2).should == [[1], {b: 2}]
+ end
+
+ it "delegates block" do
+ a = Class.new(DelegationSpecs::Target)
+ a.class_eval(<<-RUBY)
+ def delegate_block(x, ...)
+ target_block(...)
+ end
+ RUBY
- it "delegates block" do
- a = Class.new(DelegationSpecs::Target)
- a.class_eval(<<-RUBY)
- def delegate_block(x, ...)
- target_block(...)
- end
- RUBY
+ a.new.delegate_block(0, 1, b: 2) { |x| x }.should == [{b: 2}, [1]]
+ end
- a.new.delegate_block(0, 1, b: 2) { |x| x }.should == [{b: 2}, [1]]
end
end
diff --git a/spec/ruby/language/file_spec.rb b/spec/ruby/language/file_spec.rb
index 59563d9642..729dee1008 100644
--- a/spec/ruby/language/file_spec.rb
+++ b/spec/ruby/language/file_spec.rb
@@ -7,23 +7,23 @@ describe "The __FILE__ pseudo-variable" do
-> { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
end
- ruby_version_is ""..."3.3" do
- it "equals (eval) inside an eval" do
- eval("__FILE__").should == "(eval)"
- end
+ it "equals (eval) inside an eval" do
+ eval("__FILE__").should == "(eval)"
end
+end
- ruby_version_is "3.3" do
- it "equals (eval at __FILE__:__LINE__) inside an eval" do
- eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
- end
- end
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :require, CodeLoadingSpecs::Method.new
end
-describe "The __FILE__ pseudo-variable with require" do
+describe "The __FILE__ pseudo-variable" do
it_behaves_like :language___FILE__, :require, Kernel
end
-describe "The __FILE__ pseudo-variable with load" do
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :load, CodeLoadingSpecs::Method.new
+end
+
+describe "The __FILE__ pseudo-variable" do
it_behaves_like :language___FILE__, :load, Kernel
end
diff --git a/spec/ruby/language/fixtures/defined.rb b/spec/ruby/language/fixtures/defined.rb
index a9552619bf..8b6004c19f 100644
--- a/spec/ruby/language/fixtures/defined.rb
+++ b/spec/ruby/language/fixtures/defined.rb
@@ -19,9 +19,6 @@ module DefinedSpecs
DefinedSpecs
end
- def self.any_args(*)
- end
-
class Basic
A = 42
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb b/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb
index f72a32e879..d0558a2251 100644
--- a/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb
Binary files differ
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb b/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb
index cccc5969bd..a4d655ad02 100644
--- a/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb
@@ -1,3 +1,3 @@
# frozen_string_literal: true
-p "abc".equal?("abc")
+p "abc".object_id == "abc".object_id
diff --git a/spec/ruby/language/fixtures/super.rb b/spec/ruby/language/fixtures/super.rb
index 94a2a91be0..218f1e4970 100644
--- a/spec/ruby/language/fixtures/super.rb
+++ b/spec/ruby/language/fixtures/super.rb
@@ -556,20 +556,6 @@ module SuperSpecs
end
end
- module ZSuperInBlock
- class A
- def m(arg:)
- arg
- end
- end
-
- class B < A
- def m(arg:)
- proc { super }.call
- end
- end
- end
-
module Keywords
class Arguments
def foo(**args)
diff --git a/spec/ruby/language/fixtures/variables.rb b/spec/ruby/language/fixtures/variables.rb
index 527caa7a78..07265dbb2b 100644
--- a/spec/ruby/language/fixtures/variables.rb
+++ b/spec/ruby/language/fixtures/variables.rb
@@ -82,76 +82,4 @@ module VariablesSpecs
def self.false
false
end
-
- class EvalOrder
- attr_reader :order
-
- def initialize
- @order = []
- end
-
- def reset
- @order = []
- end
-
- def foo
- self << "foo"
- FooClass.new(self)
- end
-
- def bar
- self << "bar"
- BarClass.new(self)
- end
-
- def a
- self << "a"
- end
-
- def b
- self << "b"
- end
-
- def node
- self << "node"
-
- node = Node.new
- node.left = Node.new
- node.left.right = Node.new
-
- node
- end
-
- def <<(value)
- order << value
- end
-
- class FooClass
- attr_reader :evaluator
-
- def initialize(evaluator)
- @evaluator = evaluator
- end
-
- def []=(_index, _value)
- evaluator << "foo[]="
- end
- end
-
- class BarClass
- attr_reader :evaluator
-
- def initialize(evaluator)
- @evaluator = evaluator
- end
-
- def baz=(_value)
- evaluator << "bar.baz="
- end
- end
-
- class Node
- attr_accessor :left, :right
- end
- end
end
diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb
index 6ac382c42c..2f8b97199a 100644
--- a/spec/ruby/language/hash_spec.rb
+++ b/spec/ruby/language/hash_spec.rb
@@ -127,24 +127,11 @@ describe "Hash literal" do
{a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
end
- it "expands an '**{}' or '**obj' element with the last key/value pair taking precedence" do
+ it "expands an '**{}' element with the last key/value pair taking precedence" do
-> {
@h = eval "{a: 1, **{a: 2, b: 3, c: 1}, c: 3}"
}.should complain(/key :a is duplicated|duplicated key/)
@h.should == {a: 2, b: 3, c: 3}
-
- -> {
- h = {a: 2, b: 3, c: 1}
- @h = eval "{a: 1, **h, c: 3}"
- }.should_not complain
- @h.should == {a: 2, b: 3, c: 3}
- end
-
- it "expands an '**{}' and warns when finding an additional duplicate key afterwards" do
- -> {
- @h = eval "{d: 1, **{a: 2, b: 3, c: 1}, c: 3}"
- }.should complain(/key :c is duplicated|duplicated key/)
- @h.should == {a: 2, b: 3, c: 3, d: 1}
end
it "merges multiple nested '**obj' in Hash literals" do
@@ -161,9 +148,18 @@ describe "Hash literal" do
{a: 1, **obj, c: 3}.should == {a:1, b: 2, c: 3, d: 4}
end
- it "allows splatted elements keys that are not symbols" do
- h = {1 => 2, b: 3}
- {a: 1, **h}.should == {a: 1, 1 => 2, b: 3}
+ ruby_version_is ""..."2.7" do
+ it "raises a TypeError if any splatted elements keys are not symbols" do
+ h = {1 => 2, b: 3}
+ -> { {a: 1, **h} }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.7" do
+ it "allows splatted elements keys that are not symbols" do
+ h = {1 => 2, b: 3}
+ {a: 1, **h}.should == {a: 1, 1 => 2, b: 3}
+ end
end
it "raises a TypeError if #to_hash does not return a Hash" do
@@ -190,22 +186,6 @@ describe "Hash literal" do
utf8_hash.keys.first.should == usascii_hash.keys.first
usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
end
-
- it "raises an EncodingError at parse time when Symbol key with invalid bytes" do
- ScratchPad.record []
- -> {
- eval 'ScratchPad << 1; {:"\xC3" => 1}'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
- ScratchPad.recorded.should == []
- end
-
- it "raises an EncodingError at parse time when Symbol key with invalid bytes and 'key: value' syntax used" do
- ScratchPad.record []
- -> {
- eval 'ScratchPad << 1; {"\xC3": 1}'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
- ScratchPad.recorded.should == []
- end
end
describe "The ** operator" do
@@ -220,8 +200,8 @@ describe "The ** operator" do
h.should == { one: 1, two: 2 }
end
- ruby_bug "#20012", ""..."3.3" do
- it "makes a copy when calling a method taking a positional Hash" do
+ ruby_version_is ""..."3.0" do
+ it "makes a caller-side copy when calling a method taking a positional Hash" do
def m(h)
h.delete(:one); h
end
@@ -233,6 +213,19 @@ describe "The ** operator" do
end
end
+ ruby_version_is "3.0" do
+ it "does not copy when calling a method taking a positional Hash" do
+ def m(h)
+ h.delete(:one); h
+ end
+
+ h = { one: 1, two: 2 }
+ m(**h).should == { two: 2 }
+ m(**h).should.equal?(h)
+ h.should == { two: 2 }
+ end
+ end
+
ruby_version_is "3.1" do
describe "hash with omitted value" do
it "accepts short notation 'key' for 'key: value' syntax" do
diff --git a/spec/ruby/language/heredoc_spec.rb b/spec/ruby/language/heredoc_spec.rb
index b3b160fd11..61a27ad8e0 100644
--- a/spec/ruby/language/heredoc_spec.rb
+++ b/spec/ruby/language/heredoc_spec.rb
@@ -59,10 +59,20 @@ HERE
s.encoding.should == Encoding::US_ASCII
end
- it 'raises SyntaxError if quoted HEREDOC identifier is ending not on same line' do
- -> {
- eval %{<<"HERE\n"\nraises syntax error\nHERE}
- }.should raise_error(SyntaxError)
+ ruby_version_is "2.7" do
+ it 'raises SyntaxError if quoted HEREDOC identifier is ending not on same line' do
+ -> {
+ eval %{<<"HERE\n"\nraises syntax error\nHERE}
+ }.should raise_error(SyntaxError)
+ end
+ end
+
+ ruby_version_is ""..."2.7" do
+ it 'prints a warning if quoted HEREDOC identifier is ending not on same line' do
+ -> {
+ eval %{<<"HERE\n"\nit warns\nHERE}
+ }.should complain(/here document identifier ends with a newline/)
+ end
end
it "allows HEREDOC with <<~'identifier', allowing to indent identifier and content" do
diff --git a/spec/ruby/language/if_spec.rb b/spec/ruby/language/if_spec.rb
index a5da696000..d1d95c1607 100644
--- a/spec/ruby/language/if_spec.rb
+++ b/spec/ruby/language/if_spec.rb
@@ -306,49 +306,6 @@ describe "The if expression" do
ScratchPad.recorded.should == [4, 5, 4, 5]
end
end
-
- describe "when a branch syntactically does not return a value" do
- it "raises SyntaxError if both do not return a value" do
- -> {
- eval <<~RUBY
- def m
- a = if rand
- return
- else
- return
- end
- a
- end
- RUBY
- }.should raise_error(SyntaxError, /void value expression/)
- end
-
- it "does not raise SyntaxError if one branch returns a value" do
- eval(<<~RUBY).should == 1
- def m
- a = if false # using false to make it clear that's not checked for
- 42
- else
- return 1
- end
- a
- end
- m
- RUBY
-
- eval(<<~RUBY).should == 1
- def m
- a = if true # using true to make it clear that's not checked for
- return 1
- else
- 42
- end
- a
- end
- m
- RUBY
- end
- end
end
describe "The postfix if form" do
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
deleted file mode 100644
index ffb5b1fab0..0000000000
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ /dev/null
@@ -1,398 +0,0 @@
-require_relative '../spec_helper'
-
-describe "Keyword arguments" do
- def target(*args, **kwargs)
- [args, kwargs]
- end
-
- it "are separated from positional arguments" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
-
- it "when the receiving method has not keyword parameters it treats kwargs as positional" do
- def m(*a)
- a
- end
-
- m(a: 1).should == [{a: 1}]
- m({a: 1}).should == [{a: 1}]
- end
-
- context "empty kwargs are treated as if they were not passed" do
- it "when calling a method" do
- def m(*a)
- a
- end
-
- empty = {}
- m(**empty).should == []
- m(empty).should == [{}]
- end
-
- it "when yielding to a block" do
- def y(*args, **kwargs)
- yield(*args, **kwargs)
- end
-
- empty = {}
- y(**empty) { |*a| a }.should == []
- y(empty) { |*a| a }.should == [{}]
- end
- end
-
- it "extra keywords are not allowed without **kwrest" do
- def m(*a, kw:)
- a
- end
-
- m(kw: 1).should == []
- -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
- -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
- -> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
- end
-
- it "raises ArgumentError exception when required keyword argument is not passed" do
- def m(a:, b:, c:)
- [a, b, c]
- end
-
- -> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
- -> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
- end
-
- it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
- def m(a:)
- a
- end
-
- -> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
- end
-
- it "handle * and ** at the same call site" do
- def m(*a)
- a
- end
-
- m(*[], **{}).should == []
- m(*[], 42, **{}).should == [42]
- end
-
- context "**" do
- ruby_version_is "3.3" do
- it "copies a non-empty Hash for a method taking (*args)" do
- def m(*args)
- args[0]
- end
-
- h = {a: 1}
- m(**h).should_not.equal?(h)
- h.should == {a: 1}
- end
- end
-
- it "copies the given Hash for a method taking (**kwargs)" do
- def m(**kw)
- kw
- end
-
- empty = {}
- m(**empty).should == empty
- m(**empty).should_not.equal?(empty)
-
- h = {a: 1}
- m(**h).should == h
- m(**h).should_not.equal?(h)
- end
- end
-
- context "delegation" do
- it "works with (*args, **kwargs)" do
- def m(*args, **kwargs)
- target(*args, **kwargs)
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
-
- it "works with proc { |*args, **kwargs| }" do
- m = proc do |*args, **kwargs|
- target(*args, **kwargs)
- end
-
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
-
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
-
- # no autosplatting for |*args, **kwargs|
- m.([1, 2]).should == [[[1, 2]], {}]
- end
-
- it "works with -> (*args, **kwargs) {}" do
- m = -> *args, **kwargs do
- target(*args, **kwargs)
- end
-
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
-
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
- end
-
- it "works with (...)" do
- instance_eval <<~DEF
- def m(...)
- target(...)
- end
- DEF
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
-
- it "works with call(*ruby2_keyword_args)" do
- class << self
- ruby2_keywords def m(*args)
- target(*args)
- end
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
-
- kw = {a: 1}
-
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
-
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
-
- it "works with super(*ruby2_keyword_args)" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
- end
-
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super(*args)
- end
- end
-
- obj = child.new
-
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
-
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
-
- kw = {a: 1}
-
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
-
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
-
- it "works with zsuper" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
- end
-
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super
- end
- end
-
- obj = child.new
-
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
-
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
-
- kw = {a: 1}
-
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
-
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
-
- it "works with yield(*ruby2_keyword_args)" do
- class << self
- def y(args)
- yield(*args)
- end
-
- ruby2_keywords def m(*outer_args)
- y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
- end
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
-
- kw = {a: 1}
-
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
-
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
-
- it "does not work with (*args)" do
- class << self
- def m(*args)
- target(*args)
- end
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[{a: 1}], {}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
-
- ruby_version_is "3.1" do
- describe "omitted values" do
- it "accepts short notation 'key' for 'key: value' syntax" do
- def m(a:, b:)
- [a, b]
- end
-
- a = 1
- b = 2
-
- eval('m(a:, b:).should == [1, 2]')
- end
- end
- end
-
- ruby_version_is "3.2" do
- it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
-
- ruby2_keywords def m(*args)
- n(*args)
- end
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[{a: 1}], {}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
- end
-
- ruby_version_is ""..."3.2" do
- # https://bugs.ruby-lang.org/issues/18625
- it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
-
- ruby2_keywords def m(*args)
- n(*args)
- end
- end
-
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
-
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
-
- kw = {a: 1}
-
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
-
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
- end
- end
-end
diff --git a/spec/ruby/language/lambda_spec.rb b/spec/ruby/language/lambda_spec.rb
index 3ab3569ebe..6393fb5c47 100644
--- a/spec/ruby/language/lambda_spec.rb
+++ b/spec/ruby/language/lambda_spec.rb
@@ -177,16 +177,34 @@ describe "A lambda literal -> () { }" do
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
end
- evaluate <<-ruby do
- @a = -> (*, **k) { k }
- ruby
+ ruby_version_is ''...'3.0' do
+ evaluate <<-ruby do
+ @a = -> (*, **k) { k }
+ ruby
+
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ suppress_keyword_warning do
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ @a.(h).should == {a: 1}
+ end
+ end
+ end
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ ruby_version_is '3.0' do
+ evaluate <<-ruby do
+ @a = -> (*, **k) { k }
+ ruby
- h = mock("keyword splat")
- h.should_not_receive(:to_hash)
- @a.(h).should == {}
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ h = mock("keyword splat")
+ h.should_not_receive(:to_hash)
+ @a.(h).should == {}
+ end
end
evaluate <<-ruby do
@@ -263,18 +281,38 @@ describe "A lambda literal -> () { }" do
end
describe "with circular optional argument reference" do
- it "raises a SyntaxError if using an existing local with the same name as the argument" do
- a = 1
- -> {
- @proc = eval "-> (a=a) { a }"
- }.should raise_error(SyntaxError)
+ ruby_version_is ''...'2.7' do
+ it "warns and uses a nil value when there is an existing local variable with same name" do
+ a = 1
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "warns and uses a nil value when there is an existing method with same name" do
+ def a; 1; end
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
end
- it "raises a SyntaxError if there is an existing method with the same name as the argument" do
- def a; 1; end
- -> {
- @proc = eval "-> (a=a) { a }"
- }.should raise_error(SyntaxError)
+ ruby_version_is '2.7' do
+ it "raises a SyntaxError if using an existing local with the same name as the argument" do
+ a = 1
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should raise_error(SyntaxError)
+ end
+
+ it "raises a SyntaxError if there is an existing method with the same name as the argument" do
+ def a; 1; end
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should raise_error(SyntaxError)
+ end
end
it "calls an existing method with the same name as the argument if explicitly using ()" do
@@ -322,12 +360,26 @@ describe "A lambda expression 'lambda { ... }'" do
def meth; lambda; end
end
- it "raises ArgumentError" do
- implicit_lambda = nil
- suppress_warning do
+ ruby_version_is ""..."2.7" do
+ it "can be created" do
+ implicit_lambda = nil
-> {
- meth { 1 }
- }.should raise_error(ArgumentError, /tried to create Proc object without a block/)
+ implicit_lambda = meth { 1 }
+ }.should complain(/tried to create Proc object without a block/)
+
+ implicit_lambda.lambda?.should be_true
+ implicit_lambda.call.should == 1
+ end
+ end
+
+ ruby_version_is "2.7" do
+ it "raises ArgumentError" do
+ implicit_lambda = nil
+ suppress_warning do
+ -> {
+ meth { 1 }
+ }.should raise_error(ArgumentError, /tried to create Proc object without a block/)
+ end
end
end
end
@@ -496,16 +548,34 @@ describe "A lambda expression 'lambda { ... }'" do
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
end
- evaluate <<-ruby do
- @a = lambda { |*, **k| k }
- ruby
+ ruby_version_is ''...'3.0' do
+ evaluate <<-ruby do
+ @a = lambda { |*, **k| k }
+ ruby
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ suppress_keyword_warning do
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ @a.(h).should == {a: 1}
+ end
+ end
+ end
+
+ ruby_version_is '3.0' do
+ evaluate <<-ruby do
+ @a = lambda { |*, **k| k }
+ ruby
- h = mock("keyword splat")
- h.should_not_receive(:to_hash)
- @a.(h).should == {}
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ h = mock("keyword splat")
+ h.should_not_receive(:to_hash)
+ @a.(h).should == {}
+ end
end
evaluate <<-ruby do
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
index 5f42c52341..0a5bb99d0b 100644
--- a/spec/ruby/language/method_spec.rb
+++ b/spec/ruby/language/method_spec.rb
@@ -572,13 +572,6 @@ describe "A method" do
end
evaluate <<-ruby do
- def m(a:, **kw) [a, kw] end
- ruby
-
- -> { m(b: 1) }.should raise_error(ArgumentError)
- end
-
- evaluate <<-ruby do
def m(a: 1) a end
ruby
@@ -609,11 +602,13 @@ describe "A method" do
-> { m(2) }.should raise_error(ArgumentError)
end
- evaluate <<-ruby do
- def m(**k); k end;
- ruby
+ ruby_version_is "2.7" do
+ evaluate <<-ruby do
+ def m(**k); k end;
+ ruby
- m("a" => 1).should == { "a" => 1 }
+ m("a" => 1).should == { "a" => 1 }
+ end
end
evaluate <<-ruby do
@@ -749,31 +744,68 @@ describe "A method" do
end
end
- evaluate <<-ruby do
- def m(a, b: 1) [a, b] end
- ruby
+ ruby_version_is ""..."3.0" do
+ evaluate <<-ruby do
+ def m(a, b: 1) [a, b] end
+ ruby
- m(2).should == [2, 1]
- m(1, b: 2).should == [1, 2]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
+ m(2).should == [2, 1]
+ m(1, b: 2).should == [1, 2]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, 1]
+ end
+ end
- evaluate <<-ruby do
- def m(a, **) a end
- ruby
+ evaluate <<-ruby do
+ def m(a, **) a end
+ ruby
- m(1).should == 1
- m(1, a: 2, b: 3).should == 1
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ m(1).should == 1
+ m(1, a: 2, b: 3).should == 1
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == {"a" => 1, b: 2}
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(a, **k) [a, k] end
+ ruby
+
+ m(1).should == [1, {}]
+ m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, {}]
+ end
+ end
end
- evaluate <<-ruby do
- def m(a, **k) [a, k] end
- ruby
+ ruby_version_is "3.0" do
+ evaluate <<-ruby do
+ def m(a, b: 1) [a, b] end
+ ruby
- m(1).should == [1, {}]
- m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ m(2).should == [2, 1]
+ m(1, b: 2).should == [1, 2]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a, **) a end
+ ruby
+
+ m(1).should == 1
+ m(1, a: 2, b: 3).should == 1
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a, **k) [a, k] end
+ ruby
+
+ m(1).should == [1, {}]
+ m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
end
evaluate <<-ruby do
@@ -884,32 +916,72 @@ describe "A method" do
result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8]
end
- evaluate <<-ruby do
- def m(a=1, b:) [a, b] end
- ruby
+ ruby_version_is ""..."3.0" do
+ evaluate <<-ruby do
+ def m(a=1, b:) [a, b] end
+ ruby
+
+ m(b: 2).should == [1, 2]
+ m(2, b: 1).should == [2, 1]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [{"a" => 1}, 2]
+ end
+ end
- m(b: 2).should == [1, 2]
- m(2, b: 1).should == [2, 1]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ evaluate <<-ruby do
+ def m(a=1, b: 2) [a, b] end
+ ruby
+
+ m().should == [1, 2]
+ m(2).should == [2, 2]
+ m(b: 3).should == [1, 3]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [{"a" => 1}, 2]
+ end
+ end
end
- evaluate <<-ruby do
- def m(a=1, b: 2) [a, b] end
- ruby
+ ruby_version_is "3.0" do
+ evaluate <<-ruby do
+ def m(a=1, b:) [a, b] end
+ ruby
- m().should == [1, 2]
- m(2).should == [2, 2]
- m(b: 3).should == [1, 3]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ m(b: 2).should == [1, 2]
+ m(2, b: 1).should == [2, 1]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b: 2) [a, b] end
+ ruby
+
+ m().should == [1, 2]
+ m(2).should == [2, 2]
+ m(b: 3).should == [1, 3]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
end
- evaluate <<-ruby do
- def m(a=1, **) a end
- ruby
+ ruby_version_is ""..."2.7" do
+ evaluate <<-ruby do
+ def m(a=1, **) a end
+ ruby
- m().should == 1
- m(2, a: 1, b: 0).should == 2
- m("a" => 1, a: 2).should == 1
+ m().should == 1
+ m(2, a: 1, b: 0).should == 2
+ m("a" => 1, a: 2).should == {"a" => 1}
+ end
+ end
+
+ ruby_version_is "2.7" do
+ evaluate <<-ruby do
+ def m(a=1, **) a end
+ ruby
+
+ m().should == 1
+ m(2, a: 1, b: 0).should == 2
+ m("a" => 1, a: 2).should == 1
+ end
end
evaluate <<-ruby do
@@ -949,6 +1021,449 @@ describe "A method" do
m(1, 2, 3).should == [[1, 2], 3]
end
+ ruby_version_is ""..."2.7" do
+ evaluate <<-ruby do
+ def m(*, a:) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(1, 2, a: 3).should == 3
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b:) [a, b] end
+ ruby
+
+ m(b: 1).should == [[], 1]
+ m(1, 2, b: 3).should == [[1, 2], 3]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*, a: 1) a end
+ ruby
+
+ m().should == 1
+ m(1, 2).should == 1
+ m(a: 2).should == 2
+ m(1, a: 2).should == 2
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b: 1) [a, b] end
+ ruby
+
+ m().should == [[], 1]
+ m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+
+ a = mock("splat")
+ a.should_not_receive(:to_ary)
+ m(*a).should == [[a], 1]
+ end
+
+ evaluate <<-ruby do
+ def m(*, **) end
+ ruby
+
+ m().should be_nil
+ m(a: 1, b: 2).should be_nil
+ m(1, 2, 3, a: 4, b: 5).should be_nil
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ suppress_keyword_warning do
+ m(h).should be_nil
+ end
+
+ h = mock("keyword splat")
+ error = RuntimeError.new("error while converting to a hash")
+ h.should_receive(:to_hash).and_raise(error)
+ -> { m(h) }.should raise_error(error)
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **) a end
+ ruby
+
+ m().should == []
+ m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
+ m("a" => 1, a: 1).should == [{"a" => 1}]
+ m(1, **{a: 2}).should == [1]
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash)
+ -> { m(**h) }.should raise_error(TypeError)
+ end
+
+ evaluate <<-ruby do
+ def m(*, **k) k end
+ ruby
+
+ m().should == {}
+ m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ m("a" => 1, a: 1).should == {a: 1}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ m(h).should == {a: 1}
+ end
+
+ evaluate <<-ruby do
+ def m(a = nil, **k) [a, k] end
+ ruby
+
+ m().should == [nil, {}]
+ m("a" => 1).should == [{"a" => 1}, {}]
+ m(a: 1).should == [nil, {a: 1}]
+ m("a" => 1, a: 1).should == [{"a" => 1}, {a: 1}]
+ m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
+ m({a: 1}, {}).should == [{a: 1}, {}]
+
+ h = {"a" => 1, b: 2}
+ m(h).should == [{"a" => 1}, {b: 2}]
+ h.should == {"a" => 1, b: 2}
+
+ h = {"a" => 1}
+ m(h).first.should == h
+
+ h = {}
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ hh = {}
+ h = mock("keyword splat empty hash")
+ h.should_receive(:to_hash).and_return(hh)
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
+ m(h).should == [{"a" => 1}, {a: 2}]
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **k) [a, k] end
+ ruby
+
+ m().should == [[], {}]
+ m(1).should == [[1], {}]
+ m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
+ m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
+
+ m("a" => 1).should == [[{"a" => 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m("a" => 1, a: 1).should == [[{"a" => 1}], {a: 1}]
+ m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
+ m({a: 1}, {}).should == [[{a: 1}], {}]
+ m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
+
+ bo = BasicObject.new
+ def bo.to_a; [1, 2, 3]; end
+ def bo.to_hash; {:b => 2, :c => 3}; end
+
+ m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
+ end
+ end
+
+ ruby_version_is "2.7"...'3.0' do
+ evaluate <<-ruby do
+ def m(*, a:) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(1, 2, a: 3).should == 3
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b:) [a, b] end
+ ruby
+
+ m(b: 1).should == [[], 1]
+ m(1, 2, b: 3).should == [[1, 2], 3]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*, a: 1) a end
+ ruby
+
+ m().should == 1
+ m(1, 2).should == 1
+ m(a: 2).should == 2
+ m(1, a: 2).should == 2
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b: 1) [a, b] end
+ ruby
+
+ m().should == [[], 1]
+ m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+
+ a = mock("splat")
+ a.should_not_receive(:to_ary)
+ m(*a).should == [[a], 1]
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **) a end
+ ruby
+
+ m().should == []
+ m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
+ m("a" => 1, a: 1).should == []
+ m(1, **{a: 2}).should == [1]
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash)
+ -> { m(**h) }.should raise_error(TypeError)
+ end
+
+ evaluate <<-ruby do
+ def m(*, **k) k end
+ ruby
+
+ m().should == {}
+ m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ m("a" => 1, a: 1).should == {"a" => 1, a: 1}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ suppress_warning do
+ m(h).should == {a: 1}
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(a = nil, **k) [a, k] end
+ ruby
+
+ m().should == [nil, {}]
+ m("a" => 1).should == [nil, {"a" => 1}]
+ m(a: 1).should == [nil, {a: 1}]
+ m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
+ m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
+ suppress_warning do
+ m({a: 1}, {}).should == [{a: 1}, {}]
+
+ h = {"a" => 1, b: 2}
+ m(h).should == [{"a" => 1}, {b: 2}]
+ h.should == {"a" => 1, b: 2}
+
+ h = {"a" => 1}
+ m(h).first.should == h
+
+ h = {}
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ hh = {}
+ h = mock("keyword splat empty hash")
+ h.should_receive(:to_hash).and_return(hh)
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
+ m(h).should == [{"a" => 1}, {a: 2}]
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **k) [a, k] end
+ ruby
+
+ m().should == [[], {}]
+ m(1).should == [[1], {}]
+ m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
+ m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
+
+ m("a" => 1).should == [[], {"a" => 1}]
+ m(a: 1).should == [[], {a: 1}]
+ m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
+ m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
+ suppress_warning do
+ m({a: 1}, {}).should == [[{a: 1}], {}]
+ end
+ m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
+
+ bo = BasicObject.new
+ def bo.to_a; [1, 2, 3]; end
+ def bo.to_hash; {:b => 2, :c => 3}; end
+
+ m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
+ end
+
+ evaluate <<-ruby do
+ def m(*, a:) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(1, 2, a: 3).should == 3
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b:) [a, b] end
+ ruby
+
+ m(b: 1).should == [[], 1]
+ m(1, 2, b: 3).should == [[1, 2], 3]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*, a: 1) a end
+ ruby
+
+ m().should == 1
+ m(1, 2).should == 1
+ m(a: 2).should == 2
+ m(1, a: 2).should == 2
+ suppress_keyword_warning do
+ m("a" => 1, a: 2).should == 2
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b: 1) [a, b] end
+ ruby
+
+ m().should == [[], 1]
+ m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
+ suppress_keyword_warning do
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+
+ a = mock("splat")
+ a.should_not_receive(:to_ary)
+ m(*a).should == [[a], 1]
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **) a end
+ ruby
+
+ m().should == []
+ m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
+ m("a" => 1, a: 1).should == []
+ m(1, **{a: 2}).should == [1]
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash)
+ -> { m(**h) }.should raise_error(TypeError)
+ end
+
+ evaluate <<-ruby do
+ def m(*, **k) k end
+ ruby
+
+ m().should == {}
+ m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ m("a" => 1, a: 1).should == {"a" => 1, a: 1}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ suppress_keyword_warning do
+ m(h).should == {a: 1}
+ end
+ end
+
+ evaluate <<-ruby do
+ def m(a = nil, **k) [a, k] end
+ ruby
+
+ m().should == [nil, {}]
+ m("a" => 1).should == [nil, {"a" => 1}]
+ m(a: 1).should == [nil, {a: 1}]
+ m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
+ m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
+ suppress_keyword_warning do
+ m({a: 1}, {}).should == [{a: 1}, {}]
+ end
+
+ h = {"a" => 1, b: 2}
+ suppress_keyword_warning do
+ m(h).should == [{"a" => 1}, {b: 2}]
+ end
+ h.should == {"a" => 1, b: 2}
+
+ h = {"a" => 1}
+ m(h).first.should == h
+
+ h = {}
+ suppress_keyword_warning do
+ m(h).should == [nil, {}]
+ end
+
+ hh = {}
+ h = mock("keyword splat empty hash")
+ h.should_receive(:to_hash).and_return({a: 1})
+ suppress_keyword_warning do
+ m(h).should == [nil, {a: 1}]
+ end
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({"a" => 1})
+ m(h).should == [h, {}]
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **k) [a, k] end
+ ruby
+
+ m().should == [[], {}]
+ m(1).should == [[1], {}]
+ m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
+ m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
+
+ m("a" => 1).should == [[], {"a" => 1}]
+ m(a: 1).should == [[], {a: 1}]
+ m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
+ m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
+ suppress_keyword_warning do
+ m({a: 1}, {}).should == [[{a: 1}], {}]
+ end
+ m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
+
+ bo = BasicObject.new
+ def bo.to_a; [1, 2, 3]; end
+ def bo.to_hash; {:b => 2, :c => 3}; end
+
+ m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
+ end
+ end
+
evaluate <<-ruby do
def m(*, &b) b end
ruby
@@ -988,22 +1503,44 @@ describe "A method" do
end
end
- evaluate <<-ruby do
- def m(a:, **) a end
- ruby
+ ruby_version_is ''...'2.7' do
+ evaluate <<-ruby do
+ def m(a:, **) a end
+ ruby
- m(a: 1).should == 1
- m(a: 1, b: 2).should == 1
- m("a" => 1, a: 1, b: 2).should == 1
+ m(a: 1).should == 1
+ m(a: 1, b: 2).should == 1
+ -> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a:, **k) [a, k] end
+ ruby
+
+ m(a: 1).should == [1, {}]
+ m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}]
+ -> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
end
- evaluate <<-ruby do
- def m(a:, **k) [a, k] end
- ruby
+ ruby_version_is '2.7' do
+ evaluate <<-ruby do
+ def m(a:, **) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(a: 1, b: 2).should == 1
+ m("a" => 1, a: 1, b: 2).should == 1
+ end
- m(a: 1).should == [1, {}]
- m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}]
- m("a" => 1, a: 1, b: 2).should == [1, {"a" => 1, b: 2}]
+ evaluate <<-ruby do
+ def m(a:, **k) [a, k] end
+ ruby
+
+ m(a: 1).should == [1, {}]
+ m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}]
+ m("a" => 1, a: 1, b: 2).should == [1, {"a" => 1, b: 2}]
+ end
end
evaluate <<-ruby do
@@ -1100,66 +1637,125 @@ describe "A method" do
result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
end
- evaluate <<-ruby do
- def m(a, **nil); a end;
- ruby
+ ruby_version_is "2.7" do
+ evaluate <<-ruby do
+ def m(a, **nil); a end;
+ ruby
- m({a: 1}).should == {a: 1}
- m({"a" => 1}).should == {"a" => 1}
+ m({a: 1}).should == {a: 1}
+ m({"a" => 1}).should == {"a" => 1}
- -> { m(a: 1) }.should raise_error(ArgumentError, 'no keywords accepted')
- -> { m(**{a: 1}) }.should raise_error(ArgumentError, 'no keywords accepted')
- -> { m("a" => 1) }.should raise_error(ArgumentError, 'no keywords accepted')
+ -> { m(a: 1) }.should raise_error(ArgumentError)
+ -> { m(**{a: 1}) }.should raise_error(ArgumentError)
+ -> { m("a" => 1) }.should raise_error(ArgumentError)
+ end
end
- evaluate <<-ruby do
- def m(a, b = nil, c = nil, d, e: nil, **f)
- [a, b, c, d, e, f]
+ ruby_version_is ''...'3.0' do
+ evaluate <<-ruby do
+ def m(a, b = nil, c = nil, d, e: nil, **f)
+ [a, b, c, d, e, f]
+ end
+ ruby
+
+ result = m(1, 2)
+ result.should == [1, nil, nil, 2, nil, {}]
+
+ suppress_warning do
+ result = m(1, 2, {foo: :bar})
+ result.should == [1, nil, nil, 2, nil, {foo: :bar}]
end
- ruby
- result = m(1, 2)
- result.should == [1, nil, nil, 2, nil, {}]
+ result = m(1, {foo: :bar})
+ result.should == [1, nil, nil, {foo: :bar}, nil, {}]
+ end
+ end
+
+ ruby_version_is '3.0' do
+ evaluate <<-ruby do
+ def m(a, b = nil, c = nil, d, e: nil, **f)
+ [a, b, c, d, e, f]
+ end
+ ruby
+
+ result = m(1, 2)
+ result.should == [1, nil, nil, 2, nil, {}]
- result = m(1, 2, {foo: :bar})
- result.should == [1, 2, nil, {foo: :bar}, nil, {}]
+ result = m(1, 2, {foo: :bar})
+ result.should == [1, 2, nil, {foo: :bar}, nil, {}]
- result = m(1, {foo: :bar})
- result.should == [1, nil, nil, {foo: :bar}, nil, {}]
+ result = m(1, {foo: :bar})
+ result.should == [1, nil, nil, {foo: :bar}, nil, {}]
+ end
end
end
- context 'when passing an empty keyword splat to a method that does not accept keywords' do
- evaluate <<-ruby do
- def m(*a); a; end
- ruby
+ ruby_version_is '2.7' do
+ context 'when passing an empty keyword splat to a method that does not accept keywords' do
+ evaluate <<-ruby do
+ def m(*a); a; end
+ ruby
- h = {}
- m(**h).should == []
+ h = {}
+ m(**h).should == []
+ end
end
end
- context 'when passing an empty keyword splat to a method that does not accept keywords' do
- evaluate <<-ruby do
- def m(a); a; end
- ruby
- h = {}
+ ruby_version_is '2.7'...'3.0' do
+ context 'when passing an empty keyword splat to a method that does not accept keywords' do
+ evaluate <<-ruby do
+ def m(a); a; end
+ ruby
+ h = {}
- -> do
- m(**h).should == {}
- end.should raise_error(ArgumentError)
+ -> do
+ m(**h).should == {}
+ end.should complain(/warning: Passing the keyword argument as the last hash parameter is deprecated/)
+ end
end
end
- context "raises ArgumentError if passing hash as keyword arguments" do
- evaluate <<-ruby do
- def m(a: nil); a; end
- ruby
+ ruby_version_is ''...'3.0' do
+ context "assigns keyword arguments from a passed Hash without modifying it" do
+ evaluate <<-ruby do
+ def m(a: nil); a; end
+ ruby
+
+ options = {a: 1}.freeze
+ -> do
+ suppress_warning do
+ m(options).should == 1
+ end
+ end.should_not raise_error
+ options.should == {a: 1}
+ end
+ end
+ end
+
+ ruby_version_is '3.0' do
+ context 'when passing an empty keyword splat to a method that does not accept keywords' do
+ evaluate <<-ruby do
+ def m(a); a; end
+ ruby
+ h = {}
- options = {a: 1}.freeze
- -> do
- m(options)
- end.should raise_error(ArgumentError)
+ -> do
+ m(**h).should == {}
+ end.should raise_error(ArgumentError)
+ end
+ end
+
+ context "raises ArgumentError if passing hash as keyword arguments" do
+ evaluate <<-ruby do
+ def m(a: nil); a; end
+ ruby
+
+ options = {a: 1}.freeze
+ -> do
+ m(options)
+ end.should raise_error(ArgumentError)
+ end
end
end
@@ -1193,42 +1789,14 @@ describe "A method call with a space between method name and parentheses" do
end
end
- context "when a single argument is provided" do
- it "assigns a simple expression" do
- args = m (1)
- args.should == [1]
- end
-
- it "assigns an expression consisting of multiple statements" do
- args = m ((0; 1))
- args.should == [1]
- end
-
- it "assigns one single statement, without the need of parentheses" do
+ context "when a single argument provided" do
+ it "assigns it" do
args = m (1 == 1 ? true : false)
args.should == [true]
end
-
- ruby_version_is "3.3" do
- it "supports multiple statements" do
- eval("m (1; 2)").should == [2]
- end
- end
- end
-
- context "when multiple arguments are provided" do
- it "assigns simple expressions" do
- args = m (1), (2)
- args.should == [1, 2]
- end
-
- it "assigns expressions consisting of multiple statements" do
- args = m ((0; 1)), ((2; 3))
- args.should == [1, 3]
- end
end
- context "when the argument looks like an argument list" do
+ context "when 2+ arguments provided" do
it "raises a syntax error" do
-> {
eval("m (1, 2)")
@@ -1293,107 +1861,86 @@ describe "An array-dereference method ([])" do
end
end
-describe "An endless method definition" do
- context "without arguments" do
- evaluate <<-ruby do
- def m() = 42
- ruby
+ruby_version_is "3.0" do
+ describe "An endless method definition" do
+ context "without arguments" do
+ evaluate <<-ruby do
+ def m() = 42
+ ruby
- m.should == 42
+ m.should == 42
+ end
end
- context "without parenthesis" do
+ context "with arguments" do
evaluate <<-ruby do
- def m = 42
- ruby
+ def m(a, b) = a + b
+ ruby
- m.should == 42
+ m(1, 4).should == 5
end
end
- end
- context "with arguments" do
- evaluate <<-ruby do
- def m(a, b) = a + b
- ruby
+ context "with multiline body" do
+ evaluate <<-ruby do
+ def m(n) =
+ if n > 2
+ m(n - 2) + m(n - 1)
+ else
+ 1
+ end
+ ruby
- m(1, 4).should == 5
+ m(6).should == 8
+ end
end
- end
- context "with multiline body" do
- evaluate <<-ruby do
- def m(n) =
- if n > 2
- m(n - 2) + m(n - 1)
- else
- 1
+ context "with args forwarding" do
+ evaluate <<-ruby do
+ def mm(word, num:)
+ word * num
end
- ruby
- m(6).should == 8
+ def m(...) = mm(...) + mm(...)
+ ruby
+
+ m("meow", num: 2).should == "meow" * 4
+ end
end
end
- context "with args forwarding" do
- evaluate <<-ruby do
- def mm(word, num:)
- word * num
+ describe "Keyword arguments are now separated from positional arguments" do
+ context "when the method has only positional parameters" do
+ it "treats incoming keyword arguments as positional for compatibility" do
+ def foo(a, b, c, hsh)
+ hsh[:key]
end
- def m(...) = mm(...) + mm(...)
- ruby
-
- m("meow", num: 2).should == "meow" * 4
- end
- end
-end
-
-describe "Keyword arguments are now separated from positional arguments" do
- context "when the method has only positional parameters" do
- it "treats incoming keyword arguments as positional for compatibility" do
- def foo(a, b, c, hsh)
- hsh[:key]
+ foo(1, 2, 3, key: 42).should == 42
end
-
- foo(1, 2, 3, key: 42).should == 42
end
- end
- context "when the method takes a ** parameter" do
- it "captures the passed literal keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
- end
-
- foo(1, 2, 3, key: 42).should == 42
- end
+ context "when the method takes a ** parameter" do
+ it "captures the passed literal keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
+ end
- it "captures the passed ** keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
+ foo(1, 2, 3, key: 42).should == 42
end
- h = { key: 42 }
- foo(1, 2, 3, **h).should == 42
- end
+ it "captures the passed ** keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
+ end
- it "does not convert a positional Hash to keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
+ h = { key: 42 }
+ foo(1, 2, 3, **h).should == 42
end
- -> {
- foo(1, 2, 3, { key: 42 })
- }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
- end
- end
-
- context "when the method takes a key: parameter" do
- context "when it's called with a positional Hash and no **" do
- it "raises ArgumentError" do
- def foo(a, b, c, key: 1)
- key
+ it "does not convert a positional Hash to keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
end
-> {
@@ -1402,14 +1949,28 @@ describe "Keyword arguments are now separated from positional arguments" do
end
end
- context "when it's called with **" do
- it "captures the passed keyword arguments" do
- def foo(a, b, c, key: 1)
- key
+ context "when the method takes a key: parameter" do
+ context "when it's called with a positional Hash and no **" do
+ it "raises ArgumentError" do
+ def foo(a, b, c, key: 1)
+ key
+ end
+
+ -> {
+ foo(1, 2, 3, { key: 42 })
+ }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
end
+ end
- h = { key: 42 }
- foo(1, 2, 3, **h).should == 42
+ context "when it's called with **" do
+ it "captures the passed keyword arguments" do
+ def foo(a, b, c, key: 1)
+ key
+ end
+
+ h = { key: 42 }
+ foo(1, 2, 3, **h).should == 42
+ end
end
end
end
@@ -1454,14 +2015,4 @@ ruby_version_is "3.1" do
end
end
end
-
- describe "Inside 'endless' method definitions" do
- it "allows method calls without parenthesis" do
- eval <<-ruby
- def greet(person) = "Hi, ".concat person
- ruby
-
- greet("Homer").should == "Hi, Homer"
- end
- end
end
diff --git a/spec/ruby/language/module_spec.rb b/spec/ruby/language/module_spec.rb
index fffcf9c90d..1a5fcaf46f 100644
--- a/spec/ruby/language/module_spec.rb
+++ b/spec/ruby/language/module_spec.rb
@@ -28,18 +28,9 @@ describe "The module keyword" do
ModuleSpecs::Reopened.should be_true
end
- ruby_version_is '3.2' do
- it "does not reopen a module included in Object" do
- module IncludedModuleSpecs; Reopened = true; end
- ModuleSpecs::IncludedInObject::IncludedModuleSpecs.should_not == Object::IncludedModuleSpecs
- end
- end
-
- ruby_version_is ''...'3.2' do
- it "reopens a module included in Object" do
- module IncludedModuleSpecs; Reopened = true; end
- ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true
- end
+ it "reopens a module included in Object" do
+ module IncludedModuleSpecs; Reopened = true; end
+ ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true
end
it "raises a TypeError if the constant is a Class" do
@@ -78,10 +69,20 @@ describe "Assigning an anonymous module to a constant" do
mod.name.should == "ModuleSpecs_CS1"
end
- it "sets the name of a module scoped by an anonymous module" do
- a, b = Module.new, Module.new
- a::B = b
- b.name.should.end_with? '::B'
+ ruby_version_is ""..."3.0" do
+ it "does not set the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a::B = b
+ b.name.should be_nil
+ end
+ end
+
+ ruby_version_is "3.0" do
+ it "sets the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a::B = b
+ b.name.should.end_with? '::B'
+ end
end
it "sets the name of contained modules when assigning a toplevel anonymous module" do
diff --git a/spec/ruby/language/numbered_parameters_spec.rb b/spec/ruby/language/numbered_parameters_spec.rb
index 3a35cf1465..838822b2d6 100644
--- a/spec/ruby/language/numbered_parameters_spec.rb
+++ b/spec/ruby/language/numbered_parameters_spec.rb
@@ -1,104 +1,120 @@
require_relative '../spec_helper'
-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
+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 "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 "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 "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 "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 "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/m)
- 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 "cannot be overwritten with local variable" do
- -> {
- eval <<~CODE
- _1 = 0
- proc { _1 }.call("a").should == 0
- CODE
- }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
- 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/m)
+ end
- it "errors when numbered parameter is overwritten with local variable" do
- -> {
- eval("_1 = 0")
- }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
- end
+ ruby_version_is '2.7'...'3.0' do
+ 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 overwritten with local variable" do
+ -> {
+ eval("_1 = 0")
+ }.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
+ end
+ 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/)
+ ruby_version_is '3.0' do
+ it "cannot be overwritten with local variable" do
+ -> {
+ eval <<~CODE
+ _1 = 0
+ proc { _1 }.call("a").should == 0
+ CODE
+ }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
+ end
+
+ it "errors when numbered parameter is overwritten with local variable" do
+ -> {
+ eval("_1 = 0")
+ }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
+ end
+ end
- -> { eval("proc { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
- -> { eval("proc { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ 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("lambda { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
- -> { eval("lambda { |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("['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
+ -> { eval("lambda { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
+ -> { eval("lambda { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
- describe "assigning to a numbered parameter" do
- it "raises SyntaxError" do
- -> { eval("proc { _1 = 0 }") }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
+ -> { 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
- 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 "affects block parameters" do
- -> { _1 }.parameters.should == [[:req, :_1]]
- -> { _2 }.parameters.should == [[:req, :_1], [:req, :_2]]
-
- proc { _1 }.parameters.should == [[:opt, :_1]]
- proc { _2 }.parameters.should == [[:opt, :_1], [:opt, :_2]]
- end
+ describe "assigning to a numbered parameter" do
+ ruby_version_is '2.7'...'3.0' do
+ it "warns" do
+ -> { eval("proc { _1 = 0 }") }.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
+ end
+ end
+
+ ruby_version_is '3.0' do
+ it "raises SyntaxError" do
+ -> { eval("proc { _1 = 0 }") }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
+ end
+ end
+ end
- it "affects binding local variables" do
- -> { _1; binding.local_variables }.call("a").should == [:_1]
- -> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
- 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
+ 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/)
+ -> { 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 a8e023efb6..2d8e19c40a 100644
--- a/spec/ruby/language/numbers_spec.rb
+++ b/spec/ruby/language/numbers_spec.rb
@@ -53,7 +53,7 @@ describe "A number literal" do
eval('0.0174532925199432957r').should == Rational(174532925199432957, 10000000000000000000)
end
- it "can be a bignum literal with trailing 'r' to represent a Rational" do
+ 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)
end
diff --git a/spec/ruby/language/optional_assignments_spec.rb b/spec/ruby/language/optional_assignments_spec.rb
index 02461655d6..217dcab74b 100644
--- a/spec/ruby/language/optional_assignments_spec.rb
+++ b/spec/ruby/language/optional_assignments_spec.rb
@@ -300,44 +300,6 @@ describe 'Optional variable assignments' do
(@b[:k] ||= 12).should == 12
end
- it 'correctly handles a splatted argument for the index' do
- (@b[*[:k]] ||= 12).should == 12
- end
-
- it "evaluates the index precisely once" do
- ary = [:x, :y]
- @a[:x] = 15
- @a[ary.pop] ||= 25
- ary.should == [:x]
- @a.should == { x: 15, y: 25 }
- end
-
- it "evaluates the index arguments in the correct order" do
- ary = Class.new(Array) do
- def [](x, y)
- super(x + 3 * y)
- end
-
- def []=(x, y, value)
- super(x + 3 * y, value)
- end
- end.new
- ary[0, 0] = 1
- ary[1, 0] = 1
- ary[2, 0] = nil
- ary[3, 0] = 1
- ary[4, 0] = 1
- ary[5, 0] = 1
- ary[6, 0] = nil
-
- foo = [0, 2]
-
- ary[foo.pop, foo.pop] ||= 2
-
- ary[2, 0].should == 2
- ary[6, 0].should == nil
- end
-
it 'returns the assigned value, not the result of the []= method with +=' do
@b[:k] = 17
(@b[:k] += 12).should == 29
diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb
index 050a8a052d..e4abae9412 100644
--- a/spec/ruby/language/pattern_matching_spec.rb
+++ b/spec/ruby/language/pattern_matching_spec.rb
@@ -1,370 +1,196 @@
require_relative '../spec_helper'
-describe "Pattern matching" do
- # TODO: Remove excessive eval calls when Ruby 3 is the minimum version.
- # It is best to keep the eval's longer if other Ruby impls cannot parse pattern matching yet.
+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 :each do
- ScratchPad.record []
- end
-
- describe "can be standalone assoc operator that" do
- it "deconstructs value" do
- suppress_warning do
- eval(<<-RUBY).should == [0, 1]
- [0, 1] => [a, b]
- [a, b]
- RUBY
- end
+ before :each do
+ ScratchPad.record []
end
- it "deconstructs value and properly scopes variables" do
- suppress_warning do
- eval(<<-RUBY).should == [0, nil]
- a = nil
- eval(<<-PATTERN)
+ ruby_version_is "3.0" do
+ it "can be standalone assoc operator that deconstructs value" do
+ suppress_warning do
+ eval(<<-RUBY).should == [0, 1]
[0, 1] => [a, b]
- PATTERN
- [a, defined?(b)]
- RUBY
- end
- end
- end
-
- describe "find pattern" do
- it "captures preceding elements to the pattern" do
- eval(<<~RUBY).should == [0, 1]
- case [0, 1, 2, 3]
- in [*pre, 2, 3]
- pre
- else
- false
- end
- RUBY
- end
-
- it "captures following elements to the pattern" do
- eval(<<~RUBY).should == [2, 3]
- case [0, 1, 2, 3]
- in [0, 1, *post]
- post
- else
- false
+ [a, b]
+ RUBY
end
- RUBY
- end
-
- it "captures both preceding and following elements to the pattern" do
- eval(<<~RUBY).should == [[0, 1], [3, 4]]
- case [0, 1, 2, 3, 4]
- in [*pre, 2, *post]
- [pre, post]
- else
- false
- end
- RUBY
- end
-
- it "can capture the entirety of the pattern" do
- eval(<<~RUBY).should == [0, 1, 2, 3, 4]
- case [0, 1, 2, 3, 4]
- in [*everything]
- everything
- else
- false
- end
- RUBY
- end
-
- it "will match an empty Array-like structure" do
- eval(<<~RUBY).should == []
- case []
- in [*everything]
- everything
- else
- false
- end
- RUBY
- end
-
- it "can be nested" do
- eval(<<~RUBY).should == [[0, [2, 4, 6]], [[4, 16, 64]], 27]
- case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
- in [*pre, [*, 9, a], *post]
- [pre, post, a]
- else
- false
- end
- RUBY
- end
+ end
- it "can be nested with an array pattern" do
- eval(<<~RUBY).should == [[4, 16, 64]]
- case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
- in [_, _, [*, 9, *], *post]
- post
- else
- false
+ describe "find pattern" do
+ it "captures preceding elements to the pattern" do
+ eval(<<~RUBY).should == [0, 1]
+ case [0, 1, 2, 3]
+ in [*pre, 2, 3]
+ pre
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "captures following elements to the pattern" do
+ eval(<<~RUBY).should == [2, 3]
+ case [0, 1, 2, 3]
+ in [0, 1, *post]
+ post
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "captures both preceding and following elements to the pattern" do
+ eval(<<~RUBY).should == [[0, 1], [3, 4]]
+ case [0, 1, 2, 3, 4]
+ in [*pre, 2, *post]
+ [pre, post]
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "can capture the entirety of the pattern" do
+ eval(<<~RUBY).should == [0, 1, 2, 3, 4]
+ case [0, 1, 2, 3, 4]
+ in [*everything]
+ everything
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "will match an empty Array-like structure" do
+ eval(<<~RUBY).should == []
+ case []
+ in [*everything]
+ everything
+ else
+ false
+ end
+ RUBY
end
- RUBY
+ end
end
- it "can be nested within a hash pattern" do
- eval(<<~RUBY).should == [27]
- case {a: [3, 9, 27]}
- in {a: [*, 9, *post]}
- post
- else
- false
+ 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 "can nest hash and array patterns" do
- eval(<<~RUBY).should == [42, 2]
- case [0, {a: 42, b: [0, 1]}, {a: 42, b: [1, 2]}]
- in [*, {a:, b: [1, c]}, *]
- [a, c]
- else
- false
+ 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
- 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
-
- describe "warning" do
- before :each do
- @experimental, Warning[:experimental] = Warning[:experimental], true
- end
-
- after :each do
- Warning[:experimental] = @experimental
- end
- context 'when regular form' do
+ describe "warning" do
before :each do
- @src = 'case [0, 1]; in [a, b]; end'
+ @experimental, Warning[:experimental] = Warning[:experimental], true
end
- it "does not warn about pattern matching is experimental feature" do
- -> { eval @src }.should_not complain
- end
- end
-
- context 'when one-line form' do
- before :each do
- @src = '[0, 1] => [a, b]'
+ after :each do
+ Warning[:experimental] = @experimental
end
- ruby_version_is ""..."3.1" do
- it "warns about pattern matching is experimental feature" do
- -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
+ context 'when regular form' do
+ before :each do
+ @src = 'case [0, 1]; in [a, b]; end'
end
- end
- ruby_version_is "3.1" do
- it "does not warn about pattern matching is experimental feature" do
- -> { eval @src }.should_not complain
- end
- end
- end
- 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 []
+ ruby_version_is ""..."3.0" do
+ it "warns about pattern matching is experimental feature" do
+ -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
+ end
end
- RUBY
- }.should raise_error(SyntaxError, /syntax error, unexpected `in'|\(eval\):3: syntax error, unexpected keyword_in/)
- -> {
- eval <<~RUBY
- case []
- in []
- when 1 == 1
+ ruby_version_is "3.0" do
+ it "does not warn about pattern matching is experimental feature" do
+ -> { eval @src }.should_not complain
+ end
end
- RUBY
- }.should raise_error(SyntaxError, /syntax error, unexpected `when'|\(eval\):3: syntax error, unexpected keyword_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
+ context 'when one-line form' do
+ ruby_version_is '3.0' do
+ before :each do
+ @src = '[0, 1] => [a, b]'
+ end
- it "raises NoMatchingPatternError if no pattern matches and evaluates the expression only once" do
- evals = 0
- -> {
- eval <<~RUBY
- case (evals += 1; [0, 1])
- in [0]
- end
- RUBY
- }.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
- evals.should == 1
- end
+ ruby_version_is ""..."3.1" do
+ it "warns about pattern matching is experimental feature" do
+ -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
+ end
+ end
- it "does not allow calculation or method calls in a pattern" do
- -> {
- eval <<~RUBY
- case 0
- in 1 + 1
- true
+ ruby_version_is "3.1" do
+ it "does not warn about pattern matching is experimental feature" do
+ -> { eval @src }.should_not complain
+ end
+ end
end
- RUBY
- }.should raise_error(SyntaxError, /unexpected/)
- end
-
- it "evaluates the case expression once for multiple patterns, caching the result" do
- eval(<<~RUBY).should == true
- case (ScratchPad << :foo; 1)
- in 0
- false
- in 1
- true
end
- RUBY
-
- ScratchPad.recorded.should == [:foo]
- 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
+ it "binds variables" do
+ eval(<<~RUBY).should == 1
case [0, 1]
- in [a, 1] if a >= 0
- true
+ in [0, a]
+ a
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
+ 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'/)
- ScratchPad.recorded.should == []
+ -> {
+ eval <<~RUBY
+ case []
+ in []
+ when 1 == 1
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /syntax error, unexpected `when'/)
end
- it "takes guards into account when there are several matching patterns" do
+ it "checks patterns until the first matching" do
eval(<<~RUBY).should == :bar
- case 0
- in 0 if false
+ case [0, 1]
+ in [0]
:foo
- in 0 if true
+ in [0, 1]
:bar
+ in [0, 1]
+ :baz
end
RUBY
end
- it "executes else clause if no guarded pattern matches" do
+ it "executes else clause if no pattern matches" do
eval(<<~RUBY).should == false
- case 0
- in 0 if false
+ case [0, 1]
+ in [0]
true
else
false
@@ -372,1044 +198,1105 @@ describe "Pattern matching" do
RUBY
end
- it "raises NoMatchingPatternError if no guarded pattern matches and no else clause" do
+ it "raises NoMatchingPatternError if no pattern matches and no else clause" do
-> {
eval <<~RUBY
case [0, 1]
- in [0, 1] if false
+ in [0]
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
+ 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
- it "allows string literal with interpolation" do
- x = "x"
-
+ it "evaluates the case expression once for multiple patterns, caching the result" do
eval(<<~RUBY).should == true
- case "x"
- in "#{x + ""}"
+ case (ScratchPad << :foo; 1)
+ in 0
+ false
+ in 1
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
+ ScratchPad.recorded.should == [:foo]
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
+ describe "guards" do
+ it "supports if guard" do
+ eval(<<~RUBY).should == false
+ case 0
+ in 0 if false
+ true
+ else
+ false
+ end
+ RUBY
- it "allow using _ name to drop values" do
- eval(<<~RUBY).should == 0
- case [0, 1]
- in [a, _]
- a
- end
- RUBY
- end
+ eval(<<~RUBY).should == true
+ case 0
+ in 0 if true
+ true
+ else
+ false
+ end
+ RUBY
+ end
- it "supports using _ in a pattern several times" do
- eval(<<~RUBY).should == true
- case [0, 1, 2]
- in [0, _, _]
- true
- end
- RUBY
- end
+ it "supports unless guard" do
+ eval(<<~RUBY).should == false
+ case 0
+ in 0 unless true
+ true
+ else
+ false
+ end
+ RUBY
- it "supports using any name with _ at the beginning in a pattern several times" do
- eval(<<~RUBY).should == true
- case [0, 1, 2]
- in [0, _x, _x]
- true
- end
- RUBY
+ eval(<<~RUBY).should == true
+ case 0
+ in 0 unless false
+ true
+ else
+ false
+ end
+ RUBY
+ end
- eval(<<~RUBY).should == true
- case {a: 0, b: 1, c: 2}
- in {a: 0, b: _x, c: _x}
- true
- 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 support using variable name (except _) several times" do
- -> {
+ it "does not evaluate guard if pattern does not match" do
eval <<~RUBY
- case [0]
- in [a, a]
+ case 0
+ in 1 if (ScratchPad << :foo) || true
+ else
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
+ ScratchPad.recorded.should == []
+ end
- eval(<<~RUBY).should == false
- case [1, 2]
- in [n, ^n]
- true
- else
- false
- end
- RUBY
- 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 "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]
+ 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
- }.should raise_error(SyntaxError, /n: no such local variable/)
- end
- 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
+ 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
- it "does not support variable binding" do
- -> {
- eval <<~RUBY
- case [0, 1]
- in [0, 0] | [0, a]
+ describe "value pattern" do
+ it "matches an object such that pattern === object" do
+ eval(<<~RUBY).should == true
+ case 0
+ in 0
+ true
end
RUBY
- }.should raise_error(SyntaxError, /illegal variable in alternative pattern/)
- end
-
- it "support underscore prefixed variables in alternation" do
- eval(<<~RUBY).should == true
- case [0, 1]
- in [1, _]
- false
- in [0, 0] | [0, _a]
- true
- end
- RUBY
- end
- it "can be used as a nested pattern" do
- eval(<<~RUBY).should == true
- case [[1], ["2"]]
- in [[0] | nil, _]
- false
- in [[1], [1]]
- false
- in [[1], [2 | "2"]]
+ eval(<<~RUBY).should == true
+ case 0
+ in (-1..1)
true
- end
- RUBY
+ end
+ RUBY
- eval(<<~RUBY).should == true
- case [1, 2]
- in [0, _] | {a: 0}
- false
- in {a: 1, b: 2} | [1, 2]
+ eval(<<~RUBY).should == true
+ case 0
+ in Integer
true
- end
- RUBY
- end
- end
+ end
+ RUBY
- 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
+ eval(<<~RUBY).should == true
+ case "0"
+ in /0/
+ true
+ end
+ RUBY
- 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
+ eval(<<~RUBY).should == true
+ case "0"
+ in ->(s) { s == "0" }
+ true
+ end
+ RUBY
+ 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 "allows string literal with interpolation" do
+ x = "x"
- it "supports form Constant[pat, pat, ...]" do
- eval(<<~RUBY).should == true
- case [0, 1, 2]
- in Array[0, 1, 2]
- true
- end
- RUBY
+ eval(<<~RUBY).should == true
+ case "x"
+ in "#{x + ""}"
+ true
+ end
+ RUBY
+ end
end
- it "supports form [pat, pat, ...]" do
- eval(<<~RUBY).should == true
- case [0, 1, 2]
- in [0, 1, 2]
- true
- end
- RUBY
- 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 "supports form pat, pat, ..." do
- eval(<<~RUBY).should == true
- case [0, 1, 2]
- in 0, 1, 2
- true
- end
- RUBY
+ it "makes bounded variable visible outside a case statement scope" do
+ eval(<<~RUBY).should == 0
+ case 0
+ in a
+ end
- eval(<<~RUBY).should == 1
- case [0, 1, 2]
- in 0, a, 2
a
- end
- RUBY
+ RUBY
+ end
- eval(<<~RUBY).should == [1, 2]
- case [0, 1, 2]
- in 0, *rest
- rest
- end
- 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
- 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
+ [a, b, c]
+ RUBY
+ end
- eval(<<~RUBY).should == true
- case obj
- in [Integer, Integer]
- true
- end
- RUBY
- end
+ it "allow using _ name to drop values" do
+ eval(<<~RUBY).should == 0
+ case [0, 1]
+ in [a, _]
+ a
+ end
+ RUBY
+ end
- it "calls #deconstruct once for multiple patterns, caching the result" do
- obj = Object.new
+ it "supports using _ in a pattern several times" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in [0, _, _]
+ true
+ end
+ RUBY
+ end
- def obj.deconstruct
- ScratchPad << :deconstruct
- [0, 1]
+ it "supports using any name with _ at the beginning in a pattern several times" do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in [0, _x, _x]
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case {a: 0, b: 1, c: 2}
+ in {a: 0, b: _x, c: _x}
+ true
+ end
+ RUBY
end
- eval(<<~RUBY).should == true
- case obj
- in [1, 2]
- false
- in [0, 1]
- true
- end
- RUBY
+ 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
- ScratchPad.recorded.should == [:deconstruct]
- end
+ it "supports existing variables in a pattern specified with ^ operator" do
+ a = 0
- it "calls #deconstruct even on objects that are already an array" do
- obj = [1, 2]
- def obj.deconstruct
- ScratchPad << :deconstruct
- [3, 4]
+ eval(<<~RUBY).should == true
+ case 0
+ in ^a
+ true
+ end
+ RUBY
end
- eval(<<~RUBY).should == true
- case obj
- in [3, 4]
- true
- else
- false
- end
- RUBY
+ it "allows applying ^ operator to bound variables" do
+ eval(<<~RUBY).should == 1
+ case [1, 1]
+ in [n, ^n]
+ n
+ end
+ RUBY
- ScratchPad.recorded.should == [:deconstruct]
- end
+ eval(<<~RUBY).should == false
+ case [1, 2]
+ in [n, ^n]
+ true
+ else
+ false
+ 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
+ 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
- it "does not match object without #deconstruct method" do
- obj = Object.new
- obj.should_receive(:respond_to?).with(:deconstruct)
+ 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
- eval(<<~RUBY).should == false
- case obj
- in Object[]
- true
- else
- false
- 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
- it "raises TypeError if #deconstruct method does not return array" do
- obj = Object.new
- def obj.deconstruct; "" end
+ it "support underscore prefixed variables in alternation" do
+ eval(<<~RUBY).should == true
+ case [0, 1]
+ in [1, _]
+ false
+ in [0, 0] | [0, _a]
+ true
+ end
+ RUBY
+ end
- -> {
- eval <<~RUBY
- case obj
- in Object[]
- else
+ it "can be used as a nested pattern" do
+ eval(<<~RUBY).should == true
+ case [[1], ["2"]]
+ in [[0] | nil, _]
+ false
+ in [[1], [1]]
+ false
+ in [[1], [2 | "2"]]
+ true
end
RUBY
- }.should raise_error(TypeError, /deconstruct must return Array/)
+
+ eval(<<~RUBY).should == true
+ case [1, 2]
+ in [0, _] | {a: 0}
+ false
+ in {a: 1, b: 2} | [1, 2]
+ true
+ end
+ RUBY
+ end
end
- it "accepts a subclass of Array from #deconstruct" do
- obj = Object.new
- def obj.deconstruct
- subarray = Class.new(Array).new(2)
- def subarray.[](n)
- n
- end
- subarray
+ 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
- eval(<<~RUBY).should == true
- case obj
- in [1, 2]
- false
- in [0, 1]
- true
- end
- RUBY
+ 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
- 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
+ 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
- eval(<<~RUBY).should == false
- case obj
- in Object[0]
- true
- else
- false
- 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 "binds variables" do
- eval(<<~RUBY).should == [0, 1, 2]
- case [0, 1, 2]
- in [a, b, c]
- [a, b, c]
- 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 splat operator *rest" do
- eval(<<~RUBY).should == [1, 2]
- case [0, 1, 2]
- in [0, *rest]
- rest
- end
- RUBY
- end
+ it "supports form pat, pat, ..." do
+ eval(<<~RUBY).should == true
+ case [0, 1, 2]
+ in 0, 1, 2
+ true
+ end
+ RUBY
- 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
+ eval(<<~RUBY).should == 1
+ case [0, 1, 2]
+ in 0, a, 2
+ a
+ end
+ RUBY
- 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 == [1, 2]
+ case [0, 1, 2]
+ in 0, *rest
+ rest
+ end
+ RUBY
+ end
- eval(<<~RUBY).should == true
- case [0, 1, 2, 3]
- in 0, 1,;
- true
- 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
- it "matches [] with []" do
- eval(<<~RUBY).should == true
- case []
- in []
- true
+ eval(<<~RUBY).should == true
+ case obj
+ in [Integer, Integer]
+ true
+ end
+ RUBY
+ end
+
+ ruby_version_is "3.0" do
+ it "calls #deconstruct once for multiple patterns, caching the result" do
+ obj = Object.new
+
+ def obj.deconstruct
+ ScratchPad << :deconstruct
+ [0, 1]
+ end
+
+ eval(<<~RUBY).should == true
+ case obj
+ in [1, 2]
+ false
+ in [0, 1]
+ true
+ end
+ RUBY
+
+ ScratchPad.recorded.should == [:deconstruct]
end
- RUBY
- end
+ end
- it "matches anything with *" do
- eval(<<~RUBY).should == true
- case [0, 1]
- in *;
- true
+ it "calls #deconstruct even on objects that are already an array" do
+ obj = [1, 2]
+ def obj.deconstruct
+ ScratchPad << :deconstruct
+ [3, 4]
end
- RUBY
- end
- it "can be used as a nested pattern" do
- eval(<<~RUBY).should == true
- case [[1], ["2"]]
- in [[0] | nil, _]
+ eval(<<~RUBY).should == true
+ case obj
+ in [3, 4]
+ true
+ else
false
- in [[1], [1]]
+ end
+ RUBY
+
+ ScratchPad.recorded.should == [:deconstruct]
+ 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
- in [[1], [2 | "2"]]
+ end
+ RUBY
+ end
+
+ it "does not match object without #deconstruct method" do
+ obj = Object.new
+ obj.should_receive(:respond_to?).with(:deconstruct)
+
+ 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 "accepts a subclass of Array from #deconstruct" do
+ obj = Object.new
+ def obj.deconstruct
+ subarray = Class.new(Array).new(2)
+ def subarray.[](n)
+ n
+ end
+ subarray
end
- RUBY
- eval(<<~RUBY).should == true
- case [1, 2]
- in [0, _] | {a: 0}
+ eval(<<~RUBY).should == true
+ case obj
+ in [1, 2]
false
- in {a: 1, b: 2} | [1, 2]
+ in [0, 1]
true
- end
- RUBY
- end
- end
+ end
+ RUBY
+ 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 "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
- 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
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[0]
+ true
+ else
+ false
+ 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 "binds variables" do
+ eval(<<~RUBY).should == [0, 1, 2]
+ case [0, 1, 2]
+ in [a, b, c]
+ [a, b, c]
+ 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
+ it "supports splat operator *rest" do
+ eval(<<~RUBY).should == [1, 2]
+ case [0, 1, 2]
+ in [0, *rest]
+ rest
+ end
+ RUBY
+ end
- eval(<<~RUBY).should == [0, 1]
- case {a: 0, b: 1}
- in a: a, b: b
- [a, b]
- end
- RUBY
+ 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
- eval(<<~RUBY).should == { b: 1, c: 2 }
- case {a: 0, b: 1, c: 2}
- in a: 0, **rest
- rest
- 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
- 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
+ eval(<<~RUBY).should == true
+ case [0, 1, 2, 3]
+ in 0, 1,;
+ true
+ end
+ RUBY
+ end
- a = b = nil
- eval(<<~RUBY).should == [0, 1]
- case {a: 0, b: 1}
- in Hash[a:, b:]
- [a, b]
- end
- RUBY
+ it "matches [] with []" do
+ eval(<<~RUBY).should == true
+ case []
+ in []
+ true
+ end
+ RUBY
+ end
- a = b = nil
- eval(<<~RUBY).should == [0, 1]
- case {a: 0, b: 1}
- in {a:, b:}
- [a, b]
- end
- RUBY
+ it "matches anything with *" do
+ eval(<<~RUBY).should == true
+ case [0, 1]
+ in *;
+ true
+ end
+ RUBY
+ end
- a = nil
- eval(<<~RUBY).should == [0, {b: 1, c: 2}]
- case {a: 0, b: 1, c: 2}
- in {a:, **rest}
- [a, rest]
- end
- RUBY
+ it "can be used as a nested pattern" do
+ eval(<<~RUBY).should == true
+ case [[1], ["2"]]
+ in [[0] | nil, _]
+ false
+ in [[1], [1]]
+ false
+ in [[1], [2 | "2"]]
+ true
+ end
+ RUBY
- a = b = nil
- eval(<<~RUBY).should == [0, 1]
- case {a: 0, b: 1}
- in a:, b:
- [a, b]
- end
- RUBY
+ eval(<<~RUBY).should == true
+ case [1, 2]
+ in [0, _] | {a: 0}
+ false
+ in {a: 1, b: 2} | [1, 2]
+ true
+ end
+ RUBY
+ end
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
+ 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 'string': key literal" do
- eval(<<~RUBY).should == true
- case {a: 0}
- in {"a": 0}
- 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 "does not support non-symbol keys" do
- -> {
- eval <<~RUBY
- case {a: 1}
- in {"a" => 1}
+ 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
- }.should raise_error(SyntaxError, /unexpected/)
- end
+ end
- it "does not support string interpolation in keys" do
- x = "a"
+ 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'
- case {a: 1}
- in {"#{x}": 1}
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in a: a, b: b
+ [a, b]
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}
+ eval(<<~RUBY).should == { b: 1, c: 2 }
+ case {a: 0, b: 1, c: 2}
+ in a: 0, **rest
+ rest
end
RUBY
- }.should raise_error(SyntaxError, /duplicated key name/)
- end
+ 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
+ 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
- eval(<<~RUBY).should == true
- case obj
- in {a: 1}
- true
- end
- RUBY
- end
+ 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
- it "calls #deconstruct_keys per pattern" do
- obj = Object.new
+ a = nil
+ eval(<<~RUBY).should == [0, {b: 1, c: 2}]
+ case {a: 0, b: 1, c: 2}
+ in {a:, **rest}
+ [a, rest]
+ end
+ RUBY
- def obj.deconstruct_keys(*)
- ScratchPad << :deconstruct_keys
- {a: 1}
+ a = b = nil
+ eval(<<~RUBY).should == [0, 1]
+ case {a: 0, b: 1}
+ in a:, b:
+ [a, b]
+ end
+ RUBY
end
- eval(<<~RUBY).should == true
- case obj
- in {b: 1}
- false
- in {a: 1}
- true
- end
- RUBY
+ 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
- ScratchPad.recorded.should == [:deconstruct_keys, :deconstruct_keys]
- end
+ it "supports 'string': key literal" do
+ eval(<<~RUBY).should == true
+ case {a: 0}
+ in {"a": 0}
+ 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 support non-symbol keys" do
+ -> {
+ eval <<~RUBY
+ case {a: 1}
+ in {"a" => 1}
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /unexpected/)
+ end
- it "does not match object without #deconstruct_keys method" do
- obj = Object.new
- obj.should_receive(:respond_to?).with(:deconstruct_keys)
+ it "does not support string interpolation in keys" do
+ x = "a"
- eval(<<~RUBY).should == false
- case obj
- in Object[a: 1]
- true
- else
- false
- end
- RUBY
- end
+ -> {
+ eval <<~'RUBY'
+ case {a: 1}
+ in {"#{x}": 1}
+ end
+ RUBY
+ }.should raise_error(SyntaxError, /symbol literal with interpolation is not allowed/)
+ end
- it "does not match object if #deconstruct_keys method does not return Hash" do
- obj = Object.new
- def obj.deconstruct_keys(*); "" 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
- -> {
- eval <<~RUBY
+ 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 Object[a: 1]
+ in {a: 1}
+ true
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
+ 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
+ it "calls #deconstruct_keys per pattern" do
+ obj = Object.new
- eval(<<~RUBY).should == false
- case obj
- in Object[a: 2]
- true
- else
- false
+ def obj.deconstruct_keys(*)
+ ScratchPad << :deconstruct_keys
+ {a: 1}
end
- RUBY
- end
- it "passes keys specified in pattern as arguments to #deconstruct_keys method" do
- obj = Object.new
+ eval(<<~RUBY).should == true
+ case obj
+ in {b: 1}
+ false
+ in {a: 1}
+ true
+ end
+ RUBY
- def obj.deconstruct_keys(*args)
- ScratchPad << args
- {a: 1, b: 2, c: 3}
+ ScratchPad.recorded.should == [:deconstruct_keys, :deconstruct_keys]
end
- eval <<~RUBY
- case obj
- in Object[a: 1, b: 2, c: 3]
- end
- RUBY
+ 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
- ScratchPad.recorded.sort.should == [[[:a, :b, :c]]]
- end
+ it "does not match object without #deconstruct_keys method" do
+ obj = Object.new
+ obj.should_receive(:respond_to?).with(:deconstruct_keys)
- it "passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" do
- obj = Object.new
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
- def obj.deconstruct_keys(*args)
- ScratchPad << args
- {a: 1, b: 2, c: 3}
+ 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
- eval <<~RUBY
- case obj
- in Object[a: 1, b: 2, **]
- end
- RUBY
+ 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
- ScratchPad.recorded.sort.should == [[[:a, :b]]]
- end
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
- it "passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" do
- obj = Object.new
+ 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
- def obj.deconstruct_keys(*args)
- ScratchPad << args
- {a: 1, b: 2}
+ eval(<<~RUBY).should == false
+ case obj
+ in Object[a: 2]
+ true
+ else
+ false
+ end
+ RUBY
end
- eval <<~RUBY
- case obj
- in Object[a: 1, **rest]
- end
- RUBY
+ it "passes keys specified in pattern as arguments to #deconstruct_keys method" do
+ obj = Object.new
- 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]
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2, c: 3}
end
- 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
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, b: 2, c: 3]
+ end
+ RUBY
- 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
+ ScratchPad.recorded.sort.should == [[[:a, :b, :c]]]
+ end
- eval(<<~RUBY).should == false
- case {a: 1, b: 2}
- in {a: 1, **nil}
- true
- else
- false
- end
- RUBY
- end
+ it "passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" do
+ obj = Object.new
- it "can match partially" do
- eval(<<~RUBY).should == true
- case {a: 1, b: 2}
- in {a: 1}
- true
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2, c: 3}
end
- RUBY
- end
- it "matches {} with {}" do
- eval(<<~RUBY).should == true
- case {}
- in {}
- true
- end
- RUBY
- end
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, b: 2, **]
+ end
+ RUBY
- it "matches anything with **" do
- eval(<<~RUBY).should == true
- case {a: 1}
- in **;
- true
- end
- RUBY
- end
+ ScratchPad.recorded.sort.should == [[[:a, :b]]]
+ end
- it "can be used as a nested pattern" do
- eval(<<~RUBY).should == true
- case {a: {a: 1, b: 1}, b: {a: 1, b: 2}}
- in {a: {a: 0}}
- false
- in {a: {a: 1}, b: {b: 1}}
- false
- in {a: {a: 1}, b: {b: 2}}
- true
- end
- RUBY
+ it "passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" do
+ obj = Object.new
- eval(<<~RUBY).should == true
- case [{a: 1, b: [1]}, {a: 1, c: ["2"]}]
- in [{a:, c:},]
- false
- in [{a: 1, b:}, {a: 1, c: [Integer]}]
- false
- in [_, {a: 1, c: [String]}]
- true
+ def obj.deconstruct_keys(*args)
+ ScratchPad << args
+ {a: 1, b: 2}
end
- RUBY
- end
- end
- describe "refinements" do
- it "are used for #deconstruct" do
- refinery = Module.new do
- refine Array do
- def deconstruct
- [0]
+ eval <<~RUBY
+ case obj
+ in Object[a: 1, **rest]
end
- end
+ RUBY
+
+ ScratchPad.recorded.should == [[nil]]
end
- result = nil
- Module.new do
- using refinery
+ 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
- result = eval(<<~RUBY)
- case []
- in [0]
- true
+ 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
- result.should == true
- 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
- it "are used for #deconstruct_keys" do
- refinery = Module.new do
- refine Hash do
- def deconstruct_keys(_)
- {a: 0}
+ eval(<<~RUBY).should == false
+ case {a: 1, b: 2}
+ in {a: 1, **nil}
+ true
+ else
+ false
end
- end
+ RUBY
end
- result = nil
- Module.new do
- using refinery
+ it "can match partially" do
+ eval(<<~RUBY).should == true
+ case {a: 1, b: 2}
+ in {a: 1}
+ true
+ end
+ RUBY
+ end
- result = eval(<<~RUBY)
+ it "matches {} with {}" do
+ eval(<<~RUBY).should == true
case {}
- in a: 0
+ in {}
true
end
RUBY
end
- result.should == true
- end
-
- it "are used for #=== in constant pattern" do
- refinery = Module.new do
- refine Array.singleton_class do
- def ===(obj)
- obj.is_a?(Hash)
+ it "matches anything with **" do
+ eval(<<~RUBY).should == true
+ case {a: 1}
+ in **;
+ true
end
- end
+ RUBY
end
- result = nil
- Module.new do
- using refinery
+ it "can be used as a nested pattern" do
+ eval(<<~RUBY).should == true
+ case {a: {a: 1, b: 1}, b: {a: 1, b: 2}}
+ in {a: {a: 0}}
+ false
+ in {a: {a: 1}, b: {b: 1}}
+ false
+ in {a: {a: 1}, b: {b: 2}}
+ true
+ end
+ RUBY
- result = eval(<<~RUBY)
- case {}
- in Array
- true
+ eval(<<~RUBY).should == true
+ case [{a: 1, b: [1]}, {a: 1, c: ["2"]}]
+ in [{a:, c:},]
+ false
+ in [{a: 1, b:}, {a: 1, c: [Integer]}]
+ false
+ in [_, {a: 1, c: [String]}]
+ true
end
RUBY
end
-
- result.should == true
end
- end
- ruby_version_is "3.1" do
- it "can omit parentheses in one line pattern matching" do
- eval(<<~RUBY).should == [1, 2]
- [1, 2] => a, b
- [a, b]
- RUBY
+ describe "refinements" do
+ it "are used for #deconstruct" do
+ refinery = Module.new do
+ refine Array do
+ def deconstruct
+ [0]
+ end
+ end
+ end
- eval(<<~RUBY).should == 1
- {a: 1} => a:
- a
- RUBY
- end
+ result = nil
+ Module.new do
+ using refinery
- it "supports pinning instance variables" do
- eval(<<~RUBY).should == true
- @a = /a/
- case 'abc'
- in ^@a
- true
+ result = eval(<<~RUBY)
+ case []
+ in [0]
+ true
+ end
+ RUBY
end
- RUBY
- end
- it "supports pinning class variables" do
- result = nil
- Module.new do
- result = module_eval(<<~RUBY)
- @@a = 0..10
+ result.should == true
+ end
- case 2
- in ^@@a
- true
+ it "are used for #deconstruct_keys" do
+ refinery = Module.new do
+ refine Hash do
+ def deconstruct_keys(_)
+ {a: 0}
+ end
end
- RUBY
- end
+ end
- result.should == true
- end
+ result = nil
+ Module.new do
+ using refinery
- it "supports pinning global variables" do
- eval(<<~RUBY).should == true
- $a = /a/
- case 'abc'
- in ^$a
- true
+ result = eval(<<~RUBY)
+ case {}
+ in a: 0
+ true
+ end
+ RUBY
end
- RUBY
- end
- it "supports pinning expressions" do
- eval(<<~RUBY).should == true
- case 'abc'
- in ^(/a/)
- true
- end
- RUBY
+ result.should == true
+ end
- eval(<<~RUBY).should == true
- case {name: '2.6', released_at: Time.new(2018, 12, 25)}
- in {released_at: ^(Time.new(2010)..Time.new(2020))}
- true
+ it "are used for #=== in constant pattern" do
+ refinery = Module.new do
+ refine Array.singleton_class do
+ def ===(obj)
+ obj.is_a?(Hash)
+ end
+ end
end
- RUBY
- eval(<<~RUBY).should == true
- case 0
- in ^(0+0)
- true
+ result = nil
+ Module.new do
+ using refinery
+
+ result = eval(<<~RUBY)
+ case {}
+ in Array
+ true
+ end
+ RUBY
end
- RUBY
+
+ result.should == true
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "can omit parentheses in one line pattern matching" do
+ eval(<<~RUBY).should == [1, 2]
+ [1, 2] => a, b
+ [a, b]
+ RUBY
+
+ eval(<<~RUBY).should == 1
+ {a: 1} => a:
+ a
+ RUBY
+ end
end
end
end
diff --git a/spec/ruby/language/precedence_spec.rb b/spec/ruby/language/precedence_spec.rb
index c5adcca2c0..5a3c2861ce 100644
--- a/spec/ruby/language/precedence_spec.rb
+++ b/spec/ruby/language/precedence_spec.rb
@@ -14,44 +14,46 @@ require_relative 'fixtures/precedence'
# the level below (as well as showing associativity within
# the precedence level).
-# Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide'
-# Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324
-#
-# Table 22.4. Ruby operators (high to low precedence)
-# Method Operator Description
-# -----------------------------------------------------------------------
-# :: .
-# x* [ ] [ ]= Element reference, element set
-# x ** Exponentiation
-# x ! ~ + - Not, complement, unary plus and minus
-# (method names for the last two are +@ and -@)
-# x * / % Multiply, divide, and modulo
-# x + - Plus and minus
-# x >> << Right and left shift
-# x & “And” (bitwise for integers)
-# x ^ | Exclusive “or” and regular “or” (bitwise for integers)
-# x <= < > >= Comparison operators
-# x <=> == === != =~ !~ Equality and pattern match operators (!=
-# and !~ may not be defined as methods)
-# && Logical “and”
-# || Logical “or”
-# .. ... Range (inclusive and exclusive)
-# ? : Ternary if-then-else
-# = %= /= -= += |= &= Assignment
-# >>= <<= *= &&= ||= **=
-# defined? Check if symbol defined
-# not Logical negation
-# or and Logical composition
-# if unless while until Expression modifiers
-# begin/end Block expression
-# -----------------------------------------------------------------------
-#
-# * Operators marked with 'x' in the Method column are implemented as methods
-# and can be overridden (except != and !~ as noted). (But see the specs
-# below for implementations that define != and !~ as methods.)
-#
-# ** These are not included in the excerpted table but are shown here for
-# completeness.
+=begin
+Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide'
+Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324
+
+Table 22.4. Ruby operators (high to low precedence)
+Method Operator Description
+-----------------------------------------------------------------------
+ :: .
+ x* [ ] [ ]= Element reference, element set
+ x ** Exponentiation
+ x ! ~ + - Not, complement, unary plus and minus
+ (method names for the last two are +@ and -@)
+ x * / % Multiply, divide, and modulo
+ x + - Plus and minus
+ x >> << Right and left shift
+ x & “And” (bitwise for integers)
+ x ^ | Exclusive “or” and regular “or” (bitwise for integers)
+ x <= < > >= Comparison operators
+ x <=> == === != =~ !~ Equality and pattern match operators (!=
+ and !~ may not be defined as methods)
+ && Logical “and”
+ || Logical “or”
+ .. ... Range (inclusive and exclusive)
+ ? : Ternary if-then-else
+ = %= /= -= += |= &= Assignment
+ >>= <<= *= &&= ||= **=
+ defined? Check if symbol defined
+ not Logical negation
+ or and Logical composition
+ if unless while until Expression modifiers
+ begin/end Block expression
+-----------------------------------------------------------------------
+
+* Operators marked with 'x' in the Method column are implemented as methods
+and can be overridden (except != and !~ as noted). (But see the specs
+below for implementations that define != and !~ as methods.)
+
+** These are not included in the excerpted table but are shown here for
+completeness.
+=end
# -----------------------------------------------------------------------
# It seems that this table is not correct anymore
diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb
index fe865cc325..d311750200 100644
--- a/spec/ruby/language/predefined_spec.rb
+++ b/spec/ruby/language/predefined_spec.rb
@@ -7,33 +7,37 @@ require 'stringio'
# Entries marked [r/o] are read-only and an error will be raised of the program attempts to
# modify them. Entries marked [thread] are thread local.
-# Exception Information
-# ---------------------------------------------------------------------------------------------------
-#
-# $! Exception The exception object passed to raise. [thread]
-# $@ Array The stack backtrace generated by the last exception. [thread]
-
-# Pattern Matching Variables
-# ---------------------------------------------------------------------------------------------------
-#
-# These variables are set to nil after an unsuccessful pattern match.
-#
-# $& String The string matched (following a successful pattern match). This variable is
-# local to the current scope. [r/o, thread]
-# $+ String The contents of the highest-numbered group matched following a successful
-# pattern match. Thus, in "cat" =~/(c|a)(t|z)/, $+ will be set to “t”. This
-# variable is local to the current scope. [r/o, thread]
-# $` String The string preceding the match in a successful pattern match. This variable
-# is local to the current scope. [r/o, thread]
-# $' String The string following the match in a successful pattern match. This variable
-# is local to the current scope. [r/o, thread]
-# $1 to $<N> String The contents of successive groups matched in a successful pattern match. In
-# "cat" =~/(c|a)(t|z)/, $1 will be set to “a” and $2 to “t”. This variable
-# is local to the current scope. [r/o, thread]
-# $~ MatchData An object that encapsulates the results of a successful pattern match. The
-# variables $&, $`, $', and $1 to $<N> are all derived from $~. Assigning to $~
-# changes the values of these derived variables. This variable is local to the
-# current scope. [thread]
+=begin
+Exception Information
+---------------------------------------------------------------------------------------------------
+
+$! Exception The exception object passed to raise. [thread]
+$@ Array The stack backtrace generated by the last exception. [thread]
+=end
+
+=begin
+Pattern Matching Variables
+---------------------------------------------------------------------------------------------------
+
+These variables are set to nil after an unsuccessful pattern match.
+
+$& String The string matched (following a successful pattern match). This variable is
+ local to the current scope. [r/o, thread]
+$+ String The contents of the highest-numbered group matched following a successful
+ pattern match. Thus, in "cat" =~/(c|a)(t|z)/, $+ will be set to “t”. This
+ variable is local to the current scope. [r/o, thread]
+$` String The string preceding the match in a successful pattern match. This variable
+ is local to the current scope. [r/o, thread]
+$' String The string following the match in a successful pattern match. This variable
+ is local to the current scope. [r/o, thread]
+$1 to $<N> String The contents of successive groups matched in a successful pattern match. In
+ "cat" =~/(c|a)(t|z)/, $1 will be set to “a” and $2 to “t”. This variable
+ is local to the current scope. [r/o, thread]
+$~ MatchData An object that encapsulates the results of a successful pattern match. The
+ variables $&, $`, $', and $1 to $<N> are all derived from $~. Assigning to $~
+ changes the values of these derived variables. This variable is local to the
+ current scope. [thread]
+=end
describe "Predefined global $~" do
@@ -502,39 +506,41 @@ describe "Predefined global $!" do
end
end
-# Input/Output Variables
-# ---------------------------------------------------------------------------------------------------
-#
-# $/ String The input record separator (newline by default). This is the value that rou-
-# tines such as Kernel#gets use to determine record boundaries. If set to
-# nil, gets will read the entire file.
-# $-0 String Synonym for $/.
-# $\ String The string appended to the output of every call to methods such as
-# Kernel#print and IO#write. The default value is nil.
-# $, String The separator string output between the parameters to methods such as
-# Kernel#print and Array#join. Defaults to nil, which adds no text.
-# $. Integer The number of the last line read from the current input file.
-# $; String The default separator pattern used by String#split. May be set from the
-# command line using the -F flag.
-# $< Object An object that provides access to the concatenation of the contents of all
-# the files given as command-line arguments or $stdin (in the case where
-# there are no arguments). $< supports methods similar to a File object:
-# binmode, close, closed?, each, each_byte, each_line, eof, eof?,
-# file, filename, fileno, getc, gets, lineno, lineno=, path, pos, pos=,
-# read, readchar, readline, readlines, rewind, seek, skip, tell, to_a,
-# to_i, to_io, to_s, along with the methods in Enumerable. The method
-# file returns a File object for the file currently being read. This may change
-# as $< reads through the files on the command line. [r/o]
-# $> IO The destination of output for Kernel#print and Kernel#printf. The
-# default value is $stdout.
-# $_ String The last line read by Kernel#gets or Kernel#readline. Many string-
-# related functions in the Kernel module operate on $_ by default. The vari-
-# able is local to the current scope. [thread]
-# $-F String Synonym for $;.
-# $stderr IO The current standard error output.
-# $stdin IO The current standard input.
-# $stdout IO The current standard output. Assignment to $stdout is deprecated: use
-# $stdout.reopen instead.
+=begin
+Input/Output Variables
+---------------------------------------------------------------------------------------------------
+
+$/ String The input record separator (newline by default). This is the value that rou-
+ tines such as Kernel#gets use to determine record boundaries. If set to
+ nil, gets will read the entire file.
+$-0 String Synonym for $/.
+$\ String The string appended to the output of every call to methods such as
+ Kernel#print and IO#write. The default value is nil.
+$, String The separator string output between the parameters to methods such as
+ Kernel#print and Array#join. Defaults to nil, which adds no text.
+$. Integer The number of the last line read from the current input file.
+$; String The default separator pattern used by String#split. May be set from the
+ command line using the -F flag.
+$< Object An object that provides access to the concatenation of the contents of all
+ the files given as command-line arguments or $stdin (in the case where
+ there are no arguments). $< supports methods similar to a File object:
+ binmode, close, closed?, each, each_byte, each_line, eof, eof?,
+ file, filename, fileno, getc, gets, lineno, lineno=, path, pos, pos=,
+ read, readchar, readline, readlines, rewind, seek, skip, tell, to_a,
+ to_i, to_io, to_s, along with the methods in Enumerable. The method
+ file returns a File object for the file currently being read. This may change
+ as $< reads through the files on the command line. [r/o]
+$> IO The destination of output for Kernel#print and Kernel#printf. The
+ default value is $stdout.
+$_ String The last line read by Kernel#gets or Kernel#readline. Many string-
+ related functions in the Kernel module operate on $_ by default. The vari-
+ able is local to the current scope. [thread]
+$-F String Synonym for $;.
+$stderr IO The current standard error output.
+$stdin IO The current standard input.
+$stdout IO The current standard output. Assignment to $stdout is deprecated: use
+ $stdout.reopen instead.
+=end
describe "Predefined global $/" do
before :each do
@@ -564,6 +570,7 @@ describe "Predefined global $/" do
($/ = "xyz").should == "xyz"
end
+
it "changes $-0" do
$/ = "xyz"
$-0.should equal($/)
@@ -634,45 +641,6 @@ describe "Predefined global $-0" do
end
end
-describe "Predefined global $\\" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- @dollar_backslash = $\
- end
-
- after :each do
- $\ = @dollar_backslash
- $VERBOSE = @verbose
- end
-
- it "can be assigned a String" do
- str = "abc"
- $\ = str
- $\.should equal(str)
- end
-
- it "can be assigned nil" do
- $\ = nil
- $\.should be_nil
- end
-
- it "returns the value assigned" do
- ($\ = "xyz").should == "xyz"
- end
-
- it "does not call #to_str to convert the object to a String" do
- obj = mock("$\\ value")
- obj.should_not_receive(:to_str)
-
- -> { $\ = obj }.should raise_error(TypeError)
- end
-
- it "raises a TypeError if assigned not String" do
- -> { $\ = 1 }.should raise_error(TypeError)
- -> { $\ = true }.should raise_error(TypeError)
- end
-end
-
describe "Predefined global $," do
after :each do
$, = nil
@@ -686,8 +654,10 @@ describe "Predefined global $," do
-> { $, = Object.new }.should raise_error(TypeError)
end
- it "warns if assigned non-nil" do
- -> { $, = "_" }.should complain(/warning: `\$,' is deprecated/)
+ ruby_version_is "2.7" do
+ it "warns if assigned non-nil" do
+ -> { $, = "_" }.should complain(/warning: `\$,' is deprecated/)
+ end
end
end
@@ -723,8 +693,10 @@ describe "Predefined global $;" do
$; = nil
end
- it "warns if assigned non-nil" do
- -> { $; = "_" }.should complain(/warning: `\$;' is deprecated/)
+ ruby_version_is "2.7" do
+ it "warns if assigned non-nil" do
+ -> { $; = "_" }.should complain(/warning: `\$;' is deprecated/)
+ end
end
end
@@ -797,52 +769,54 @@ describe "Predefined global $_" do
end
end
-# Execution Environment Variables
-# ---------------------------------------------------------------------------------------------------
-#
-# $0 String The name of the top-level Ruby program being executed. Typically this will
-# be the program’s filename. On some operating systems, assigning to this
-# variable will change the name of the process reported (for example) by the
-# ps(1) command.
-# $* Array An array of strings containing the command-line options from the invoca-
-# tion of the program. Options used by the Ruby interpreter will have been
-# removed. [r/o]
-# $" Array An array containing the filenames of modules loaded by require. [r/o]
-# $$ Integer The process number of the program being executed. [r/o]
-# $? Process::Status The exit status of the last child process to terminate. [r/o, thread]
-# $: Array An array of strings, where each string specifies a directory to be searched for
-# Ruby scripts and binary extensions used by the load and require methods.
-# The initial value is the value of the arguments passed via the -I command-
-# line option, followed by an installation-defined standard library location, fol-
-# lowed by the current directory (“.”). This variable may be set from within a
-# program to alter the default search path; typically, programs use $: << dir
-# to append dir to the path. [r/o]
-# $-a Object True if the -a option is specified on the command line. [r/o]
-# $-d Object Synonym for $DEBUG.
-# $DEBUG Object Set to true if the -d command-line option is specified.
-# __FILE__ String The name of the current source file. [r/o]
-# $F Array The array that receives the split input line if the -a command-line option is
-# used.
-# $FILENAME String The name of the current input file. Equivalent to $<.filename. [r/o]
-# $-i String If in-place edit mode is enabled (perhaps using the -i command-line
-# option), $-i holds the extension used when creating the backup file. If you
-# set a value into $-i, enables in-place edit mode.
-# $-I Array Synonym for $:. [r/o]
-# $-K String Sets the multibyte coding system for strings and regular expressions. Equiv-
-# alent to the -K command-line option.
-# $-l Object Set to true if the -l option (which enables line-end processing) is present
-# on the command line. [r/o]
-# __LINE__ String The current line number in the source file. [r/o]
-# $LOAD_PATH Array A synonym for $:. [r/o]
-# $-p Object Set to true if the -p option (which puts an implicit while gets . . . end
-# loop around your program) is present on the command line. [r/o]
-# $VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com-
-# mand line. Set to false if no option, or -W1 is given. Set to nil if -W0
-# was specified. Setting this option to true causes the interpreter and some
-# library routines to report additional information. Setting to nil suppresses
-# all warnings (including the output of Kernel.warn).
-# $-v Object Synonym for $VERBOSE.
-# $-w Object Synonym for $VERBOSE.
+=begin
+Execution Environment Variables
+---------------------------------------------------------------------------------------------------
+
+$0 String The name of the top-level Ruby program being executed. Typically this will
+ be the program’s filename. On some operating systems, assigning to this
+ variable will change the name of the process reported (for example) by the
+ ps(1) command.
+$* Array An array of strings containing the command-line options from the invoca-
+ tion of the program. Options used by the Ruby interpreter will have been
+ removed. [r/o]
+$" Array An array containing the filenames of modules loaded by require. [r/o]
+$$ Integer The process number of the program being executed. [r/o]
+$? Process::Status The exit status of the last child process to terminate. [r/o, thread]
+$: Array An array of strings, where each string specifies a directory to be searched for
+ Ruby scripts and binary extensions used by the load and require methods.
+ The initial value is the value of the arguments passed via the -I command-
+ line option, followed by an installation-defined standard library location, fol-
+ lowed by the current directory (“.”). This variable may be set from within a
+ program to alter the default search path; typically, programs use $: << dir
+ to append dir to the path. [r/o]
+$-a Object True if the -a option is specified on the command line. [r/o]
+$-d Object Synonym for $DEBUG.
+$DEBUG Object Set to true if the -d command-line option is specified.
+__FILE__ String The name of the current source file. [r/o]
+$F Array The array that receives the split input line if the -a command-line option is
+ used.
+$FILENAME String The name of the current input file. Equivalent to $<.filename. [r/o]
+$-i String If in-place edit mode is enabled (perhaps using the -i command-line
+ option), $-i holds the extension used when creating the backup file. If you
+ set a value into $-i, enables in-place edit mode.
+$-I Array Synonym for $:. [r/o]
+$-K String Sets the multibyte coding system for strings and regular expressions. Equiv-
+ alent to the -K command-line option.
+$-l Object Set to true if the -l option (which enables line-end processing) is present
+ on the command line. [r/o]
+__LINE__ String The current line number in the source file. [r/o]
+$LOAD_PATH Array A synonym for $:. [r/o]
+$-p Object Set to true if the -p option (which puts an implicit while gets . . . end
+ loop around your program) is present on the command line. [r/o]
+$VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com-
+ mand line. Set to false if no option, or -W1 is given. Set to nil if -W0
+ was specified. Setting this option to true causes the interpreter and some
+ library routines to report additional information. Setting to nil suppresses
+ all warnings (including the output of Kernel.warn).
+$-v Object Synonym for $VERBOSE.
+$-w Object Synonym for $VERBOSE.
+=end
describe "Execution variable $:" do
it "is initialized to an array of strings" do
$:.is_a?(Array).should == true
@@ -861,8 +835,6 @@ describe "Execution variable $:" do
it "can be changed via <<" do
$: << "foo"
$:.should include("foo")
- ensure
- $:.delete("foo")
end
it "is read-only" do
@@ -878,16 +850,6 @@ describe "Execution variable $:" do
$-I = []
}.should raise_error(NameError)
end
-
- it "default $LOAD_PATH entries until sitelibdir included have @gem_prelude_index set" do
- skip "no sense in ruby itself" if MSpecScript.instance_variable_defined?(:@testing_ruby)
-
- $:.should.include?(RbConfig::CONFIG['sitelibdir'])
- idx = $:.index(RbConfig::CONFIG['sitelibdir'])
-
- $:[idx..-1].all? { |p| p.instance_variable_defined?(:@gem_prelude_index) }.should be_true
- $:[0...idx].all? { |p| !p.instance_variable_defined?(:@gem_prelude_index) }.should be_true
- end
end
describe "Global variable $\"" do
@@ -979,10 +941,6 @@ describe "Global variable $VERBOSE" do
$VERBOSE = @verbose
end
- it "is false by default" do
- $VERBOSE.should be_false
- end
-
it "converts truthy values to true" do
[true, 1, 0, [], ""].each do |true_value|
$VERBOSE = true_value
@@ -1037,7 +995,7 @@ describe "Global variable $0" do
it "is the path given as the main script and the same as __FILE__" do
script = "fixtures/dollar_zero.rb"
- Dir.chdir(__dir__) do
+ Dir.chdir(File.dirname(__FILE__)) do
ruby_exe(script).should == "#{script}\n#{script}\nOK"
end
end
@@ -1064,20 +1022,22 @@ describe "Global variable $0" do
end
end
-# Standard Objects
-# ---------------------------------------------------------------------------------------------------
-#
-# ARGF Object A synonym for $<.
-# ARGV Array A synonym for $*.
-# ENV Object A hash-like object containing the program’s environment variables. An
-# instance of class Object, ENV implements the full set of Hash methods. Used
-# to query and set the value of an environment variable, as in ENV["PATH"]
-# and ENV["term"]="ansi".
-# false FalseClass Singleton instance of class FalseClass. [r/o]
-# nil NilClass The singleton instance of class NilClass. The value of uninitialized
-# instance and global variables. [r/o]
-# self Object The receiver (object) of the current method. [r/o]
-# true TrueClass Singleton instance of class TrueClass. [r/o]
+=begin
+Standard Objects
+---------------------------------------------------------------------------------------------------
+
+ARGF Object A synonym for $<.
+ARGV Array A synonym for $*.
+ENV Object A hash-like object containing the program’s environment variables. An
+ instance of class Object, ENV implements the full set of Hash methods. Used
+ to query and set the value of an environment variable, as in ENV["PATH"]
+ and ENV["term"]="ansi".
+false FalseClass Singleton instance of class FalseClass. [r/o]
+nil NilClass The singleton instance of class NilClass. The value of uninitialized
+ instance and global variables. [r/o]
+self Object The receiver (object) of the current method. [r/o]
+true TrueClass Singleton instance of class TrueClass. [r/o]
+=end
describe "The predefined standard objects" do
it "includes ARGF" do
@@ -1130,51 +1090,80 @@ describe "The self pseudo-variable" do
end
end
-# Global Constants
-# ---------------------------------------------------------------------------------------------------
-#
-# The following constants are defined by the Ruby interpreter.
-#
-# DATA IO If the main program file contains the directive __END__, then
-# the constant DATA will be initialized so that reading from it will
-# return lines following __END__ from the source file.
-# FALSE FalseClass Synonym for false (deprecated, removed in Ruby 3).
-# NIL NilClass Synonym for nil (deprecated, removed in Ruby 3).
-# RUBY_PLATFORM String The identifier of the platform running this program. This string
-# is in the same form as the platform identifier used by the GNU
-# configure utility (which is not a coincidence).
-# RUBY_RELEASE_DATE String The date of this release.
-# RUBY_VERSION String The version number of the interpreter.
-# STDERR IO The actual standard error stream for the program. The initial
-# value of $stderr.
-# STDIN IO The actual standard input stream for the program. The initial
-# value of $stdin.
-# STDOUT IO The actual standard output stream for the program. The initial
-# value of $stdout.
-# SCRIPT_LINES__ Hash If a constant SCRIPT_LINES__ is defined and references a Hash,
-# Ruby will store an entry containing the contents of each file it
-# parses, with the file’s name as the key and an array of strings as
-# the value.
-# TOPLEVEL_BINDING Binding A Binding object representing the binding at Ruby’s top level—
-# the level where programs are initially executed.
-# TRUE TrueClass Synonym for true (deprecated, removed in Ruby 3).
+=begin
+Global Constants
+---------------------------------------------------------------------------------------------------
+
+The following constants are defined by the Ruby interpreter.
+
+DATA IO If the main program file contains the directive __END__, then
+ the constant DATA will be initialized so that reading from it will
+ return lines following __END__ from the source file.
+FALSE FalseClass Synonym for false (deprecated, removed in Ruby 3).
+NIL NilClass Synonym for nil (deprecated, removed in Ruby 3).
+RUBY_PLATFORM String The identifier of the platform running this program. This string
+ is in the same form as the platform identifier used by the GNU
+ configure utility (which is not a coincidence).
+RUBY_RELEASE_DATE String The date of this release.
+RUBY_VERSION String The version number of the interpreter.
+STDERR IO The actual standard error stream for the program. The initial
+ value of $stderr.
+STDIN IO The actual standard input stream for the program. The initial
+ value of $stdin.
+STDOUT IO The actual standard output stream for the program. The initial
+ value of $stdout.
+SCRIPT_LINES__ Hash If a constant SCRIPT_LINES__ is defined and references a Hash,
+ Ruby will store an entry containing the contents of each file it
+ parses, with the file’s name as the key and an array of strings as
+ the value.
+TOPLEVEL_BINDING Binding A Binding object representing the binding at Ruby’s top level—
+ the level where programs are initially executed.
+TRUE TrueClass Synonym for true (deprecated, removed in Ruby 3).
+=end
describe "The predefined global constants" do
describe "TRUE" do
- it "is no longer defined" do
- Object.const_defined?(:TRUE).should == false
+ ruby_version_is "3.0" do
+ it "is no longer defined" do
+ Object.const_defined?(:TRUE).should == false
+ end
+ end
+
+ ruby_version_is ""..."3.0" do
+ it "includes TRUE" do
+ Object.const_defined?(:TRUE).should == true
+ -> { TRUE }.should complain(/constant ::TRUE is deprecated/)
+ end
end
end
describe "FALSE" do
- it "is no longer defined" do
- Object.const_defined?(:FALSE).should == false
+ ruby_version_is "3.0" do
+ it "is no longer defined" do
+ Object.const_defined?(:FALSE).should == false
+ end
+ end
+
+ ruby_version_is ""..."3.0" do
+ it "includes FALSE" do
+ Object.const_defined?(:FALSE).should == true
+ -> { FALSE }.should complain(/constant ::FALSE is deprecated/)
+ end
end
end
describe "NIL" do
- it "is no longer defined" do
- Object.const_defined?(:NIL).should == false
+ ruby_version_is "3.0" do
+ it "is no longer defined" do
+ Object.const_defined?(:NIL).should == false
+ end
+ end
+
+ ruby_version_is ""..."3.0" do
+ it "includes NIL" do
+ Object.const_defined?(:NIL).should == true
+ -> { NIL }.should complain(/constant ::NIL is deprecated/)
+ end
end
end
@@ -1315,57 +1304,32 @@ describe "The predefined global constant" do
end
end
-describe "$LOAD_PATH.resolve_feature_path" do
- it "returns what will be loaded without actual loading, .rb file" do
- extension, path = $LOAD_PATH.resolve_feature_path('set')
- extension.should == :rb
- path.should.end_with?('/set.rb')
- end
-
- it "returns what will be loaded without actual loading, .so file" do
- require 'rbconfig'
- skip "no dynamically loadable standard extension" if RbConfig::CONFIG["EXTSTATIC"] == "static"
+ruby_version_is "2.7" do
+ describe "$LOAD_PATH.resolve_feature_path" do
+ it "returns what will be loaded without actual loading, .rb file" do
+ extension, path = $LOAD_PATH.resolve_feature_path('set')
+ extension.should == :rb
+ path.should.end_with?('/set.rb')
+ end
- extension, path = $LOAD_PATH.resolve_feature_path('etc')
- extension.should == :so
- path.should.end_with?("/etc.#{RbConfig::CONFIG['DLEXT']}")
- end
+ it "returns what will be loaded without actual loading, .so file" do
+ require 'rbconfig'
- ruby_version_is ""..."3.1" do
- it "raises LoadError if feature cannot be found" do
- -> { $LOAD_PATH.resolve_feature_path('noop') }.should raise_error(LoadError)
+ extension, path = $LOAD_PATH.resolve_feature_path('etc')
+ extension.should == :so
+ path.should.end_with?("/etc.#{RbConfig::CONFIG['DLEXT']}")
end
- end
- ruby_version_is "3.1" do
- it "return nil if feature cannot be found" do
- $LOAD_PATH.resolve_feature_path('noop').should be_nil
+ ruby_version_is "2.7"..."3.1" do
+ it "raises LoadError if feature cannot be found" do
+ -> { $LOAD_PATH.resolve_feature_path('noop') }.should raise_error(LoadError)
+ end
end
- end
-end
-
-# Some other pre-defined global variables
-
-describe "Predefined global $=" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- @dollar_assign = $=
- end
-
- after :each do
- $= = @dollar_assign
- $VERBOSE = @verbose
- end
-
- it "warns when accessed" do
- -> { a = $= }.should complain(/is no longer effective/)
- end
- it "warns when assigned" do
- -> { $= = "_" }.should complain(/is no longer effective/)
- end
-
- it "returns the value assigned" do
- ($= = "xyz").should == "xyz"
+ ruby_version_is "3.1" do
+ it "return nil if feature cannot be found" do
+ $LOAD_PATH.resolve_feature_path('noop').should be_nil
+ end
+ end
end
end
diff --git a/spec/ruby/language/proc_spec.rb b/spec/ruby/language/proc_spec.rb
index cc69b7799c..ef4a43bed6 100644
--- a/spec/ruby/language/proc_spec.rb
+++ b/spec/ruby/language/proc_spec.rb
@@ -161,18 +161,6 @@ describe "A Proc" do
end
end
- describe "taking |*a, b| arguments" do
- it "assigns [] to the argument when passed no values" do
- proc { |*a, b| [a, b] }.call.should == [[], nil]
- end
- end
-
- describe "taking |a, *b, c| arguments" do
- it "assigns [] to the argument when passed no values" do
- proc { |a, *b, c| [a, b, c] }.call.should == [nil, [], nil]
- end
- end
-
describe "taking |a, | arguments" do
before :each do
@l = lambda { |a, | a }
@@ -235,15 +223,24 @@ describe "A Proc" do
@p = proc { |*a, **kw| [a, kw] }
end
- it 'does not autosplat keyword arguments' do
- @p.call([1, {a: 1}]).should == [[[1, {a: 1}]], {}]
+ ruby_version_is ""..."2.7" do
+ it 'autosplats keyword arguments' do
+ @p.call([1, {a: 1}]).should == [[1], {a: 1}]
+ end
+ end
+
+ ruby_version_is "2.7"..."3.0" do
+ it 'autosplats keyword arguments and warns' do
+ -> {
+ @p.call([1, {a: 1}]).should == [[1], {a: 1}]
+ }.should complain(/warning: Using the last argument as keyword parameters is deprecated; maybe \*\* should be added to the call/)
+ end
end
- end
- describe "taking |required keyword arguments, **kw| arguments" do
- it "raises ArgumentError for missing required argument" do
- p = proc { |a:, **kw| [a, kw] }
- -> { p.call() }.should raise_error(ArgumentError)
+ ruby_version_is "3.0" do
+ it 'does not autosplat keyword arguments' do
+ @p.call([1, {a: 1}]).should == [[[1, {a: 1}]], {}]
+ end
end
end
end
diff --git a/spec/ruby/language/range_spec.rb b/spec/ruby/language/range_spec.rb
index ccc9f55537..4cde7e9488 100644
--- a/spec/ruby/language/range_spec.rb
+++ b/spec/ruby/language/range_spec.rb
@@ -10,21 +10,21 @@ describe "Literal Ranges" do
(1...10).should == Range.new(1, 10, true)
end
- it "creates a simple range as an object literal" do
- ary = []
- 2.times do
- ary.push(1..3)
- end
- ary[0].should.equal?(ary[1])
- end
-
it "creates endless ranges" do
(1..).should == Range.new(1, nil)
(1...).should == Range.new(1, nil, true)
end
- it "creates beginless ranges" do
- (..1).should == Range.new(nil, 1)
- (...1).should == Range.new(nil, 1, true)
+ ruby_version_is "3.0" do
+ it "is frozen" do
+ (42..).should.frozen?
+ 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/regexp/character_classes_spec.rb b/spec/ruby/language/regexp/character_classes_spec.rb
index 98d431a817..0cf1e9b6f4 100644
--- a/spec/ruby/language/regexp/character_classes_spec.rb
+++ b/spec/ruby/language/regexp/character_classes_spec.rb
@@ -609,13 +609,10 @@ describe "Regexp with character classes" do
"루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"]
end
- it "supports negated property condition" do
- "a".match(eval("/\P{L}/")).should be_nil
- "1".match(eval("/\P{N}/")).should be_nil
- end
-
- it "raises a RegexpError for an unterminated unicode property" do
- -> { Regexp.new('\p{') }.should raise_error(RegexpError)
+ ruby_bug "#17340", ''...'3.0' do
+ it "raises a RegexpError for an unterminated unicode property" do
+ -> { Regexp.new('\p{') }.should raise_error(RegexpError)
+ end
end
it "supports \\X (unicode 9.0 with UTR #51 workarounds)" do
diff --git a/spec/ruby/language/regexp/escapes_spec.rb b/spec/ruby/language/regexp/escapes_spec.rb
index 16a4d8c23b..2e5fe5ad2e 100644
--- a/spec/ruby/language/regexp/escapes_spec.rb
+++ b/spec/ruby/language/regexp/escapes_spec.rb
@@ -2,10 +2,8 @@
require_relative '../../spec_helper'
require_relative '../fixtures/classes'
-# TODO: synchronize with spec/core/regexp/new_spec.rb -
-# escaping is also tested there
describe "Regexps with escape characters" do
- it "supports escape sequences" do
+ it "they're supported" do
/\t/.match("\t").to_a.should == ["\t"] # horizontal tab
/\v/.match("\v").to_a.should == ["\v"] # vertical tab
/\n/.match("\n").to_a.should == ["\n"] # newline
@@ -17,7 +15,9 @@ describe "Regexps with escape characters" do
# \nnn octal char (encoded byte value)
end
- it "supports quoting meta-characters via escape sequence" do
+ it "support quoting meta-characters via escape sequence" do
+ /\\/.match("\\").to_a.should == ["\\"]
+ /\//.match("/").to_a.should == ["/"]
# parenthesis, etc
/\(/.match("(").to_a.should == ["("]
/\)/.match(")").to_a.should == [")"]
@@ -25,8 +25,6 @@ describe "Regexps with escape characters" do
/\]/.match("]").to_a.should == ["]"]
/\{/.match("{").to_a.should == ["{"]
/\}/.match("}").to_a.should == ["}"]
- /\</.match("<").to_a.should == ["<"]
- /\>/.match(">").to_a.should == [">"]
# alternation separator
/\|/.match("|").to_a.should == ["|"]
# quantifiers
@@ -39,81 +37,11 @@ describe "Regexps with escape characters" do
/\$/.match("$").to_a.should == ["$"]
end
- it "supports quoting meta-characters via escape sequence when used as a terminator" do
- # parenthesis, etc
- # %r[[, %r((, etc literals - are forbidden
- %r(\().match("(").to_a.should == ["("]
- %r(\)).match(")").to_a.should == [")"]
- %r)\().match("(").to_a.should == ["("]
- %r)\)).match(")").to_a.should == [")"]
-
- %r[\[].match("[").to_a.should == ["["]
- %r[\]].match("]").to_a.should == ["]"]
- %r]\[].match("[").to_a.should == ["["]
- %r]\]].match("]").to_a.should == ["]"]
-
- %r{\{}.match("{").to_a.should == ["{"]
- %r{\}}.match("}").to_a.should == ["}"]
- %r}\{}.match("{").to_a.should == ["{"]
- %r}\}}.match("}").to_a.should == ["}"]
-
- %r<\<>.match("<").to_a.should == ["<"]
- %r<\>>.match(">").to_a.should == [">"]
- %r>\<>.match("<").to_a.should == ["<"]
- %r>\>>.match(">").to_a.should == [">"]
-
- # alternation separator
- %r|\||.match("|").to_a.should == ["|"]
- # quantifiers
- %r?\??.match("?").to_a.should == ["?"]
- %r.\...match(".").to_a.should == ["."]
- %r*\**.match("*").to_a.should == ["*"]
- %r+\++.match("+").to_a.should == ["+"]
- # line anchors
- %r^\^^.match("^").to_a.should == ["^"]
- %r$\$$.match("$").to_a.should == ["$"]
- end
-
- it "supports quoting non-meta-characters via escape sequence when used as a terminator" do
- non_meta_character_terminators = [
- '!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`', '/', '=', '~'
- ]
-
- non_meta_character_terminators.each do |c|
- pattern = eval("%r" + c + "\\" + c + c)
- pattern.match(c).to_a.should == [c]
- end
- end
-
- it "does not change semantics of escaped non-meta-character when used as a terminator" do
- all_terminators = [*("!".."/"), *(":".."@"), *("[".."`"), *("{".."~")]
- meta_character_terminators = ["$", "^", "*", "+", ".", "?", "|", "}", ")", ">", "]"]
- special_cases = ['(', '{', '[', '<', '\\']
-
- # it should be equivalent to
- # [ '!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`', '/', '=', '~' ]
- non_meta_character_terminators = all_terminators - meta_character_terminators - special_cases
-
- non_meta_character_terminators.each do |c|
- pattern = eval("%r" + c + "\\" + c + c)
- pattern.should == /#{c}/
- end
- end
-
- it "does not change semantics of escaped meta-character when used as a terminator" do
- meta_character_terminators = ["$", "^", "*", "+", ".", "?", "|", "}", ")", ">", "]"]
-
- meta_character_terminators.each do |c|
- pattern = eval("%r" + c + "\\" + c + c)
- pattern.should == eval("/\\#{c}/")
- end
- end
-
it "allows any character to be escaped" do
/\y/.match("y").to_a.should == ["y"]
end
- it "supports \\x (hex characters)" do
+ it "support \\x (hex characters)" do
/\xA/.match("\nxyz").to_a.should == ["\n"]
/\x0A/.match("\n").to_a.should == ["\n"]
/\xAA/.match("\nA").should be_nil
@@ -125,7 +53,7 @@ describe "Regexps with escape characters" do
# \x{7HHHHHHH} wide hexadecimal char (character code point value)
end
- it "supports \\c (control characters)" do
+ it "support \\c (control characters)" do
#/\c \c@\c`/.match("\00\00\00").to_a.should == ["\00\00\00"]
/\c#\cc\cC/.match("\03\03\03").to_a.should == ["\03\03\03"]
/\c'\cG\cg/.match("\a\a\a").to_a.should == ["\a\a\a"]
diff --git a/spec/ruby/language/regexp/repetition_spec.rb b/spec/ruby/language/regexp/repetition_spec.rb
index d76619688f..9a191d74e2 100644
--- a/spec/ruby/language/regexp/repetition_spec.rb
+++ b/spec/ruby/language/regexp/repetition_spec.rb
@@ -87,7 +87,9 @@ describe "Regexps with repetition" do
/a+?*/.match("a")[0].should == "a"
/(a+?)*/.match("a")[0].should == "a"
- /a+?*/.match("aa")[0].should == "aa"
+ ruby_bug '#17341', ''...'3.0' do
+ /a+?*/.match("aa")[0].should == "aa"
+ end
/(a+?)*/.match("aa")[0].should == "aa"
# a+?+ should not be reduced, it should be equivalent to (a+?)+
@@ -98,7 +100,9 @@ describe "Regexps with repetition" do
/a+?+/.match("a")[0].should == "a"
/(a+?)+/.match("a")[0].should == "a"
- /a+?+/.match("aa")[0].should == "aa"
+ ruby_bug '#17341', ''...'3.0' do
+ /a+?+/.match("aa")[0].should == "aa"
+ end
/(a+?)+/.match("aa")[0].should == "aa"
# both a**? and a+*? should be equivalent to (a+)??
diff --git a/spec/ruby/language/regexp_spec.rb b/spec/ruby/language/regexp_spec.rb
index cdafd29f49..f607fa6010 100644
--- a/spec/ruby/language/regexp_spec.rb
+++ b/spec/ruby/language/regexp_spec.rb
@@ -18,8 +18,10 @@ describe "Literal Regexps" do
/Hello/.should be_kind_of(Regexp)
end
- it "is frozen" do
- /Hello/.should.frozen?
+ ruby_version_is "3.0" do
+ it "is frozen" do
+ /Hello/.should.frozen?
+ end
end
it "caches the Regexp object" do
@@ -94,6 +96,7 @@ describe "Literal Regexps" do
/./.match("\0").to_a.should == ["\0"]
end
+
it "supports | (alternations)" do
/a|b/.match("a").to_a.should == ["a"]
end
@@ -112,7 +115,7 @@ describe "Literal Regexps" do
/foo.(?<=\d)/.match("fooA foo1").to_a.should == ["foo1"]
end
- ruby_bug "#13671", ""..."3.4" do # https://bugs.ruby-lang.org/issues/13671
+ ruby_bug "#13671", ""..."3.2" do # https://bugs.ruby-lang.org/issues/13671
it "handles a lookbehind with ss characters" do
r = Regexp.new("(?<!dss)", Regexp::IGNORECASE)
r.should =~ "✨"
@@ -158,6 +161,26 @@ describe "Literal Regexps" do
pattern.should_not =~ 'T'
end
+ escapable_terminators = ['!', '"', '#', '%', '&', "'", ',', '-', ':', ';', '@', '_', '`']
+
+ it "supports escaping characters when used as a terminator" do
+ escapable_terminators.each do |c|
+ ref = "(?-mix:#{c})"
+ pattern = eval("%r" + c + "\\" + c + c)
+ pattern.to_s.should == ref
+ end
+ end
+
+ it "treats an escaped non-escapable character normally when used as a terminator" do
+ all_terminators = [*("!".."/"), *(":".."@"), *("[".."`"), *("{".."~")]
+ special_cases = ['(', '{', '[', '<', '\\', '=', '~']
+ (all_terminators - special_cases - escapable_terminators).each do |c|
+ ref = "(?-mix:\\#{c})"
+ pattern = eval("%r" + c + "\\" + c + c)
+ pattern.to_s.should == ref
+ end
+ end
+
it "support handling unicode 9.0 characters with POSIX bracket expressions" do
char_lowercase = "\u{104D8}" # OSAGE SMALL LETTER A
/[[:lower:]]/.match(char_lowercase).to_s.should == char_lowercase
diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb
index b91b52fa0f..4d164b38c6 100644
--- a/spec/ruby/language/rescue_spec.rb
+++ b/spec/ruby/language/rescue_spec.rb
@@ -115,18 +115,6 @@ describe "The rescue keyword" do
end
end
- it "converts the splatted list of exceptions using #to_a" do
- exceptions = mock("to_a")
- exceptions.should_receive(:to_a).and_return(exception_list)
- caught_it = false
- begin
- raise SpecificExampleException, "not important"
- rescue *exceptions
- caught_it = true
- end
- caught_it.should be_true
- end
-
it "can combine a splatted list of exceptions with a literal list of exceptions" do
caught_it = false
begin
@@ -504,12 +492,14 @@ describe "The rescue keyword" do
}.should raise_error(Exception)
end
- it "rescues with multiple assignment" do
+ ruby_version_is "2.7" do
+ it "rescues with multiple assignment" do
- a, b = raise rescue [1, 2]
+ a, b = raise rescue [1, 2]
- a.should == 1
- b.should == 2
+ a.should == 1
+ b.should == 2
+ end
end
end
end
diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb
index a62ed1242d..d8506834c8 100644
--- a/spec/ruby/language/return_spec.rb
+++ b/spec/ruby/language/return_spec.rb
@@ -422,31 +422,18 @@ describe "The return keyword" do
end
describe "within a block within a class" do
- it "is not allowed" do
- File.write(@filename, <<-END_OF_CODE)
- class ReturnSpecs::A
- ScratchPad << "before return"
- 1.times { return }
- ScratchPad << "after return"
- end
- END_OF_CODE
-
- -> { load @filename }.should raise_error(LocalJumpError)
- end
- end
-
- describe "within BEGIN" do
- it "is allowed" do
- File.write(@filename, <<-END_OF_CODE)
- BEGIN {
- ScratchPad << "before call"
- return
- ScratchPad << "after call"
- }
- END_OF_CODE
-
- load @filename
- ScratchPad.recorded.should == ["before call"]
+ ruby_version_is "2.7" do
+ it "is not allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ class ReturnSpecs::A
+ ScratchPad << "before return"
+ 1.times { return }
+ ScratchPad << "after return"
+ end
+ END_OF_CODE
+
+ -> { load @filename }.should raise_error(LocalJumpError)
+ end
end
end
@@ -477,13 +464,25 @@ describe "The return keyword" do
end
describe "return with argument" do
- it "warns but does not affect exit status" do
- err = ruby_exe(<<-END_OF_CODE, args: "2>&1")
- return 10
- END_OF_CODE
- $?.exitstatus.should == 0
+ ruby_version_is ""..."2.7" do
+ it "does not affect exit status" do
+ ruby_exe(<<-END_OF_CODE).should == ""
+ return 10
+ END_OF_CODE
+
+ $?.exitstatus.should == 0
+ end
+ end
+
+ ruby_version_is "2.7" do
+ it "warns but does not affect exit status" do
+ err = ruby_exe(<<-END_OF_CODE, args: "2>&1")
+ return 10
+ END_OF_CODE
+ $?.exitstatus.should == 0
- err.should =~ /warning: argument of top-level return is ignored/
+ err.should =~ /warning: argument of top-level return is ignored/
+ end
end
end
end
diff --git a/spec/ruby/language/safe_spec.rb b/spec/ruby/language/safe_spec.rb
index 03ae96148e..89830a2069 100644
--- a/spec/ruby/language/safe_spec.rb
+++ b/spec/ruby/language/safe_spec.rb
@@ -1,11 +1,119 @@
require_relative '../spec_helper'
describe "The $SAFE variable" do
- it "$SAFE is a regular global variable" do
- $SAFE.should == nil
- $SAFE = 42
- $SAFE.should == 42
- ensure
- $SAFE = nil
+ ruby_version_is ""..."2.7" do
+ after :each do
+ $SAFE = 0
+ end
+
+ it "is 0 by default" do
+ $SAFE.should == 0
+ proc {
+ $SAFE.should == 0
+ }.call
+ end
+
+ it "can be set to 0" do
+ proc {
+ $SAFE = 0
+ $SAFE.should == 0
+ }.call
+ end
+
+ it "can be set to 1" do
+ proc {
+ $SAFE = 1
+ $SAFE.should == 1
+ }.call
+ end
+
+ [2, 3, 4].each do |n|
+ it "cannot be set to #{n}" do
+ -> {
+ proc {
+ $SAFE = n
+ }.call
+ }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/)
+ end
+ end
+
+ it "raises ArgumentError when set to values below 0" do
+ -> {
+ proc {
+ $SAFE = -100
+ }.call
+ }.should raise_error(ArgumentError, "$SAFE should be >= 0")
+ end
+
+ it "cannot be set to values above 4" do
+ -> {
+ proc {
+ $SAFE = 100
+ }.call
+ }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/)
+ end
+
+ it "can be manually lowered" do
+ $SAFE = 1
+ $SAFE = 0
+ $SAFE.should == 0
+ end
+
+ it "is not Proc local" do
+ $SAFE.should == 0
+ proc {
+ $SAFE = 1
+ }.call
+ $SAFE.should == 1
+ end
+
+ it "is not lambda local" do
+ $SAFE.should == 0
+ -> {
+ $SAFE = 1
+ }.call
+ $SAFE.should == 1
+ end
+
+ it "is global like regular global variables" do
+ Thread.new { $SAFE }.value.should == 0
+ $SAFE = 1
+ Thread.new { $SAFE }.value.should == 1
+ end
+
+ it "can be read when default from Thread#safe_level" do
+ Thread.current.safe_level.should == 0
+ end
+
+ it "can be read when modified from Thread#safe_level" do
+ proc {
+ $SAFE = 1
+ Thread.current.safe_level.should == 1
+ }.call
+ end
+ end
+
+ ruby_version_is "2.7"..."3.0" do
+ it "warn when access" do
+ -> {
+ $SAFE
+ }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/)
+ end
+
+ it "warn when set" do
+ -> {
+ $SAFE = 1
+ }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/)
+ end
+ end
+
+ ruby_version_is "3.0" do
+ it "$SAFE is a regular global variable" do
+ $SAFE.should == nil
+ $SAFE = 42
+ $SAFE.should == 42
+ ensure
+ $SAFE = nil
+ end
end
end
diff --git a/spec/ruby/language/send_spec.rb b/spec/ruby/language/send_spec.rb
index a1656559fe..e57e2c65dc 100644
--- a/spec/ruby/language/send_spec.rb
+++ b/spec/ruby/language/send_spec.rb
@@ -258,10 +258,20 @@ describe "Invoking a private setter method" do
end
describe "Invoking a private getter method" do
- it "permits self as a receiver" do
- receiver = LangSendSpecs::PrivateGetter.new
- receiver.call_self_foo_or_equals(6)
- receiver.call_self_foo.should == 6
+ ruby_version_is ""..."2.7" do
+ it "does not permit self as a receiver" do
+ receiver = LangSendSpecs::PrivateGetter.new
+ -> { receiver.call_self_foo }.should raise_error(NoMethodError)
+ -> { receiver.call_self_foo_or_equals(6) }.should raise_error(NoMethodError)
+ end
+ end
+
+ ruby_version_is "2.7" do
+ it "permits self as a receiver" do
+ receiver = LangSendSpecs::PrivateGetter.new
+ receiver.call_self_foo_or_equals(6)
+ receiver.call_self_foo.should == 6
+ end
end
end
@@ -411,18 +421,36 @@ describe "Invoking a method" do
specs.rest_len(0,*a,4,*5,6,7,*c,-1).should == 11
end
- it "expands the Array elements from the splat before applying block argument operations" do
- def self.m(*args, &block)
- [args, block]
+ ruby_version_is ""..."3.0" do
+ it "expands the Array elements from the splat after executing the arguments and block if no other arguments follow the splat" do
+ def self.m(*args, &block)
+ [args, block]
+ end
+
+ args = [1, nil]
+ m(*args, &args.pop).should == [[1], nil]
+
+ args = [1, nil]
+ order = []
+ m(*(order << :args; args), &(order << :block; args.pop)).should == [[1], nil]
+ order.should == [:args, :block]
end
+ end
- args = [1, nil]
- m(*args, &args.pop).should == [[1, nil], nil]
+ ruby_version_is "3.0" do
+ it "expands the Array elements from the splat before applying block argument operations" do
+ def self.m(*args, &block)
+ [args, block]
+ end
- args = [1, nil]
- order = []
- m(*(order << :args; args), &(order << :block; args.pop)).should == [[1, nil], nil]
- order.should == [:args, :block]
+ args = [1, nil]
+ m(*args, &args.pop).should == [[1, nil], nil]
+
+ args = [1, nil]
+ order = []
+ m(*(order << :args; args), &(order << :block; args.pop)).should == [[1, nil], nil]
+ order.should == [:args, :block]
+ end
end
it "evaluates the splatted arguments before the block if there are other arguments after the splat" do
diff --git a/spec/ruby/language/singleton_class_spec.rb b/spec/ruby/language/singleton_class_spec.rb
index 9d037717b2..c1fb682ea0 100644
--- a/spec/ruby/language/singleton_class_spec.rb
+++ b/spec/ruby/language/singleton_class_spec.rb
@@ -291,27 +291,3 @@ describe "Instantiating a singleton class" do
}.should raise_error(TypeError)
end
end
-
-describe "Frozen properties" do
- it "is frozen if the object it is created from is frozen" do
- o = Object.new
- o.freeze
- klass = o.singleton_class
- klass.frozen?.should == true
- end
-
- it "will be frozen if the object it is created from becomes frozen" do
- o = Object.new
- klass = o.singleton_class
- klass.frozen?.should == false
- o.freeze
- klass.frozen?.should == true
- end
-
- it "will be unfrozen if the frozen object is cloned with freeze set to false" do
- o = Object.new
- o.freeze
- o2 = o.clone(freeze: false)
- o2.singleton_class.frozen?.should == false
- end
-end
diff --git a/spec/ruby/language/source_encoding_spec.rb b/spec/ruby/language/source_encoding_spec.rb
index 7135bc0a70..19364fc676 100644
--- a/spec/ruby/language/source_encoding_spec.rb
+++ b/spec/ruby/language/source_encoding_spec.rb
@@ -15,7 +15,7 @@ describe "Source files" do
end
describe "encoded in UTF-16 LE without a BOM" do
- it "are parsed as empty because they contain a NUL byte before the encoding comment" do
+ it "are parsed because empty as they contain a NUL byte before the encoding comment" do
ruby_exe(fixture(__FILE__, "utf16-le-nobom.rb"), args: "2>&1").should == ""
end
end
diff --git a/spec/ruby/language/string_spec.rb b/spec/ruby/language/string_spec.rb
index 418dc2ca7d..ce4941569e 100644
--- a/spec/ruby/language/string_spec.rb
+++ b/spec/ruby/language/string_spec.rb
@@ -56,6 +56,28 @@ describe "Ruby character strings" do
"#\$".should == '#$'
end
+ ruby_version_is ''...'2.7' do
+ it "taints the result of interpolation when an interpolated value is tainted" do
+ "#{"".taint}".tainted?.should be_true
+
+ @ip.taint
+ "#@ip".tainted?.should be_true
+
+ $ip.taint
+ "#$ip".tainted?.should be_true
+ end
+
+ it "untrusts the result of interpolation when an interpolated value is untrusted" do
+ "#{"".untrust}".untrusted?.should be_true
+
+ @ip.untrust
+ "#@ip".untrusted?.should be_true
+
+ $ip.untrust
+ "#$ip".untrusted?.should be_true
+ end
+ end
+
it "allows using non-alnum characters as string delimiters" do
%(hey #{@ip}).should == "hey xxx"
%[hey #{@ip}].should == "hey xxx"
@@ -277,11 +299,23 @@ describe "Ruby String interpolation" do
eval(code).should_not.frozen?
end
- it "creates a non-frozen String when # frozen-string-literal: true is used" do
- code = <<~'RUBY'
- # frozen-string-literal: true
- "a#{6*7}c"
- RUBY
- eval(code).should_not.frozen?
+ ruby_version_is "3.0" do
+ it "creates a non-frozen String when # frozen-string-literal: true is used" do
+ code = <<~'RUBY'
+ # frozen-string-literal: true
+ "a#{6*7}c"
+ RUBY
+ eval(code).should_not.frozen?
+ end
+ end
+
+ ruby_version_is ""..."3.0" do
+ it "creates a frozen String when # frozen-string-literal: true is used" do
+ code = <<~'RUBY'
+ # frozen-string-literal: true
+ "a#{6*7}c"
+ RUBY
+ eval(code).should.frozen?
+ end
end
end
diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb
index d22c603605..1ac5c5e1be 100644
--- a/spec/ruby/language/super_spec.rb
+++ b/spec/ruby/language/super_spec.rb
@@ -203,25 +203,6 @@ describe "The super keyword" do
-> { klass.new.a(:a_called) }.should raise_error(RuntimeError)
end
- it "is able to navigate to super, when a method is defined dynamically on the singleton class" do
- foo_class = Class.new do
- def bar
- "bar"
- end
- end
-
- mixin_module = Module.new do
- def bar
- "super_" + super
- end
- end
-
- foo = foo_class.new
- foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar))
-
- foo.bar.should == "super_bar"
- end
-
# Rubinius ticket github#157
it "calls method_missing when a superclass method is not found" do
SuperSpecs::MM_B.new.is_a?(Hash).should == false
@@ -341,10 +322,6 @@ describe "The super keyword" do
SuperSpecs::ZSuperWithUnderscores::B.new.m_modified(1, 2).should == [14, 2]
end
- it "should pass method arguments when called within a closure" do
- SuperSpecs::ZSuperInBlock::B.new.m(arg: 1).should == 1
- end
-
describe 'when using keyword arguments' do
before :each do
@req = SuperSpecs::Keywords::RequiredArguments.new
diff --git a/spec/ruby/language/symbol_spec.rb b/spec/ruby/language/symbol_spec.rb
index 7c1898efc2..d6a41d3059 100644
--- a/spec/ruby/language/symbol_spec.rb
+++ b/spec/ruby/language/symbol_spec.rb
@@ -96,11 +96,11 @@ describe "A Symbol literal" do
%I{a b #{"c"}}.should == [:a, :b, :c]
end
- it "raises an EncodingError at parse time when Symbol with invalid bytes" do
+ it "with invalid bytes raises an EncodingError at parse time" do
ScratchPad.record []
-> {
eval 'ScratchPad << 1; :"\xC3"'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
+ }.should raise_error(EncodingError, /invalid/)
ScratchPad.recorded.should == []
end
end
diff --git a/spec/ruby/language/undef_spec.rb b/spec/ruby/language/undef_spec.rb
index 29dba4afb4..4e473b803f 100644
--- a/spec/ruby/language/undef_spec.rb
+++ b/spec/ruby/language/undef_spec.rb
@@ -38,19 +38,12 @@ describe "The undef keyword" do
-> { @obj.meth(5) }.should raise_error(NoMethodError)
end
- it "with an interpolated symbol" do
+ it "with a interpolated symbol" do
@undef_class.class_eval do
undef :"#{'meth'}"
end
-> { @obj.meth(5) }.should raise_error(NoMethodError)
end
-
- it "with an interpolated symbol when interpolated expression is not a String literal" do
- @undef_class.class_eval do
- undef :"#{'meth'.to_sym}"
- end
- -> { @obj.meth(5) }.should raise_error(NoMethodError)
- end
end
it "allows undefining multiple methods at a time" do
diff --git a/spec/ruby/language/variables_spec.rb b/spec/ruby/language/variables_spec.rb
index 23c2cdb557..699187335c 100644
--- a/spec/ruby/language/variables_spec.rb
+++ b/spec/ruby/language/variables_spec.rb
@@ -1,86 +1,6 @@
require_relative '../spec_helper'
require_relative 'fixtures/variables'
-describe "Evaluation order during assignment" do
- context "with single assignment" do
- it "evaluates from left to right" do
- obj = VariablesSpecs::EvalOrder.new
- obj.instance_eval do
- foo[0] = a
- end
-
- obj.order.should == ["foo", "a", "foo[]="]
- end
- end
-
- context "with multiple assignment" do
- ruby_version_is ""..."3.1" do
- it "does not evaluate from left to right" do
- obj = VariablesSpecs::EvalOrder.new
-
- obj.instance_eval do
- foo[0], bar.baz = a, b
- end
-
- obj.order.should == ["a", "b", "foo", "foo[]=", "bar", "bar.baz="]
- end
-
- it "cannot be used to swap variables with nested method calls" do
- node = VariablesSpecs::EvalOrder.new.node
-
- original_node = node
- original_node_left = node.left
- original_node_left_right = node.left.right
-
- node.left, node.left.right, node = node.left.right, node, node.left
- # Should evaluate in the order of:
- # RHS: node.left.right, node, node.left
- # LHS:
- # * node(original_node), original_node.left = original_node_left_right
- # * node(original_node), node.left(changed in the previous assignment to original_node_left_right),
- # original_node_left_right.right = original_node
- # * node = original_node_left
-
- node.should == original_node_left
- node.right.should_not == original_node
- node.right.left.should_not == original_node_left_right
- end
- end
-
- ruby_version_is "3.1" do
- it "evaluates from left to right, receivers first then methods" do
- obj = VariablesSpecs::EvalOrder.new
- obj.instance_eval do
- foo[0], bar.baz = a, b
- end
-
- obj.order.should == ["foo", "bar", "a", "b", "foo[]=", "bar.baz="]
- end
-
- it "can be used to swap variables with nested method calls" do
- node = VariablesSpecs::EvalOrder.new.node
-
- original_node = node
- original_node_left = node.left
- original_node_left_right = node.left.right
-
- node.left, node.left.right, node = node.left.right, node, node.left
- # Should evaluate in the order of:
- # LHS: node, node.left(original_node_left)
- # RHS: original_node_left_right, original_node, original_node_left
- # Ops:
- # * node(original_node), original_node.left = original_node_left_right
- # * original_node_left.right = original_node
- # * node = original_node_left
-
- node.should == original_node_left
- node.right.should == original_node
- node.right.left.should == original_node_left_right
- end
- end
- end
-end
-
describe "Multiple assignment" do
context "with a single RHS value" do
it "assigns a simple MLHS" do
@@ -877,6 +797,17 @@ describe 'Local variable shadowing' do
end
describe 'Allowed characters' do
+ # new feature in 2.6 -- https://bugs.ruby-lang.org/issues/13770
+ it 'does not allow non-ASCII upcased characters at the beginning' do
+ -> do
+ eval <<-CODE
+ def test
+ ἍBB = 1
+ end
+ CODE
+ end.should raise_error(SyntaxError, /dynamic constant assignment/)
+ end
+
it 'allows non-ASCII lowercased characters at the beginning' do
result = nil
@@ -890,50 +821,33 @@ describe 'Allowed characters' do
result.should == 1
end
-
- it 'parses a non-ASCII upcased character as a constant identifier' do
- -> do
- eval <<-CODE
- def test
- ἍBB = 1
- end
- CODE
- end.should raise_error(SyntaxError, /dynamic constant assignment/)
- end
end
describe "Instance variables" do
context "when instance variable is uninitialized" do
- it "doesn't warn about accessing uninitialized instance variable" do
- obj = Object.new
- def obj.foobar; a = @a; end
-
- -> { obj.foobar }.should_not complain(verbose: true)
- end
-
- it "doesn't warn at lazy initialization" do
- obj = Object.new
- def obj.foobar; @a ||= 42; end
-
- -> { obj.foobar }.should_not complain(verbose: true)
- end
- end
-
- describe "global variable" do
- context "when global variable is uninitialized" do
- it "warns about accessing uninitialized global variable in verbose mode" do
+ ruby_version_is ""..."3.0" do
+ it "warns about accessing uninitialized instance variable" do
obj = Object.new
- def obj.foobar; a = $specs_uninitialized_global_variable; end
+ def obj.foobar; a = @a; end
- -> { obj.foobar }.should complain(/warning: global variable `\$specs_uninitialized_global_variable' not initialized/, verbose: true)
+ -> { obj.foobar }.should complain(/warning: instance variable @a not initialized/, verbose: true)
end
+ end
- it "doesn't warn at lazy initialization" do
+ ruby_version_is "3.0" do
+ it "doesn't warn about accessing uninitialized instance variable" do
obj = Object.new
- def obj.foobar; $specs_uninitialized_global_variable_lazy ||= 42; end
+ def obj.foobar; a = @a; end
-> { obj.foobar }.should_not complain(verbose: true)
end
end
+
+ it "doesn't warn at lazy initialization" do
+ obj = Object.new
+ def obj.foobar; @a ||= 42; end
+
+ -> { obj.foobar }.should_not complain(verbose: true)
+ end
end
end
diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb
index 5283517636..3db6d353a9 100644
--- a/spec/ruby/language/yield_spec.rb
+++ b/spec/ruby/language/yield_spec.rb
@@ -186,23 +186,30 @@ describe "The yield call" do
end
describe "Using yield in a singleton class literal" do
- it 'raises a SyntaxError' do
- code = <<~RUBY
- class << Object.new
- yield
- end
- RUBY
+ ruby_version_is "2.7"..."3.0" do
+ it 'emits a deprecation warning' do
+ code = <<~RUBY
+ def m
+ class << Object.new
+ yield
+ end
+ end
+ m { :ok }
+ RUBY
- -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
+ -> { eval(code) }.should complain(/warning: `yield' in class syntax will not be supported from Ruby 3.0/)
+ end
end
-end
-describe "Using yield in non-lambda block" do
- it 'raises a SyntaxError' do
- code = <<~RUBY
- 1.times { yield }
+ ruby_version_is "3.0" do
+ it 'raises a SyntaxError' do
+ code = <<~RUBY
+ class << Object.new
+ yield
+ end
RUBY
- -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
+ -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
+ end
end
end