require_relative '../spec_helper' describe "A method send" do evaluate <<-ruby do def m(a) a end ruby a = b = m 1 a.should == 1 b.should == 1 end context "with a single splatted Object argument" do before :all do def m(a) a end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) m(*x).should equal(x) end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1]) m(*x).should == 1 end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) m(*x).should == x end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { m(*x) }.should raise_error(TypeError) end end context "with a leading splatted Object argument" do before :all do def m(a, b, *c, d, e) [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) m(*x, 1, 2, 3).should == [x, 1, [], 2, 3] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1]) m(*x, 2, 3, 4).should == [1, 2, [], 3, 4] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) m(*x, 2, 3, 4).should == [x, 2, [], 3, 4] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { m(*x, 2, 3) }.should raise_error(TypeError) end end context "with a middle splatted Object argument" do before :all do def m(a, b, *c, d, e) [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) m(1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([5, 6, 7]) m(1, 2, *x, 3).should == [1, 2, [5, 6], 7, 3] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) m(1, 2, *x, 4).should == [1, 2, [], x, 4] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { m(1, *x, 2, 3) }.should raise_error(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) end it "allows an array being splatted to be modified by another argument" do args = [3, 4] m(1, args.shift, *args, 4, 5).should == [1, 3, [4], 4, 5] end end context "with a trailing splatted Object argument" do before :all do def m(a, *b, c) [a, b, c] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) m(1, 2, *x).should == [1, [2], x] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([5, 6, 7]) m(1, 2, *x).should == [1, [2, 5, 6], 7] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) m(1, 2, *x, 4).should == [1, [2, x], 4] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { m(1, 2, *x) }.should raise_error(TypeError) end end context "with a block argument" do before :all do def m(x) if block_given? [true, yield(x + 'b')] else [false] end end end it "that refers to a proc passes the proc as the block" do m('a', &-> y { y + 'c'}).should == [true, 'abc'] end it "that is nil passes no block" do m('a', &nil).should == [false] end end end describe "An element assignment method send" do before :each do ScratchPad.clear end context "with a single splatted Object argument" do before :all do @o = mock("element set receiver") def @o.[]=(a, b) ScratchPad.record [a, b] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o[*x] = 1).should == 1 ScratchPad.recorded.should == [x, 1] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1]) (@o[*x] = 2).should == 2 ScratchPad.recorded.should == [1, 2] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o[*x] = 1).should == 1 ScratchPad.recorded.should == [x, 1] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o[*x] = 1 }.should raise_error(TypeError) end end context "with a leading splatted Object argument" do before :all do @o = mock("element set receiver") def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o[*x, 2, 3, 4] = 1).should == 1 ScratchPad.recorded.should == [x, 2, [3], 4, 1] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1, 2, 3]) (@o[*x, 4, 5] = 6).should == 6 ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o[*x, 2, 3, 4] = 5).should == 5 ScratchPad.recorded.should == [x, 2, [3], 4, 5] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o[*x, 2, 3] = 4 }.should raise_error(TypeError) end end context "with a middle splatted Object argument" do before :all do @o = mock("element set receiver") def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o[1, *x, 2, 3] = 4).should == 4 ScratchPad.recorded.should == [1, x, [2], 3, 4] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([2, 3]) (@o[1, *x, 4] = 5).should == 5 ScratchPad.recorded.should == [1, 2, [3], 4, 5] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o[1, 2, *x, 3] = 4).should == 4 ScratchPad.recorded.should == [1, 2, [x], 3, 4] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o[1, 2, *x, 3] = 4 }.should raise_error(TypeError) end end context "with a trailing splatted Object argument" do before :all do @o = mock("element set receiver") def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o[1, 2, 3, 4, *x] = 5).should == 5 ScratchPad.recorded.should == [1, 2, [3, 4], x, 5] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([4, 5]) (@o[1, 2, 3, *x] = 6).should == 6 ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o[1, 2, 3, *x] = 4).should == 4 ScratchPad.recorded.should == [1, 2, [3], x, 4] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o[1, 2, 3, *x] = 4 }.should raise_error(TypeError) end end end describe "An attribute assignment method send" do context "with a single splatted Object argument" do before :all do @o = mock("element set receiver") def @o.m=(a, b) [a, b] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o.send :m=, *x, 1).should == [x, 1] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1]) (@o.send :m=, *x, 2).should == [1, 2] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o.send :m=, *x, 1).should == [x, 1] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o.send :m=, *x, 1 }.should raise_error(TypeError) end end context "with a leading splatted Object argument" do before :all do @o = mock("element set receiver") def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o.send :m=, *x, 2, 3, 4, 1).should == [x, 2, [3], 4, 1] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([1, 2, 3]) (@o.send :m=, *x, 4, 5, 6).should == [1, 2, [3, 4], 5, 6] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o.send :m=, *x, 2, 3, 4, 5).should == [x, 2, [3], 4, 5] end it "raises a TypeError if #to_a does not return an Array" do x = mock("splat argument") x.should_receive(:to_a).and_return(1) -> { @o.send :m=, *x, 2, 3, 4 }.should raise_error(TypeError) end end context "with a middle splatted Object argument" do before :all do @o = mock("element set receiver") def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o.send :m=, 1, *x, 2, 3, 4).should == [1, x, [2], 3, 4] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([2, 3]) (@o.send :m=, 1, *x, 4, 5).should == [1, 2, [3], 4, 5] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o.send :m=, 1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4] end it "raises a TypeError if #to_a does not return an Array" 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) end end context "with a trailing splatted Object argument" do before :all do @o = mock("element set receiver") def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end end it "does not call #to_ary" do x = mock("splat argument") x.should_not_receive(:to_ary) (@o.send :m=, 1, 2, 3, 4, *x, 5).should == [1, 2, [3, 4], x, 5] end it "calls #to_a" do x = mock("splat argument") x.should_receive(:to_a).and_return([4, 5]) (@o.send :m=, 1, 2, 3, *x, 6).should == [1, 2, [3, 4], 5, 6] end it "wraps the argument in an Array if #to_a returns nil" do x = mock("splat argument") x.should_receive(:to_a).and_return(nil) (@o.send :m=, 1, 2, 3, *x, 4).should == [1, 2, [3], x, 4] end it "raises a TypeError if #to_a does not return an Array" 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) end end end describe "A method" do SpecEvaluate.desc = "for definition" context "assigns no local variables" do evaluate <<-ruby do def m end ruby m.should be_nil end evaluate <<-ruby do def m() end ruby m.should be_nil end end context "assigns local variables from method parameters" do evaluate <<-ruby do def m(a) a end ruby m((args = 1, 2, 3)).should equal(args) end evaluate <<-ruby do def m((a)) a end ruby m(1).should == 1 m([1, 2, 3]).should == 1 end evaluate <<-ruby do def m((*a, b)) [a, b] end ruby m(1).should == [[], 1] m([1, 2, 3]).should == [[1, 2], 3] end evaluate <<-ruby do def m(a=1) a end ruby m().should == 1 m(2).should == 2 end evaluate <<-ruby do def m() end ruby m().should be_nil m(*[]).should be_nil m(**{}).should be_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 end evaluate <<-ruby do def m(*a) a end ruby m().should == [] m(1).should == [1] m(1, 2, 3).should == [1, 2, 3] m(*[]).should == [] m(**{}).should == [] end evaluate <<-ruby do def m(a:) a end ruby -> { m() }.should raise_error(ArgumentError) m(a: 1).should == 1 suppress_keyword_warning do -> { m("a" => 1, a: 1) }.should raise_error(ArgumentError) end 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 m().should == 1 m(a: 2).should == 2 end evaluate <<-ruby do def m(**) end ruby m().should be_nil m(a: 1, b: 2).should be_nil -> { m(1) }.should raise_error(ArgumentError) end evaluate <<-ruby do def m(**k) k end ruby m().should == {} m(a: 1, b: 2).should == { a: 1, b: 2 } m(*[]).should == {} m(**{}).should == {} 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) end evaluate <<-ruby do def m(**k); k end; ruby m("a" => 1).should == { "a" => 1 } end evaluate <<-ruby do def m(&b) b end ruby m { }.should be_an_instance_of(Proc) end evaluate <<-ruby do def m(a, b) [a, b] end ruby m(1, 2).should == [1, 2] end evaluate <<-ruby do def m(a, (b, c)) [a, b, c] end ruby m(1, 2).should == [1, 2, nil] m(1, [2, 3, 4]).should == [1, 2, 3] end evaluate <<-ruby do def m((a), (b)) [a, b] end ruby m(1, 2).should == [1, 2] m([1, 2], [3, 4]).should == [1, 3] end evaluate <<-ruby 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) end evaluate <<-ruby do def m((*a), (*b)) [a, b] end ruby m(1, 2).should == [[1], [2]] m([1, 2], [3, 4]).should == [[1, 2], [3, 4]] end evaluate <<-ruby do def m((a, b), (c, d)) [a, b, c, d] end ruby m(1, 2).should == [1, nil, 2, nil] m([1, 2, 3], [4, 5, 6]).should == [1, 2, 4, 5] end evaluate <<-ruby do def m((a, *b), (*c, d)) [a, b, c, d] end ruby m(1, 2).should == [1, [], [], 2] m([1, 2, 3], [4, 5, 6]).should == [1, [2, 3], [4, 5], 6] end evaluate <<-ruby do def m((a, b, *c, d), (*e, f, g), (*h)) [a, b, c, d, e, f, g, h] end ruby m(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]] result = m([1, 2, 3], [4, 5, 6, 7, 8], [9, 10]) result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]] end evaluate <<-ruby do def m(a, (b, (c, *d), *e)) [a, b, c, d, e] end ruby m(1, 2).should == [1, 2, nil, [], []] m(1, [2, [3, 4, 5], 6, 7, 8]).should == [1, 2, 3, [4, 5], [6, 7, 8]] end evaluate <<-ruby do def m(a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))) [a, b, c, d, e, f, g, h, i, j] end ruby m(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil] result = m(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]]) result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12] end evaluate <<-ruby do def m(a, b=1) [a, b] end ruby m(2).should == [2, 1] m(1, 2).should == [1, 2] end evaluate <<-ruby do def m(a, *) a end ruby m(1).should == 1 m(1, 2, 3).should == 1 end evaluate <<-ruby do def m(a, *b) [a, b] end ruby m(1).should == [1, []] m(1, 2, 3).should == [1, [2, 3]] end evaluate <<-ruby do def m(a, b:) [a, b] end ruby m(1, b: 2).should == [1, 2] suppress_keyword_warning do -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError) end end 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 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 evaluate <<-ruby do def m(a, &b) [a, b] end ruby m(1).should == [1, nil] m(1, &(l = -> {})).should == [1, l] end evaluate <<-ruby do def m(a=1, b) [a, b] end ruby m(2).should == [1, 2] m(2, 3).should == [2, 3] end evaluate <<-ruby do def m(a=1, *) a end ruby m().should == 1 m(2, 3, 4).should == 2 end evaluate <<-ruby do def m(a=1, *b) [a, b] end ruby m().should == [1, []] m(2, 3, 4).should == [2, [3, 4]] end evaluate <<-ruby do def m(a=1, (b, c)) [a, b, c] end ruby m(2).should == [1, 2, nil] m(2, 3).should == [2, 3, nil] m(2, [3, 4, 5]).should == [2, 3, 4] end evaluate <<-ruby do def m(a=1, (b, (c, *d))) [a, b, c, d] end ruby m(2).should == [1, 2, nil, []] m(2, 3).should == [2, 3, nil, []] m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6]] end evaluate <<-ruby do def m(a=1, (b, (c, *d), *e)) [a, b, c, d, e] end ruby m(2).should == [1, 2, nil, [], []] m(2, [3, 4, 5, 6]).should == [2, 3, 4, [], [5, 6]] m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6], [7]] end evaluate <<-ruby do def m(a=1, (b), (c)) [a, b, c] end ruby m(2, 3).should == [1, 2, 3] m(2, 3, 4).should == [2, 3, 4] m(2, [3, 4], [5, 6, 7]).should == [2, 3, 5] end evaluate <<-ruby do def m(a=1, (*b), (*c)) [a, b, c] end ruby -> { m() }.should raise_error(ArgumentError) -> { m(2) }.should raise_error(ArgumentError) m(2, 3).should == [1, [2], [3]] m(2, [3, 4], [5, 6]).should == [2, [3, 4], [5, 6]] end evaluate <<-ruby do def m(a=1, (b, c), (d, e)) [a, b, c, d, e] end ruby m(2, 3).should == [1, 2, nil, 3, nil] m(2, [3, 4, 5], [6, 7, 8]).should == [2, 3, 4, 6, 7] end evaluate <<-ruby do def m(a=1, (b, *c), (*d, e)) [a, b, c, d, e] end ruby m(1, 2).should == [1, 1, [], [], 2] m(1, [2, 3], [4, 5, 6]).should == [1, 2, [3], [4, 5], 6] end evaluate <<-ruby do def m(a=1, (b, *c), (d, (*e, f))) [a, b, c, d, e, f] end ruby m(1, 2).should == [1, 1, [], 2, [], nil] m(nil, nil).should == [1, nil, [], nil, [], nil] result = m([1, 2, 3], [4, 5, 6], [7, 8, 9]) result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8] end 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] -> { 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 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 evaluate <<-ruby do def m(a=1, **k) [a, k] end ruby m().should == [1, {}] m(2, a: 1, b: 2).should == [2, {a: 1, b: 2}] end evaluate <<-ruby do def m(a=1, &b) [a, b] end ruby m().should == [1, nil] m(&(l = -> {})).should == [1, l] p = -> {} l = mock("to_proc") l.should_receive(:to_proc).and_return(p) m(&l).should == [1, p] end evaluate <<-ruby do def m(*, a) a end ruby m(1).should == 1 m(1, 2, 3).should == 3 end evaluate <<-ruby do def m(*a, b) [a, b] end ruby m(1).should == [[], 1] m(1, 2, 3).should == [[1, 2], 3] end evaluate <<-ruby do def m(*, &b) b end ruby m().should be_nil m(1, 2, 3, 4).should be_nil m(&(l = ->{})).should equal(l) end evaluate <<-ruby do def m(*a, &b) [a, b] end ruby m().should == [[], nil] m(1).should == [[1], nil] m(1, 2, 3, &(l = -> {})).should == [[1, 2, 3], l] end evaluate <<-ruby do def m(a:, b:) [a, b] end ruby m(a: 1, b: 2).should == [1, 2] suppress_keyword_warning do -> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) end end evaluate <<-ruby do def m(a:, b: 1) [a, b] end ruby 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) end end 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 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 evaluate <<-ruby do def m(a:, &b) [a, b] end ruby m(a: 1).should == [1, nil] m(a: 1, &(l = ->{})).should == [1, l] end evaluate <<-ruby do def m(a: 1, b:) [a, b] end ruby m(b: 0).should == [1, 0] m(b: 2, a: 3).should == [3, 2] end evaluate <<-ruby do def m(a: def m(a: 1) a end, b:) [a, b] end ruby m(a: 2, b: 3).should == [2, 3] m(b: 1).should == [:m, 1] # Note the default value of a: in the original method. m().should == 1 end evaluate <<-ruby do def m(a: 1, b: 2) [a, b] end ruby m().should == [1, 2] m(b: 3, a: 4).should == [4, 3] end evaluate <<-ruby do def m(a: 1, **) a end ruby m().should == 1 m(a: 2, b: 1).should == 2 end evaluate <<-ruby do def m(a: 1, **k) [a, k] end ruby m(b: 2, c: 3).should == [1, {b: 2, c: 3}] end evaluate <<-ruby do def m(a: 1, &b) [a, b] end ruby m(&(l = ->{})).should == [1, l] m().should == [1, nil] end evaluate <<-ruby do def m(**, &b) b end ruby m(a: 1, b: 2, &(l = ->{})).should == l end evaluate <<-ruby do def m(**k, &b) [k, b] end ruby m(a: 1, b: 2).should == [{ a: 1, b: 2}, nil] end evaluate <<-ruby do def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) [a, b, c, d, e, f, g, h, k, l] end ruby result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l] end evaluate <<-ruby do def m(a, b=1, *c, d, e:, f: 2, g:, **k, &l) [a, b, c, d, e, f, g, k, l] end ruby result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l] 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') end 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, {foo: :bar}) result.should == [1, nil, nil, {foo: :bar}, nil, {}] 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 = {} m(**h).should == [] 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 = {} -> 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 it "assigns the last Hash to the last optional argument if the Hash contains non-Symbol keys and is not passed as keywords" do def m(a = nil, b = {}, v: false) [a, b, v] end h = { "key" => "value" } m(:a, h).should == [:a, h, false] m(:a, h, v: true).should == [:a, h, true] m(v: true).should == [nil, {}, true] 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_error(TypeError, "no implicit conversion of nil into Hash") -> { m(a: 1, **nil) }.should raise_error(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) args end def n(value, &block) [value, block.call] end end context "when no arguments provided" do it "assigns nil" do args = m () args.should == [nil] 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 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 it "raises a syntax error" do -> { eval("m (1, 2)") }.should raise_error(SyntaxError) -> { eval("m (1, 2, 3)") }.should raise_error(SyntaxError) end end it "allows to pass a block with curly braces" do args = n () { :block_value } args.should == [nil, :block_value] args = n (1) { :block_value } args.should == [1, :block_value] end it "allows to pass a block with do/end" do args = n () do :block_value end args.should == [nil, :block_value] args = n (1) do :block_value end args.should == [1, :block_value] end end describe "An array-dereference method ([])" do SpecEvaluate.desc = "for definition" context "received the passed-in block" do evaluate <<-ruby do def [](*, &b) b.call end ruby pr = proc {:ok} self[&pr].should == :ok self['foo', &pr].should == :ok self.[](&pr).should == :ok self.[]('foo', &pr).should == :ok end evaluate <<-ruby do def [](*) yield end ruby pr = proc {:ok} self[&pr].should == :ok self['foo', &pr].should == :ok self.[](&pr).should == :ok self.[]('foo', &pr).should == :ok end end end describe "An endless method definition" do context "without arguments" do evaluate <<-ruby do def m() = 42 ruby m.should == 42 end context "without parenthesis" do evaluate <<-ruby do def m = 42 ruby m.should == 42 end end end context "with arguments" do evaluate <<-ruby do def m(a, b) = a + b ruby m(1, 4).should == 5 end end 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(6).should == 8 end end # tested more thoroughly in language/delegation_spec.rb context "with args forwarding" do evaluate <<-ruby do def mm(word, num:) word * num 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] 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 it "captures the passed ** keyword arguments" do def foo(a, b, c, **hsh) hsh[:key] end h = { key: 42 } foo(1, 2, 3, **h).should == 42 end it "does not convert a positional Hash to keyword arguments" do def foo(a, b, c, **hsh) hsh[:key] 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 end -> { foo(1, 2, 3, { key: 42 }) }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)') end end 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 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 == [] 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] def bar "baz" end def foo(val) call bar:, val: end ruby 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 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 -> { m_with_block_parameter { } }.should_not complain(verbose: true) end 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 -> { 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 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 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_experimental = 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_experimental 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_experimental = 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_experimental end end end end