mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
Do not respect ruby2_keywords on method/proc with post arguments
Previously, ruby2_keywords could be used on a method or proc with
post arguments, but I don't think the behavior is desired:
```ruby
def a(*c, **kw) [c, kw] end
def b(*a, b) a(*a, b) end
ruby2_keywords(:b)
b({foo: 1}, bar: 1)
```
This changes ruby2_keywords to emit a warning and not set the
flag on a method/proc with post arguments.
While here, fix the ruby2_keywords specs for warnings, since they
weren't testing what they should be testing. They all warned
because the method didn't accept a rest argument, not because it
accepted a keyword or keyword rest argument.
[Backport #21402]
This commit is contained in:
parent
d49c7d0661
commit
fd036dbc3f
3
proc.c
3
proc.c
@ -3958,12 +3958,13 @@ proc_ruby2_keywords(VALUE procval)
|
||||
switch (proc->block.type) {
|
||||
case block_type_iseq:
|
||||
if (ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_rest &&
|
||||
!ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_post &&
|
||||
!ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kw &&
|
||||
!ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kwrest) {
|
||||
ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.ruby2_keywords = 1;
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or proc does not accept argument splat)");
|
||||
rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or post arguments or proc does not accept argument splat)");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -275,7 +275,7 @@ describe "Module#ruby2_keywords" do
|
||||
|
||||
it "prints warning when a method accepts keywords" do
|
||||
obj = Object.new
|
||||
def obj.foo(a:, b:) end
|
||||
def obj.foo(*a, b:) end
|
||||
|
||||
-> {
|
||||
obj.singleton_class.class_exec do
|
||||
@ -286,7 +286,7 @@ describe "Module#ruby2_keywords" do
|
||||
|
||||
it "prints warning when a method accepts keyword splat" do
|
||||
obj = Object.new
|
||||
def obj.foo(**a) end
|
||||
def obj.foo(*a, **b) end
|
||||
|
||||
-> {
|
||||
obj.singleton_class.class_exec do
|
||||
@ -294,4 +294,17 @@ describe "Module#ruby2_keywords" do
|
||||
end
|
||||
}.should complain(/Skipping set of ruby2_keywords flag for/)
|
||||
end
|
||||
|
||||
ruby_version_is "3.5" do
|
||||
it "prints warning when a method accepts post arguments" do
|
||||
obj = Object.new
|
||||
def obj.foo(*a, b) end
|
||||
|
||||
-> {
|
||||
obj.singleton_class.class_exec do
|
||||
ruby2_keywords :foo
|
||||
end
|
||||
}.should complain(/Skipping set of ruby2_keywords flag for/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -39,7 +39,7 @@ describe "Proc#ruby2_keywords" do
|
||||
end
|
||||
|
||||
it "prints warning when a proc accepts keywords" do
|
||||
f = -> a:, b: { }
|
||||
f = -> *a, b: { }
|
||||
|
||||
-> {
|
||||
f.ruby2_keywords
|
||||
@ -47,10 +47,20 @@ describe "Proc#ruby2_keywords" do
|
||||
end
|
||||
|
||||
it "prints warning when a proc accepts keyword splat" do
|
||||
f = -> **a { }
|
||||
f = -> *a, **b { }
|
||||
|
||||
-> {
|
||||
f.ruby2_keywords
|
||||
}.should complain(/Skipping set of ruby2_keywords flag for/)
|
||||
end
|
||||
|
||||
ruby_version_is "3.5" do
|
||||
it "prints warning when a proc accepts post arguments" do
|
||||
f = -> *a, b { }
|
||||
|
||||
-> {
|
||||
f.ruby2_keywords
|
||||
}.should complain(/Skipping set of ruby2_keywords flag for/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2424,6 +2424,21 @@ class TestKeywordArguments < Test::Unit::TestCase
|
||||
assert_raise(ArgumentError) { m.call(42, a: 1, **h2) }
|
||||
end
|
||||
|
||||
def test_ruby2_keywords_post_arg
|
||||
def self.a(*c, **kw) [c, kw] end
|
||||
def self.b(*a, b) a(*a, b) end
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for b \(method accepts keywords or post arguments or method does not accept argument splat\)/) do
|
||||
assert_nil(singleton_class.send(:ruby2_keywords, :b))
|
||||
end
|
||||
assert_equal([[{foo: 1}, {bar: 1}], {}], b({foo: 1}, bar: 1))
|
||||
|
||||
b = ->(*a, b){a(*a, b)}
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or post arguments or proc does not accept argument splat\)/) do
|
||||
b.ruby2_keywords
|
||||
end
|
||||
assert_equal([[{foo: 1}, {bar: 1}], {}], b.({foo: 1}, bar: 1))
|
||||
end
|
||||
|
||||
def test_proc_ruby2_keywords
|
||||
h1 = {:a=>1}
|
||||
foo = ->(*args, &block){block.call(*args)}
|
||||
@ -2436,8 +2451,8 @@ class TestKeywordArguments < Test::Unit::TestCase
|
||||
assert_raise(ArgumentError) { foo.call(:a=>1, &->(arg, **kw){[arg, kw]}) }
|
||||
assert_equal(h1, foo.call(:a=>1, &->(arg){arg}))
|
||||
|
||||
[->(){}, ->(arg){}, ->(*args, **kw){}, ->(*args, k: 1){}, ->(*args, k: ){}].each do |pr|
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or proc does not accept argument splat\)/) do
|
||||
[->(){}, ->(arg){}, ->(*args, x){}, ->(*args, **kw){}, ->(*args, k: 1){}, ->(*args, k: ){}].each do |pr|
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or post arguments or proc does not accept argument splat\)/) do
|
||||
pr.ruby2_keywords
|
||||
end
|
||||
end
|
||||
@ -2790,10 +2805,21 @@ class TestKeywordArguments < Test::Unit::TestCase
|
||||
assert_equal(:opt, o.clear_last_opt(a: 1))
|
||||
assert_nothing_raised(ArgumentError) { o.clear_last_empty_method(a: 1) }
|
||||
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar \(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 post arguments or method does not accept argument splat\)/) do
|
||||
assert_nil(c.send(:ruby2_keywords, :bar))
|
||||
end
|
||||
|
||||
c.class_eval do
|
||||
def bar_post(*a, x) = nil
|
||||
define_method(:bar_post_bmethod) { |*a, x| }
|
||||
end
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar_post \(method accepts keywords or post arguments or method does not accept argument splat\)/) do
|
||||
assert_nil(c.send(:ruby2_keywords, :bar_post))
|
||||
end
|
||||
assert_warn(/Skipping set of ruby2_keywords flag for bar_post_bmethod \(method accepts keywords or post arguments or method does not accept argument splat\)/) do
|
||||
assert_nil(c.send(:ruby2_keywords, :bar_post_bmethod))
|
||||
end
|
||||
|
||||
utf16_sym = "abcdef".encode("UTF-16LE").to_sym
|
||||
c.send(:define_method, utf16_sym, c.instance_method(:itself))
|
||||
assert_warn(/abcdef/) do
|
||||
|
||||
@ -2599,13 +2599,14 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
|
||||
switch (me->def->type) {
|
||||
case VM_METHOD_TYPE_ISEQ:
|
||||
if (ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_rest &&
|
||||
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_post &&
|
||||
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kw &&
|
||||
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kwrest) {
|
||||
ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.ruby2_keywords = 1;
|
||||
rb_clear_method_cache(module, name);
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or method does not accept argument splat)", QUOTE_ID(name));
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name));
|
||||
}
|
||||
break;
|
||||
case VM_METHOD_TYPE_BMETHOD: {
|
||||
@ -2618,13 +2619,14 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
|
||||
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(iseq)->param.flags.has_rest &&
|
||||
!ISEQ_BODY(iseq)->param.flags.has_post &&
|
||||
!ISEQ_BODY(iseq)->param.flags.has_kw &&
|
||||
!ISEQ_BODY(iseq)->param.flags.has_kwrest) {
|
||||
ISEQ_BODY(iseq)->param.flags.ruby2_keywords = 1;
|
||||
rb_clear_method_cache(module, name);
|
||||
}
|
||||
else {
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or method does not accept argument splat)", QUOTE_ID(name));
|
||||
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user