diff --git a/lib/steep/type_construction.rb b/lib/steep/type_construction.rb index c1ee2a80..1e4f3b4b 100644 --- a/lib/steep/type_construction.rb +++ b/lib/steep/type_construction.rb @@ -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 diff --git a/test/type_construction_test.rb b/test/type_construction_test.rb index 9f799e95..61228190 100644 --- a/test/type_construction_test.rb +++ b/test/type_construction_test.rb @@ -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 /^\^\(::Integer\) -> X\(\d+\)$/ =~ error.expected.to_s + assert_equal parse_type("^(::String) -> ::String"), error.actual + end + end + + assert_equal parse_type("::Array[untyped]"), pair.context.type_env[:r] + end + end + end + def test_send_unsatisfiable_constraint with_checker(<