1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/core/method/parameters_spec.rb
Jeremy Evans f53dfab95c Add support for anonymous rest and keyword rest argument forwarding
This allows for the following syntax:

```ruby
def foo(*)
  bar(*)
end
def baz(**)
  quux(**)
end
```

This is a natural addition after the introduction of anonymous
block forwarding.  Anonymous rest and keyword rest arguments were
already supported in method parameters, this just allows them to
be used as arguments to other methods.  The same advantages of
anonymous block forwarding apply to rest and keyword rest argument
forwarding.

This has some minor changes to #parameters output.  Now, instead
of `[:rest], [:keyrest]`, you get `[:rest, :*], [:keyrest, :**]`.
These were already used for `...` forwarding, so I think it makes
it more consistent to include them in other cases.  If we want to
use `[:rest], [:keyrest]` in both cases, that is also possible.

I don't think the previous behavior of `[:rest], [:keyrest]` in
the non-... case and `[:rest, :*], [:keyrest, :**]` in the ...
case makes sense, but if we did want that behavior, we'll have to
make more substantial changes, such as using a different ID in the
... forwarding case.

Implements [Feature #18351]
2021-12-30 14:37:42 -08:00

270 lines
12 KiB
Ruby

require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Method#parameters" do
class MethodSpecs::Methods
def one_key(a: 1); end
def one_keyrest(**a); end
def one_keyreq(a:); end
def one_splat_one_req(*a,b); end
def one_splat_two_req(*a,b,c); end
def one_splat_one_req_with_block(*a,b,&blk); end
def one_opt_with_stabby(a=-> b { true }); end
def one_unnamed_splat(*); end
def one_splat_one_block(*args, &block)
local_is_not_parameter = {}
end
define_method(:one_optional_defined_method) {|x = 1|}
end
it "returns an empty Array when the method expects no arguments" do
MethodSpecs::Methods.instance_method(:zero).parameters.should == []
end
it "returns [[:req,:name]] for a method expecting one required argument called 'name'" do
MethodSpecs::Methods.instance_method(:one_req).parameters.should == [[:req,:a]]
end
it "returns [[:req,:a],[:req,:b]] for a method expecting two required arguments called 'a' and 'b''" do
m = MethodSpecs::Methods.instance_method(:two_req)
m.parameters.should == [[:req,:a], [:req,:b]]
end
it "returns [[:block,:blk]] for a method expecting one block argument called 'a'" do
m = MethodSpecs::Methods.instance_method(:zero_with_block)
m.parameters.should == [[:block,:blk]]
end
it "returns [[:req,:a],[:block,:b] for a method expecting a required argument ('a') and a block argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_req_with_block)
m.parameters.should == [[:req,:a], [:block,:blk]]
end
it "returns [[:req,:a],[:req,:b],[:block,:c] for a method expecting two required arguments ('a','b') and a block argument ('c')" do
m = MethodSpecs::Methods.instance_method(:two_req_with_block)
m.parameters.should == [[:req,:a], [:req,:b], [:block,:blk]]
end
it "returns [[:opt,:a]] for a method expecting one optional argument ('a')" do
m = MethodSpecs::Methods.instance_method(:one_opt)
m.parameters.should == [[:opt,:a]]
end
it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
m.parameters.should == [[:req,:a],[:opt,:b]]
end
it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
m.parameters.should == [[:req,:a],[:opt,:b]]
end
it "returns [[:req,:a],[:opt,:b],[:opt,:c]] for a method expecting one required argument ('a') and two optional arguments ('b','c')" do
m = MethodSpecs::Methods.instance_method(:one_req_two_opt)
m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c]]
end
it "returns [[:req,:a],[:req,:b],[:opt,:c]] for a method expecting two required arguments ('a','b') and one optional arguments ('c')" do
m = MethodSpecs::Methods.instance_method(:two_req_one_opt)
m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c]]
end
it "returns [[:opt,:a],[:block,:b]] for a method expecting one required argument ('a') and one block argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_opt_with_block)
m.parameters.should == [[:opt,:a],[:block,:blk]]
end
it "returns [[:req,:a],[:opt,:b],[:block,:c]] for a method expecting one required argument ('a'), one optional argument ('b'), and a block ('c')" do
m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_block)
m.parameters.should == [[:req,:a],[:opt,:b],[:block,:blk]]
end
it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:block,:d]] for a method expecting one required argument ('a'), two optional arguments ('b','c'), and a block ('d')" do
m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_block)
m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:block,:blk]]
end
it "returns [[:rest,:a]] for a method expecting a single splat argument ('a')" do
m = MethodSpecs::Methods.instance_method(:zero_with_splat)
m.parameters.should == [[:rest,:a]]
end
it "returns [[:req,:a],[:rest,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_req_with_splat)
m.parameters.should == [[:req,:a],[:rest,:b]]
end
it "returns [[:req,:a],[:req,:b],[:rest,:c]] for a method expecting two required arguments ('a','b') and a splat argument ('c')" do
m = MethodSpecs::Methods.instance_method(:two_req_with_splat)
m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c]]
end
it "returns [[:req,:a],[:opt,:b],[:rest,:c]] for a method expecting a required argument ('a','b'), an optional argument ('b'), and a splat argument ('c')" do
m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat)
m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c]]
end
it "returns [[:req,:a],[:req,:b],[:opt,:b],[:rest,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), and a splat argument ('d')" do
m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat)
m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d]]
end
it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] for a method expecting a required argument ('a'), two optional arguments ('b','c'), and a splat argument ('d')" do
m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_splat)
m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]]
end
it "returns [[:rest,:a],[:block,:b]] for a method expecting a splat argument ('a') and a block argument ('b')" do
m = MethodSpecs::Methods.instance_method(:zero_with_splat_and_block)
m.parameters.should == [[:rest,:a],[:block,:blk]]
end
it "returns [[:req,:a],[:rest,:b],[:block,:c]] for a method expecting a required argument ('a'), a splat argument ('b'), and a block ('c')" do
m = MethodSpecs::Methods.instance_method(:one_req_with_splat_and_block)
m.parameters.should == [[:req,:a],[:rest,:b],[:block,:blk]]
end
it "returns [[:req,:a],[:req,:b],[:rest,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), a splat argument ('c'), and a block ('d')" do
m = MethodSpecs::Methods.instance_method(:two_req_with_splat_and_block)
m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c],[:block,:blk]]
end
it "returns [[:req,:a],[:opt,:b],[:rest,:c],[:block,:d]] for a method expecting a required argument ('a'), a splat argument ('c'), and a block ('d')" do
m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat_and_block)
m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c],[:block,:blk]]
end
it "returns [[:req,:a],[:req,:b],[:opt,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), a splat argument ('d'), and a block ('e')" do
m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat_and_block)
m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d],[:block,:blk]]
end
it "returns [[:rest,:a],[:req,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
m = MethodSpecs::Methods.instance_method(:one_splat_one_req)
m.parameters.should == [[:rest,:a],[:req,:b]]
end
it "returns [[:rest,:a],[:req,:b],[:req,:c]] for a method expecting a splat argument ('a') and two required arguments ('b','c')" do
m = MethodSpecs::Methods.instance_method(:one_splat_two_req)
m.parameters.should == [[:rest,:a],[:req,:b],[:req,:c]]
end
it "returns [[:rest,:a],[:req,:b],[:block,:c]] for a method expecting a splat argument ('a'), a required argument ('b'), and a block ('c')" do
m = MethodSpecs::Methods.instance_method(:one_splat_one_req_with_block)
m.parameters.should == [[:rest,:a],[:req,:b],[:block,:blk]]
end
it "returns [[:key,:a]] for a method with a single optional keyword argument" do
m = MethodSpecs::Methods.instance_method(:one_key)
m.parameters.should == [[:key,:a]]
end
it "returns [[:keyrest,:a]] for a method with a keyword rest argument" do
m = MethodSpecs::Methods.instance_method(:one_keyrest)
m.parameters.should == [[:keyrest,:a]]
end
it "returns [[:keyreq,:a]] for a method with a single required keyword argument" do
m = MethodSpecs::Methods.instance_method(:one_keyreq)
m.parameters.should == [[:keyreq,:a]]
end
it "works with ->(){} as the value of an optional argument" do
m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby)
m.parameters.should == [[:opt,:a]]
end
# define_method variants
it "returns [] for a define_method method with explicit no-args || specification" do
m = MethodSpecs::Methods.instance_method(:zero_defined_method)
m.parameters.should == []
end
it "returns [[:rest, :x]] for a define_method method with rest arg 'x' only" do
m = MethodSpecs::Methods.instance_method(:zero_with_splat_defined_method)
m.parameters.should == [[:rest, :x]]
end
it "returns [[:req, :x]] for a define_method method expecting one required argument 'x'" do
m = MethodSpecs::Methods.instance_method(:one_req_defined_method)
m.parameters.should == [[:req, :x]]
end
it "returns [[:req, :x], [:req, :y]] for a define_method method expecting two required arguments 'x' and 'y'" do
m = MethodSpecs::Methods.instance_method(:two_req_defined_method)
m.parameters.should == [[:req, :x], [:req, :y]]
end
it "returns [] for a define_method method with no args specification" do
m = MethodSpecs::Methods.instance_method(:no_args_defined_method)
m.parameters.should == []
end
it "returns [[:req]] for a define_method method with a grouping as its only argument" do
m = MethodSpecs::Methods.instance_method(:two_grouped_defined_method)
m.parameters.should == [[:req]]
end
it "returns [[:opt, :x]] for a define_method method with an optional argument 'x'" do
m = MethodSpecs::Methods.instance_method(:one_optional_defined_method)
m.parameters.should == [[:opt, :x]]
end
it "returns [[:rest]] for a Method generated by respond_to_missing?" do
m = MethodSpecs::Methods.new
m.method(:handled_via_method_missing).parameters.should == [[:rest]]
end
ruby_version_is '3.1' do
it "adds * rest arg for \"star\" argument" do
m = MethodSpecs::Methods.new
m.method(:one_unnamed_splat).parameters.should == [[:rest, :*]]
end
end
ruby_version_is ''...'3.1' do
it "adds nameless rest arg for \"star\" argument" do
m = MethodSpecs::Methods.new
m.method(:one_unnamed_splat).parameters.should == [[:rest]]
end
end
it "returns the args and block for a splat and block argument" do
m = MethodSpecs::Methods.new
m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]]
end
it "returns [] for a Method generated by attr_reader" do
m = MethodSpecs::Methods.new
m.method(:reader).parameters.should == []
end
it "return [[:req]] for a Method generated by attr_writer" do
m = MethodSpecs::Methods.new
m.method(:writer=).parameters.should == [[:req]]
end
it "returns [[:rest]] for core methods with variable-length argument lists" do
# delete! takes rest args
"foo".method(:delete!).parameters.should == [[:rest]]
end
it "returns [[:rest]] or [[:opt]] for core methods with optional arguments" do
# pop takes 1 optional argument
[
[[:rest]],
[[:opt]]
].should include([].method(:pop).parameters)
end
it "returns [[:req]] for each parameter for core methods with fixed-length argument lists" do
"foo".method(:+).parameters.should == [[:req]]
end
end