Skip to content

Commit

Permalink
Support &method(:name) call for block_pass
Browse files Browse the repository at this point in the history
Pass `&method(:name)` to the block argument on method call is usually
used, and well-known idiom in Ruby.  Therefore it is valuable to
support the idiom on type check.

This gives a special treatment to it to support type checking.
  • Loading branch information
tk0miya committed Oct 9, 2024
1 parent 3b4c928 commit 66880ac
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
12 changes: 11 additions & 1 deletion lib/steep/type_construction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2401,7 +2401,17 @@ def synthesize(node, hint: nil, condition: false)
if value_node
type, constr = synthesize(value_node, hint: hint)

if hint.is_a?(AST::Types::Proc) && value_node.type == :sym
if hint.is_a?(AST::Types::Proc) && value_node.type == :send && value_node.children[1] == :method && type.is_a?(AST::Types::Name::Instance) && type.name.to_s == '::Method'
method_name = value_node.children[2].children[0]
if method = calculate_interface(AST::Types::Self.instance, private: true)&.methods&.[](method_name)
# TODO: select appropriate method type from `method.method_types`.
type = AST::Types::Proc.new(
type: method.method_types.first&.type || raise,
block: nil,
self_type: nil
)
end
elsif hint.is_a?(AST::Types::Proc) && value_node.type == :sym
if hint.one_arg?
if hint.type.params
# Assumes Symbol#to_proc implementation
Expand Down
56 changes: 56 additions & 0 deletions test/type_construction_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6950,6 +6950,62 @@ def m: [T] () { (untyped) -> T } -> T
end
end

def test_block_pass_and_method__valid
with_checker(<<-RBS) do |checker|
class Object
def method: (Symbol) -> Method
def my_to_s: (Integer) -> String
end
class Method
end
RBS
source = parse_ruby(<<-EOF)
x = [1, 2, 3]
r = x.map(&method(:my_to_s))
EOF

with_standard_construction(checker, source) do |construction, typing|
pair = construction.synthesize(source.node)

assert_no_error typing

assert_equal parse_type("::Array[::String]"), pair.context.type_env[:r]
end
end
end

def test_block_pass_and_method__invalid
with_checker(<<-RBS) do |checker|
class Object
def method: (Symbol) -> Method
def my_to_s: (String) -> String
end
class Method
end
RBS
source = parse_ruby(<<-EOF)
x = [1, 2, 3]
r = x.map(&method(:my_to_s))
EOF

with_standard_construction(checker, source) do |construction, typing|
pair = construction.synthesize(source.node)

assert_typing_error typing, size: 1 do |errors|
assert_any!(errors) do |error|
assert_instance_of Diagnostic::Ruby::BlockTypeMismatch, error
assert_equal "^(::Integer) -> X(1)", error.expected.to_s
assert_equal "^(::String) -> ::String", error.actual.to_s
end
end

assert_equal parse_type("::Array[untyped]"), pair.context.type_env[:r]
end
end
end

def test_send_unsatisfiable_constraint
with_checker(<<RBS) do |checker|
class SendTest
Expand Down

0 comments on commit 66880ac

Please sign in to comment.