Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target :lib do
signature "stdlib/rdoc/0/"
signature "stdlib/ripper/0"
signature "stdlib/pp/0"
signature "steep/patch.rbs"

# configure_code_diagnostics do |config|
# config[D::Ruby::MethodDefinitionMissing] = :hint
Expand Down
37 changes: 37 additions & 0 deletions core/module.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,43 @@ class Module < Object
#
def remove_method: (*interned arg0) -> self

# <!--
# rdoc-file=vm_method.c
# - ruby2_keywords(method_name, ...) -> nil
# -->
# For the given method names, marks the method as passing keywords through a
# normal argument splat. This should only be called on methods that accept an
# argument splat (`*args`) but not explicit keywords or a keyword splat. It
# marks the method such that if the method is called with keyword arguments, the
# final hash argument is marked with a special flag such that if it is the final
# element of a normal argument splat to another method call, and that method
# call does not include explicit keywords or a keyword splat, the final element
# is interpreted as keywords. In other words, keywords will be passed through
# the method to other methods.
#
# This should only be used for methods that delegate keywords to another method,
# and only for backwards compatibility with Ruby versions before 3.0. See
# https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyw
# ord-arguments-in-ruby-3-0/ for details on why `ruby2_keywords` exists and when
# and how to use it.
#
# This method will probably be removed at some point, as it exists only for
# backwards compatibility. As it does not exist in Ruby versions before 2.7,
# check that the module responds to this method before calling it:
#
# module Mod
# def foo(meth, *args, &block)
# send(:"do_#{meth}", *args, &block)
# end
# ruby2_keywords(:foo) if respond_to?(:ruby2_keywords, true)
# end
#
# However, be aware that if the `ruby2_keywords` method is removed, the behavior
# of the `foo` method using the above approach will change so that the method
# does not pass through keywords.
#
private def ruby2_keywords: (*interned method_name) -> nil

# <!--
# rdoc-file=object.c
# - mod.set_temporary_name(string) -> self
Expand Down
31 changes: 31 additions & 0 deletions core/proc.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,37 @@ class Proc
#
def parameters: (?lambda: boolish) -> Method::param_types

# <!--
# rdoc-file=proc.c
# - proc.ruby2_keywords -> proc
# -->
# Marks the proc as passing keywords through a normal argument splat. This
# should only be called on procs that accept an argument splat (`*args`) but not
# explicit keywords or a keyword splat. It marks the proc such that if the proc
# is called with keyword arguments, the final hash argument is marked with a
# special flag such that if it is the final element of a normal argument splat
# to another method call, and that method call does not include explicit
# keywords or a keyword splat, the final element is interpreted as keywords. In
# other words, keywords will be passed through the proc to other methods.
#
# This should only be used for procs that delegate keywords to another method,
# and only for backwards compatibility with Ruby versions before 2.7.
#
# This method will probably be removed at some point, as it exists only for
# backwards compatibility. As it does not exist in Ruby versions before 2.7,
# check that the proc responds to this method before calling it. Also, be aware
# that if this method is removed, the behavior of the proc will change so that
# it does not pass through keywords.
#
# module Mod
# foo = ->(meth, *args, &block) do
# send(:"do_#{meth}", *args, &block)
# end
# foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords)
# end
#
def ruby2_keywords: () -> self

# <!--
# rdoc-file=proc.c
# - prc.source_location -> [String, Integer, Integer, Integer, Integer]
Expand Down
8 changes: 0 additions & 8 deletions sig/unit_test/spy.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,3 @@ module RBS
end
end
end

class Proc
def ruby2_keywords: () -> self
end

class Module
def ruby2_keywords: (*Symbol) -> void
end
9 changes: 9 additions & 0 deletions steep/patch.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Remove here if duplicated

class Module
def ruby2_keywords: (*untyped) -> void
end

class Proc
def ruby2_keywords: () -> self
end
10 changes: 10 additions & 0 deletions test/stdlib/Module_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class ModuleInstanceTest < Test::Unit::TestCase

module Foo
BAR = 1

def foo(meth, *args, &block)
end
end

def test_include
Expand Down Expand Up @@ -412,6 +415,13 @@ def test_attr_accessor
)
end

def test_ruby2_keywords
assert_send_type(
"(Symbol) -> nil",
Foo, :ruby2_keywords, :foo
)
end

def test_set_temporary_name
mod = Module.new

Expand Down
5 changes: 5 additions & 0 deletions test/stdlib/Proc_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ def test_parameters
proc{|*, **, &b| }, :parameters
end

def test_ruby2_keywords
assert_send_type '() -> ProcInstanceTest::MyProc',
MyProc.new{}, :ruby2_keywords
end

def test_source_location
if_ruby(..."4.1") do
assert_send_type '() -> [String, Integer]',
Expand Down