diff options
Diffstat (limited to 'spec/ruby/language/method_spec.rb')
| -rw-r--r-- | spec/ruby/language/method_spec.rb | 390 |
1 files changed, 296 insertions, 94 deletions
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index 5f42c52341..324bd6cea5 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -19,7 +19,7 @@ describe "A method send" do x = mock("splat argument") x.should_not_receive(:to_ary) - m(*x).should equal(x) + m(*x).should.equal?(x) end it "calls #to_a" do @@ -40,7 +40,7 @@ describe "A method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { m(*x) }.should raise_error(TypeError) + -> { m(*x) }.should.raise(TypeError) end end @@ -74,7 +74,7 @@ describe "A method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { m(*x, 2, 3) }.should raise_error(TypeError) + -> { m(*x, 2, 3) }.should.raise(TypeError) end end @@ -108,13 +108,13 @@ describe "A method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { m(1, *x, 2, 3) }.should raise_error(TypeError) + -> { m(1, *x, 2, 3) }.should.raise(TypeError) end it "copies the splatted array" do args = [3, 4] m(1, 2, *args, 4, 5).should == [1, 2, [3, 4], 4, 5] - m(1, 2, *args, 4, 5)[2].should_not equal(args) + m(1, 2, *args, 4, 5)[2].should_not.equal?(args) end it "allows an array being splatted to be modified by another argument" do @@ -153,7 +153,7 @@ describe "A method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { m(1, 2, *x) }.should raise_error(TypeError) + -> { m(1, 2, *x) }.should.raise(TypeError) end end @@ -217,7 +217,7 @@ describe "An element assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o[*x] = 1 }.should raise_error(TypeError) + -> { @o[*x] = 1 }.should.raise(TypeError) end end @@ -255,7 +255,7 @@ describe "An element assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o[*x, 2, 3] = 4 }.should raise_error(TypeError) + -> { @o[*x, 2, 3] = 4 }.should.raise(TypeError) end end @@ -293,7 +293,7 @@ describe "An element assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o[1, 2, *x, 3] = 4 }.should raise_error(TypeError) + -> { @o[1, 2, *x, 3] = 4 }.should.raise(TypeError) end end @@ -331,7 +331,7 @@ describe "An element assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o[1, 2, 3, *x] = 4 }.should raise_error(TypeError) + -> { @o[1, 2, 3, *x] = 4 }.should.raise(TypeError) end end end @@ -368,7 +368,7 @@ describe "An attribute assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o.send :m=, *x, 1 }.should raise_error(TypeError) + -> { @o.send :m=, *x, 1 }.should.raise(TypeError) end end @@ -403,7 +403,7 @@ describe "An attribute assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o.send :m=, *x, 2, 3, 4 }.should raise_error(TypeError) + -> { @o.send :m=, *x, 2, 3, 4 }.should.raise(TypeError) end end @@ -438,7 +438,7 @@ describe "An attribute assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o.send :m=, 1, 2, *x, 3, 4 }.should raise_error(TypeError) + -> { @o.send :m=, 1, 2, *x, 3, 4 }.should.raise(TypeError) end end @@ -473,7 +473,7 @@ describe "An attribute assignment method send" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) - -> { @o.send :m=, 1, 2, 3, *x, 4 }.should raise_error(TypeError) + -> { @o.send :m=, 1, 2, 3, *x, 4 }.should.raise(TypeError) end end end @@ -487,7 +487,7 @@ describe "A method" do end ruby - m.should be_nil + m.should == nil end evaluate <<-ruby do @@ -495,7 +495,7 @@ describe "A method" do end ruby - m.should be_nil + m.should == nil end end @@ -504,7 +504,7 @@ describe "A method" do def m(a) a end ruby - m((args = 1, 2, 3)).should equal(args) + m((args = 1, 2, 3)).should.equal?(args) end evaluate <<-ruby do @@ -535,18 +535,18 @@ describe "A method" do def m() end ruby - m().should be_nil - m(*[]).should be_nil - m(**{}).should be_nil + m().should == nil + m(*[]).should == nil + m(**{}).should == nil end evaluate <<-ruby do def m(*) end ruby - m().should be_nil - m(1).should be_nil - m(1, 2, 3).should be_nil + m().should == nil + m(1).should == nil + m(1, 2, 3).should == nil end evaluate <<-ruby do @@ -564,10 +564,10 @@ describe "A method" do def m(a:) a end ruby - -> { m() }.should raise_error(ArgumentError) + -> { m() }.should.raise(ArgumentError) m(a: 1).should == 1 suppress_keyword_warning do - -> { m("a" => 1, a: 1) }.should raise_error(ArgumentError) + -> { m("a" => 1, a: 1) }.should.raise(ArgumentError) end end @@ -575,7 +575,7 @@ describe "A method" do def m(a:, **kw) [a, kw] end ruby - -> { m(b: 1) }.should raise_error(ArgumentError) + -> { m(b: 1) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -590,9 +590,9 @@ describe "A method" do def m(**) end ruby - m().should be_nil - m(a: 1, b: 2).should be_nil - -> { m(1) }.should raise_error(ArgumentError) + m().should == nil + m(a: 1, b: 2).should == nil + -> { m(1) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -606,7 +606,7 @@ describe "A method" do suppress_warning { eval "m(**{a: 1, b: 2}, **{a: 4, c: 7})" }.should == { a: 4, b: 2, c: 7 } - -> { m(2) }.should raise_error(ArgumentError) + -> { m(2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -620,7 +620,7 @@ describe "A method" do def m(&b) b end ruby - m { }.should be_an_instance_of(Proc) + m { }.should.instance_of?(Proc) end evaluate <<-ruby do @@ -650,9 +650,9 @@ describe "A method" do def m((*), (*)) end ruby - m(2, 3).should be_nil - m([2, 3, 4], [5, 6]).should be_nil - -> { m a: 1 }.should raise_error(ArgumentError) + m(2, 3).should == nil + m([2, 3, 4], [5, 6]).should == nil + -> { m a: 1 }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -745,7 +745,7 @@ describe "A method" do m(1, b: 2).should == [1, 2] suppress_keyword_warning do - -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, b: 2) }.should.raise(ArgumentError) end end @@ -755,7 +755,7 @@ describe "A method" do m(2).should == [2, 1] m(1, b: 2).should == [1, 2] - -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, b: 2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -764,7 +764,7 @@ describe "A method" do m(1).should == 1 m(1, a: 2, b: 3).should == 1 - -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, b: 2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -773,7 +773,7 @@ describe "A method" do 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("a" => 1, b: 2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -848,8 +848,8 @@ describe "A method" do def m(a=1, (*b), (*c)) [a, b, c] end ruby - -> { m() }.should raise_error(ArgumentError) - -> { m(2) }.should raise_error(ArgumentError) + -> { m() }.should.raise(ArgumentError) + -> { m(2) }.should.raise(ArgumentError) m(2, 3).should == [1, [2], [3]] m(2, [3, 4], [5, 6]).should == [2, [3, 4], [5, 6]] end @@ -890,7 +890,7 @@ describe "A method" do m(b: 2).should == [1, 2] m(2, b: 1).should == [2, 1] - -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, b: 2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -900,7 +900,7 @@ describe "A method" do 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("a" => 1, b: 2) }.should.raise(ArgumentError) end evaluate <<-ruby do @@ -953,9 +953,9 @@ describe "A method" do def m(*, &b) b end ruby - m().should be_nil - m(1, 2, 3, 4).should be_nil - m(&(l = ->{})).should equal(l) + m().should == nil + m(1, 2, 3, 4).should == nil + m(&(l = ->{})).should.equal?(l) end evaluate <<-ruby do @@ -973,7 +973,7 @@ describe "A method" do m(a: 1, b: 2).should == [1, 2] suppress_keyword_warning do - -> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, a: 1, b: 2) }.should.raise(ArgumentError) end end @@ -984,7 +984,7 @@ describe "A method" do m(a: 1).should == [1, 1] m(a: 1, b: 2).should == [1, 2] suppress_keyword_warning do - -> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + -> { m("a" => 1, a: 1, b: 2) }.should.raise(ArgumentError) end end @@ -1101,15 +1101,25 @@ describe "A method" do end evaluate <<-ruby do + def m(**nil); :ok; end; + ruby + + m().should == :ok + -> { m(a: 1) }.should.raise(ArgumentError, 'no keywords accepted') + -> { m(**{a: 1}) }.should.raise(ArgumentError, 'no keywords accepted') + -> { m("a" => 1) }.should.raise(ArgumentError, 'no keywords accepted') + end + + 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 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(ArgumentError, 'no keywords accepted') + -> { m(**{a: 1}) }.should.raise(ArgumentError, 'no keywords accepted') + -> { m("a" => 1) }.should.raise(ArgumentError, 'no keywords accepted') end evaluate <<-ruby do @@ -1127,6 +1137,18 @@ describe "A method" do result = m(1, {foo: :bar}) result.should == [1, nil, nil, {foo: :bar}, nil, {}] end + + ruby_version_is "4.1" do + evaluate <<-ruby do + def m(a, &nil); a end; + ruby + + m(1).should == 1 + + -> { m(1) {} }.should.raise(ArgumentError, 'no block accepted') + -> { m(1, &proc {}) }.should.raise(ArgumentError, 'no block accepted') + end + end end context 'when passing an empty keyword splat to a method that does not accept keywords' do @@ -1147,7 +1169,7 @@ describe "A method" do -> do m(**h).should == {} - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end end @@ -1159,7 +1181,7 @@ describe "A method" do options = {a: 1}.freeze -> do m(options) - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end end @@ -1175,6 +1197,31 @@ describe "A method" do end end +context "when passing **nil into a method that accepts keyword arguments" do + ruby_version_is ""..."3.4" do + it "raises TypeError" do + def m(**kw) kw; end + + h = nil + -> { m(a: 1, **h) }.should.raise(TypeError, "no implicit conversion of nil into Hash") + -> { m(a: 1, **nil) }.should.raise(TypeError, "no implicit conversion of nil into Hash") + end + end + + ruby_version_is "3.4" do + it "expands nil using ** into {}" do + def m(**kw) kw; end + + h = nil + m(**h).should == {} + m(a: 1, **h).should == {a: 1} + + m(**nil).should == {} + m(a: 1, **nil).should == {a: 1} + end + end +end + describe "A method call with a space between method name and parentheses" do before(:each) do def m(*args) @@ -1209,10 +1256,8 @@ describe "A method call with a space between method name and parentheses" do args.should == [true] end - ruby_version_is "3.3" do - it "supports multiple statements" do - eval("m (1; 2)").should == [2] - end + it "supports multiple statements" do + eval("m (1; 2)").should == [2] end end @@ -1232,11 +1277,11 @@ describe "A method call with a space between method name and parentheses" do it "raises a syntax error" do -> { eval("m (1, 2)") - }.should raise_error(SyntaxError) + }.should.raise(SyntaxError) -> { eval("m (1, 2, 3)") - }.should raise_error(SyntaxError) + }.should.raise(SyntaxError) end end @@ -1335,6 +1380,7 @@ describe "An endless method definition" do end end + # tested more thoroughly in language/delegation_spec.rb context "with args forwarding" do evaluate <<-ruby do def mm(word, num:) @@ -1385,7 +1431,7 @@ describe "Keyword arguments are now separated from positional arguments" do -> { foo(1, 2, 3, { key: 42 }) - }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)') + }.should.raise(ArgumentError, 'wrong number of arguments (given 4, expected 3)') end end @@ -1398,7 +1444,7 @@ describe "Keyword arguments are now separated from positional arguments" do -> { foo(1, 2, 3, { key: 42 }) - }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)') + }.should.raise(ArgumentError, 'wrong number of arguments (given 4, expected 3)') end end @@ -1415,53 +1461,209 @@ describe "Keyword arguments are now separated from positional arguments" do end end -ruby_version_is "3.1" do - describe "kwarg with omitted value in a method call" do - context "accepts short notation 'kwarg' in method call" do - evaluate <<-ruby do - def call(*args, **kwargs) = [args, kwargs] - ruby +describe "kwarg with omitted value in a method call" do + context "accepts short notation 'kwarg' in method call" do + evaluate <<-ruby do + def call(*args, **kwargs) = [args, kwargs] + ruby + + a, b, c = 1, 2, 3 + arr, h = call(a:) + h.should == {a: 1} + arr.should == [] + + arr, h = call(a:, b:, c:) + h.should == {a: 1, b: 2, c: 3} + arr.should == [] - a, b, c = 1, 2, 3 - arr, h = eval('call a:') - h.should == {a: 1} - arr.should == [] + arr, h = call(a:, b: 10, c:) + h.should == {a: 1, b: 10, c: 3} + arr.should == [] + end + end + + context "with methods and local variables" do + evaluate <<-ruby do + def call(*args, **kwargs) = [args, kwargs] - arr, h = eval('call(a:, b:, c:)') - h.should == {a: 1, b: 2, c: 3} - arr.should == [] + def bar + "baz" + end + + def foo(val) + call bar:, val: + end + ruby - arr, h = eval('call(a:, b: 10, c:)') - h.should == {a: 1, b: 10, c: 3} - arr.should == [] + foo(1).should == [[], {bar: "baz", val: 1}] + end + end +end + +describe "Inside 'endless' method definitions" do + it "allows method calls without parenthesis" do + def greet(person) = "Hi, ".dup.concat person + + greet("Homer").should == "Hi, Homer" + end +end + +describe "warning about not used block argument" do + ruby_version_is "3.4" do + it "warns when passing a block argument to a method that never uses it" do + def m_that_does_not_use_block + 42 end + + -> { + m_that_does_not_use_block { } + }.should complain( + /#{__FILE__}:#{__LINE__ - 2}: warning: the block passed to 'm_that_does_not_use_block' defined at #{__FILE__}:#{__LINE__ - 7} may be ignored/, + verbose: true) end - context "with methods and local variables" do - evaluate <<-ruby do - def call(*args, **kwargs) = [args, kwargs] + it "does not warn when passing a block argument to a method that declares a block parameter" do + def m_with_block_parameter(&block) + 42 + end - def bar - "baz" - end + -> { m_with_block_parameter { } }.should_not complain(verbose: true) + end - def foo(val) - call bar:, val: - end - ruby + it "does not warn when passing a block argument to a method that declares an anonymous block parameter" do + def m_with_anonymous_block_parameter(&) + 42 + end - foo(1).should == [[], {bar: "baz", val: 1}] + -> { m_with_anonymous_block_parameter { } }.should_not complain(verbose: true) + end + + it "does not warn when passing a block argument to a method that yields an implicit block parameter" do + def m_with_yield + yield 42 end + + -> { m_with_yield { } }.should_not complain(verbose: true) end - end - describe "Inside 'endless' method definitions" do - it "allows method calls without parenthesis" do - eval <<-ruby - def greet(person) = "Hi, ".concat person - ruby + it "warns when passing a block argument to a method that calls #block_given?" do + def m_with_block_given + block_given? + end + + -> { + m_with_block_given { } + }.should complain( + /#{__FILE__}:#{__LINE__ - 2}: warning: the block passed to 'm_with_block_given' defined at #{__FILE__}:#{__LINE__ - 7} may be ignored/, + verbose: true) + end + + it "does not warn when passing a block argument to a method that calls super" do + parent = Class.new do + def m + end + end + + child = Class.new(parent) do + def m + super + end + end + + obj = child.new + -> { obj.m { } }.should_not complain(verbose: true) + end - greet("Homer").should == "Hi, Homer" + it "does not warn when passing a block argument to a method that calls super(...)" do + parent = Class.new do + def m(a) + end + end + + child = Class.new(parent) do + def m(...) + super(...) + end + end + + obj = child.new + -> { obj.m(42) { } }.should_not complain(verbose: true) + end + + it "does not warn when called #initialize()" do + klass = Class.new do + def initialize + end + end + + -> { klass.new {} }.should_not complain(verbose: true) + end + + it "does not warn when passing a block argument to a method that calls super()" do + parent = Class.new do + def m + end + end + + child = Class.new(parent) do + def m + super() + end + end + + obj = child.new + -> { obj.m { } }.should_not complain(verbose: true) + end + + it "warns only once per call site" do + def m_that_does_not_use_block + 42 + end + + def call_m_that_does_not_use_block + m_that_does_not_use_block {} + end + + -> { + m_that_does_not_use_block { } + }.should complain(/the block passed to 'm_that_does_not_use_block' defined at .+ may be ignored/, verbose: true) + + -> { + m_that_does_not_use_block { } + }.should_not complain(verbose: true) + end + + it "can be disabled with :strict_unused_block warning category" do + def m_that_does_not_use_block + 42 + end + + # ensure that warning is emitted + -> { m_that_does_not_use_block { } }.should complain(verbose: true) + + warn_strict_unused_block = Warning[:strict_unused_block] + Warning[:strict_unused_block] = false + begin + -> { m_that_does_not_use_block { } }.should_not complain(verbose: true) + ensure + Warning[:strict_unused_block] = warn_strict_unused_block + end + end + + it "can be enabled with :strict_unused_block = true warning category in not verbose mode" do + def m_that_does_not_use_block + 42 + end + + warn_strict_unused_block = Warning[:strict_unused_block] + Warning[:strict_unused_block] = true + begin + -> { + m_that_does_not_use_block { } + }.should complain(/the block passed to 'm_that_does_not_use_block' defined at .+ may be ignored/) + ensure + Warning[:strict_unused_block] = warn_strict_unused_block + end end end end |
