зеркало из https://github.com/github/ruby.git
Allow ruby2_keywords to be used with bmethods
There are libraries that use define_method with argument splats where they would like to pass keywords through the method. To more easily allow such libraries to use ruby2_keywords to handle backwards compatibility, it is necessary for ruby2_keywords to support bmethods.
This commit is contained in:
Родитель
3374e1450c
Коммит
468184a996
|
@ -2637,6 +2637,10 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
send(meth, *args)
|
||||
end
|
||||
|
||||
ruby2_keywords(define_method(:bfoo) do |meth, *args|
|
||||
send(meth, *args)
|
||||
end)
|
||||
|
||||
ruby2_keywords def foo_bar(*args)
|
||||
bar(*args)
|
||||
end
|
||||
|
@ -2743,6 +2747,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[1], h1], o.foo(:bar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.foo(:baz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.bfoo(:bar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.bfoo(:baz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.store_foo(:bar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.store_foo(:baz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.foo_bar(1, :a=>1))
|
||||
|
@ -2750,6 +2756,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[1], h1], o.foo(:bar, 1, **h1))
|
||||
assert_equal([1, h1], o.foo(:baz, 1, **h1))
|
||||
assert_equal([[1], h1], o.bfoo(:bar, 1, **h1))
|
||||
assert_equal([1, h1], o.bfoo(:baz, 1, **h1))
|
||||
assert_equal([[1], h1], o.store_foo(:bar, 1, **h1))
|
||||
assert_equal([1, h1], o.store_foo(:baz, 1, **h1))
|
||||
assert_equal([[1], h1], o.foo_bar(1, **h1))
|
||||
|
@ -2757,6 +2765,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[h1], {}], o.foo(:bar, h1, **{}))
|
||||
assert_equal([h1], o.foo(:baz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.bfoo(:bar, h1, **{}))
|
||||
assert_equal([h1], o.bfoo(:baz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.store_foo(:bar, h1, **{}))
|
||||
assert_equal([h1], o.store_foo(:baz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.foo_bar(h1, **{}))
|
||||
|
@ -2766,6 +2776,10 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
assert_equal([[1], h1], o.foo(:bar, 1, h1))
|
||||
end
|
||||
assert_equal([1, h1], o.foo(:baz, 1, h1))
|
||||
assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
|
||||
assert_equal([[1], h1], o.bfoo(:bar, 1, h1))
|
||||
end
|
||||
assert_equal([1, h1], o.bfoo(:baz, 1, h1))
|
||||
assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
|
||||
assert_equal([[1], h1], o.store_foo(:bar, 1, h1))
|
||||
end
|
||||
|
@ -2797,6 +2811,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[1], h1], o.foo(:dbar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.foo(:dbaz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.bfoo(:dbar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.bfoo(:dbaz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.store_foo(:dbar, 1, :a=>1))
|
||||
assert_equal([1, h1], o.store_foo(:dbaz, 1, :a=>1))
|
||||
assert_equal([[1], h1], o.foo_dbar(1, :a=>1))
|
||||
|
@ -2804,6 +2820,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[1], h1], o.foo(:dbar, 1, **h1))
|
||||
assert_equal([1, h1], o.foo(:dbaz, 1, **h1))
|
||||
assert_equal([[1], h1], o.bfoo(:dbar, 1, **h1))
|
||||
assert_equal([1, h1], o.bfoo(:dbaz, 1, **h1))
|
||||
assert_equal([[1], h1], o.store_foo(:dbar, 1, **h1))
|
||||
assert_equal([1, h1], o.store_foo(:dbaz, 1, **h1))
|
||||
assert_equal([[1], h1], o.foo_dbar(1, **h1))
|
||||
|
@ -2811,6 +2829,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
|
||||
assert_equal([[h1], {}], o.foo(:dbar, h1, **{}))
|
||||
assert_equal([h1], o.foo(:dbaz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.bfoo(:dbar, h1, **{}))
|
||||
assert_equal([h1], o.bfoo(:dbaz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.store_foo(:dbar, h1, **{}))
|
||||
assert_equal([h1], o.store_foo(:dbaz, h1, **{}))
|
||||
assert_equal([[h1], {}], o.foo_dbar(h1, **{}))
|
||||
|
@ -2820,6 +2840,10 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
assert_equal([[1], h1], o.foo(:dbar, 1, h1))
|
||||
end
|
||||
assert_equal([1, h1], o.foo(:dbaz, 1, h1))
|
||||
assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
|
||||
assert_equal([[1], h1], o.bfoo(:dbar, 1, h1))
|
||||
end
|
||||
assert_equal([1, h1], o.bfoo(:dbaz, 1, h1))
|
||||
assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
|
||||
assert_equal([[1], h1], o.store_foo(:dbar, 1, h1))
|
||||
end
|
||||
|
@ -2883,10 +2907,17 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
assert_equal([1, h1], o.baz(1, h1))
|
||||
assert_equal([h1], o.baz(h1, **{}))
|
||||
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar \(method not defined in Ruby, method accepts keywords, or method does not accept argument splat\)/) do
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do
|
||||
assert_nil(c.send(:ruby2_keywords, :bar))
|
||||
end
|
||||
|
||||
o = Object.new
|
||||
class << o
|
||||
alias bar p
|
||||
end
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar \(method not defined in Ruby\)/) do
|
||||
assert_nil(o.singleton_class.send(:ruby2_keywords, :bar))
|
||||
end
|
||||
sc = Class.new(c)
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar \(can only set in method defining module\)/) do
|
||||
sc.send(:ruby2_keywords, :bar)
|
||||
|
|
46
vm_method.c
46
vm_method.c
|
@ -1805,15 +1805,43 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
|
|||
}
|
||||
|
||||
if (module == defined_class || origin_class == defined_class) {
|
||||
if (me->def->type == VM_METHOD_TYPE_ISEQ &&
|
||||
me->def->body.iseq.iseqptr->body->param.flags.has_rest &&
|
||||
!me->def->body.iseq.iseqptr->body->param.flags.has_kw &&
|
||||
!me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) {
|
||||
me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1;
|
||||
rb_clear_method_cache_by_class(module);
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby, method accepts keywords, or method does not accept argument splat)", rb_id2name(name));
|
||||
switch (me->def->type) {
|
||||
case VM_METHOD_TYPE_ISEQ:
|
||||
if (me->def->body.iseq.iseqptr->body->param.flags.has_rest &&
|
||||
!me->def->body.iseq.iseqptr->body->param.flags.has_kw &&
|
||||
!me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) {
|
||||
me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1;
|
||||
rb_clear_method_cache_by_class(module);
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
|
||||
}
|
||||
break;
|
||||
case VM_METHOD_TYPE_BMETHOD: {
|
||||
VALUE procval = me->def->body.bmethod.proc;
|
||||
if (vm_block_handler_type(procval) == block_handler_type_proc) {
|
||||
procval = vm_proc_to_block_handler(VM_BH_TO_PROC(procval));
|
||||
}
|
||||
|
||||
if (vm_block_handler_type(procval) == block_handler_type_iseq) {
|
||||
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(procval);
|
||||
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
|
||||
if (iseq->body->param.flags.has_rest &&
|
||||
!iseq->body->param.flags.has_kw &&
|
||||
!iseq->body->param.flags.has_kwrest) {
|
||||
iseq->body->param.flags.ruby2_keywords = 1;
|
||||
rb_clear_method_cache_by_class(module);
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby)", rb_id2name(name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
Загрузка…
Ссылка в новой задаче