From 99674185d63162eadcded3d2623a181fddf2bbb3 Mon Sep 17 00:00:00 2001 From: Ufuk Kayserilioglu Date: Wed, 26 Jun 2024 17:38:01 -0400 Subject: Start handling `&nil` in method parameters Similar to `:nokey` for `**nil` declaring methods/procs, a method/proc with a `&nil` declaration will return a `:noblock` entry in the parameters array. --- iseq.c | 8 +++++++- proc.c | 7 +++++++ spec/ruby/core/method/parameters_spec.rb | 13 +++++++++++++ spec/ruby/core/proc/parameters_spec.rb | 8 ++++++++ spec/ruby/language/block_spec.rb | 2 +- spec/ruby/language/method_spec.rb | 2 +- test/ruby/test_iseq.rb | 11 +++++++++++ test/ruby/test_method.rb | 6 ++++++ 8 files changed, 54 insertions(+), 3 deletions(-) diff --git a/iseq.c b/iseq.c index c418e17869..648f2c388f 100644 --- a/iseq.c +++ b/iseq.c @@ -3787,7 +3787,13 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc) } rb_ary_push(args, a); } - if (body->param.flags.has_block) { + if (body->param.flags.accepts_no_block) { + ID noblock; + CONST_ID(noblock, "noblock"); + PARAM_TYPE(noblock); + rb_ary_push(args, a); + } + else if (body->param.flags.has_block) { CONST_ID(block, "block"); rb_ary_push(args, PARAM(body->param.block_start, block)); } diff --git a/proc.c b/proc.c index 67963a1b01..3e2afeab3c 100644 --- a/proc.c +++ b/proc.c @@ -3433,6 +3433,7 @@ method_inspect(VALUE method) const VALUE keyrest = ID2SYM(rb_intern("keyrest")); const VALUE block = ID2SYM(rb_intern("block")); const VALUE nokey = ID2SYM(rb_intern("nokey")); + const VALUE noblock = ID2SYM(rb_intern("noblock")); int forwarding = 0; rb_str_buf_cat2(str, "("); @@ -3467,6 +3468,9 @@ method_inspect(VALUE method) else if (kind == nokey) { name = rb_str_new2("nil"); } + else if (kind == noblock) { + name = rb_str_new2("nil"); + } else { name = Qnil; } @@ -3519,6 +3523,9 @@ method_inspect(VALUE method) else if (kind == nokey) { rb_str_buf_cat2(str, "**nil"); } + else if (kind == noblock) { + rb_str_buf_cat2(str, "&nil"); + } if (i < RARRAY_LEN(params) - 1) { rb_str_buf_cat2(str, ", "); diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb index f1c2523cf0..41b9cd8d12 100644 --- a/spec/ruby/core/method/parameters_spec.rb +++ b/spec/ruby/core/method/parameters_spec.rb @@ -22,6 +22,12 @@ describe "Method#parameters" do local_is_not_parameter = {} end + ruby_version_is "4.1" do + eval <<-RUBY + def one_noblock(&nil); end + RUBY + end + def forward_parameters(...) end def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end @@ -187,6 +193,13 @@ describe "Method#parameters" do m.parameters.should == [[:nokey]] end + ruby_version_is "4.1" do + it "returns [[:noblock]] for a method with a single &nil parameter" do + m = MethodSpecs::Methods.instance_method(:one_noblock) + m.parameters.should == [[:noblock]] + end + 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]] diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb index cf8a8f5b12..0876ad0daf 100644 --- a/spec/ruby/core/proc/parameters_spec.rb +++ b/spec/ruby/core/proc/parameters_spec.rb @@ -172,4 +172,12 @@ describe "Proc#parameters" do eval("lambda { it }").parameters.should == [[:req]] end end + + ruby_version_is "4.1" do + it "returns :noblock for &nil parameter" do + eval <<~RUBY + proc { |&nil| }.parameters.should == [[:noblock]] + RUBY + end + end end diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb index 1890f0726a..ad6f7190b4 100644 --- a/spec/ruby/language/block_spec.rb +++ b/spec/ruby/language/block_spec.rb @@ -1111,7 +1111,7 @@ describe "`it` calls without arguments in a block" do end end - ruby_version_is "3.4" do + ruby_version_is "4.1" do it "works alongside disallowed block argument" do no_block = eval <<-EOF proc {|arg1, &nil| arg1} diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index d12fd71b21..dd93703d9f 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -1128,7 +1128,7 @@ describe "A method" do result.should == [1, nil, nil, {foo: :bar}, nil, {}] end - ruby_version_is "3.4" do + ruby_version_is "4.1" do evaluate <<-ruby do def m(a, &nil); a end; ruby diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 6846f7958b..43b1f0f620 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -682,6 +682,17 @@ class TestISeq < Test::Unit::TestCase assert_equal([[:nokey]], iseq.eval.singleton_method(:foo).parameters) end + def test_to_binary_dumps_noblock + iseq = assert_iseq_to_binary(<<-RUBY) + o = Object.new + class << o + def foo(&nil); end + end + o + RUBY + assert_equal([[:noblock]], iseq.eval.singleton_method(:foo).parameters) + end + def test_to_binary_line_info assert_iseq_to_binary("#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #14660]').eval begin; diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index c3819cdebf..7c3e8e03a7 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -32,6 +32,7 @@ class TestMethod < Test::Unit::TestCase def mk7(a, b = nil, *c, d, **o) nil && o end def mk8(a, b = nil, *c, d, e:, f: nil, **o) nil && o end def mnk(**nil) end + def mnb(&nil) end def mf(...) end class Base @@ -617,6 +618,7 @@ class TestMethod < Test::Unit::TestCase define_method(:pmk7) {|a, b = nil, *c, d, **o|} define_method(:pmk8) {|a, b = nil, *c, d, e:, f: nil, **o|} define_method(:pmnk) {|**nil|} + define_method(:pmnb) {|&nil|} def test_bound_parameters assert_equal([], method(:m0).parameters) @@ -640,6 +642,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:mk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:mk8).parameters) assert_equal([[:nokey]], method(:mnk).parameters) + assert_equal([[:noblock]], method(:mnb).parameters) # pending assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], method(:mf).parameters) end @@ -666,6 +669,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:mk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:mk8).parameters) assert_equal([[:nokey]], self.class.instance_method(:mnk).parameters) + assert_equal([[:noblock]], self.class.instance_method(:mnb).parameters) # pending assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], self.class.instance_method(:mf).parameters) end @@ -691,6 +695,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:pmk8).parameters) assert_equal([[:nokey]], method(:pmnk).parameters) + assert_equal([[:noblock]], method(:pmnb).parameters) end def test_bmethod_unbound_parameters @@ -715,6 +720,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:pmk8).parameters) assert_equal([[:nokey]], self.class.instance_method(:pmnk).parameters) + assert_equal([[:noblock]], self.class.instance_method(:pmnb).parameters) end def test_hidden_parameters -- cgit v1.2.3