Skip to content

Commit 8527ed5

Browse files
authored
Merge pull request #2805 from ksss/add-ruby2_keywords
Add signatures for `{Module,Proc}#ruby2_keywords`
2 parents b975b05 + 4d5e657 commit 8527ed5

7 files changed

Lines changed: 93 additions & 8 deletions

File tree

Steepfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ target :lib do
1515
signature "stdlib/rdoc/0/"
1616
signature "stdlib/ripper/0"
1717
signature "stdlib/pp/0"
18+
signature "steep/patch.rbs"
1819

1920
# configure_code_diagnostics do |config|
2021
# config[D::Ruby::MethodDefinitionMissing] = :hint

core/module.rbs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,43 @@ class Module < Object
16461646
#
16471647
def remove_method: (*interned arg0) -> self
16481648

1649+
# <!--
1650+
# rdoc-file=vm_method.c
1651+
# - ruby2_keywords(method_name, ...) -> nil
1652+
# -->
1653+
# For the given method names, marks the method as passing keywords through a
1654+
# normal argument splat. This should only be called on methods that accept an
1655+
# argument splat (`*args`) but not explicit keywords or a keyword splat. It
1656+
# marks the method such that if the method is called with keyword arguments, the
1657+
# final hash argument is marked with a special flag such that if it is the final
1658+
# element of a normal argument splat to another method call, and that method
1659+
# call does not include explicit keywords or a keyword splat, the final element
1660+
# is interpreted as keywords. In other words, keywords will be passed through
1661+
# the method to other methods.
1662+
#
1663+
# This should only be used for methods that delegate keywords to another method,
1664+
# and only for backwards compatibility with Ruby versions before 3.0. See
1665+
# https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyw
1666+
# ord-arguments-in-ruby-3-0/ for details on why `ruby2_keywords` exists and when
1667+
# and how to use it.
1668+
#
1669+
# This method will probably be removed at some point, as it exists only for
1670+
# backwards compatibility. As it does not exist in Ruby versions before 2.7,
1671+
# check that the module responds to this method before calling it:
1672+
#
1673+
# module Mod
1674+
# def foo(meth, *args, &block)
1675+
# send(:"do_#{meth}", *args, &block)
1676+
# end
1677+
# ruby2_keywords(:foo) if respond_to?(:ruby2_keywords, true)
1678+
# end
1679+
#
1680+
# However, be aware that if the `ruby2_keywords` method is removed, the behavior
1681+
# of the `foo` method using the above approach will change so that the method
1682+
# does not pass through keywords.
1683+
#
1684+
private def ruby2_keywords: (*interned method_name) -> nil
1685+
16491686
# <!--
16501687
# rdoc-file=object.c
16511688
# - mod.set_temporary_name(string) -> self

core/proc.rbs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,37 @@ class Proc
831831
#
832832
def parameters: (?lambda: boolish) -> Method::param_types
833833

834+
# <!--
835+
# rdoc-file=proc.c
836+
# - proc.ruby2_keywords -> proc
837+
# -->
838+
# Marks the proc as passing keywords through a normal argument splat. This
839+
# should only be called on procs that accept an argument splat (`*args`) but not
840+
# explicit keywords or a keyword splat. It marks the proc such that if the proc
841+
# is called with keyword arguments, the final hash argument is marked with a
842+
# special flag such that if it is the final element of a normal argument splat
843+
# to another method call, and that method call does not include explicit
844+
# keywords or a keyword splat, the final element is interpreted as keywords. In
845+
# other words, keywords will be passed through the proc to other methods.
846+
#
847+
# This should only be used for procs that delegate keywords to another method,
848+
# and only for backwards compatibility with Ruby versions before 2.7.
849+
#
850+
# This method will probably be removed at some point, as it exists only for
851+
# backwards compatibility. As it does not exist in Ruby versions before 2.7,
852+
# check that the proc responds to this method before calling it. Also, be aware
853+
# that if this method is removed, the behavior of the proc will change so that
854+
# it does not pass through keywords.
855+
#
856+
# module Mod
857+
# foo = ->(meth, *args, &block) do
858+
# send(:"do_#{meth}", *args, &block)
859+
# end
860+
# foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords)
861+
# end
862+
#
863+
def ruby2_keywords: () -> self
864+
834865
# <!--
835866
# rdoc-file=proc.c
836867
# - prc.source_location -> [String, Integer, Integer, Integer, Integer]

sig/unit_test/spy.rbs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,3 @@ module RBS
2020
end
2121
end
2222
end
23-
24-
class Proc
25-
def ruby2_keywords: () -> self
26-
end
27-
28-
class Module
29-
def ruby2_keywords: (*Symbol) -> void
30-
end

steep/patch.rbs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Remove here if duplicated
2+
3+
class Module
4+
def ruby2_keywords: (*untyped) -> void
5+
end
6+
7+
class Proc
8+
def ruby2_keywords: () -> self
9+
end

test/stdlib/Module_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class ModuleInstanceTest < Test::Unit::TestCase
2525

2626
module Foo
2727
BAR = 1
28+
29+
def foo(meth, *args, &block)
30+
end
2831
end
2932

3033
def test_include
@@ -412,6 +415,13 @@ def test_attr_accessor
412415
)
413416
end
414417

418+
def test_ruby2_keywords
419+
assert_send_type(
420+
"(Symbol) -> nil",
421+
Foo, :ruby2_keywords, :foo
422+
)
423+
end
424+
415425
def test_set_temporary_name
416426
mod = Module.new
417427

test/stdlib/Proc_test.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ def test_parameters
138138
proc{|*, **, &b| }, :parameters
139139
end
140140

141+
def test_ruby2_keywords
142+
assert_send_type '() -> ProcInstanceTest::MyProc',
143+
MyProc.new{}, :ruby2_keywords
144+
end
145+
141146
def test_source_location
142147
if_ruby(..."4.1") do
143148
assert_send_type '() -> [String, Integer]',

0 commit comments

Comments
 (0)