summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUfuk Kayserilioglu <ufuk.kayserilioglu@shopify.com>2024-06-26 17:38:01 -0400
committerKevin Newton <kddnewton@gmail.com>2026-02-15 14:12:15 -0500
commit99674185d63162eadcded3d2623a181fddf2bbb3 (patch)
tree0b2b62b3d5c9124b1fc9a9551850a3494de1fb3d
parent071c6d149b8e67960ba809089c3736926620c40e (diff)
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.
-rw-r--r--iseq.c8
-rw-r--r--proc.c7
-rw-r--r--spec/ruby/core/method/parameters_spec.rb13
-rw-r--r--spec/ruby/core/proc/parameters_spec.rb8
-rw-r--r--spec/ruby/language/block_spec.rb2
-rw-r--r--spec/ruby/language/method_spec.rb2
-rw-r--r--test/ruby/test_iseq.rb11
-rw-r--r--test/ruby/test_method.rb6
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