8000 Better detection of enumerable type from an instance by paracycle · Pull Request #3358 · sorbet/sorbet · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Better detection of enumerable type from an instance #3358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 12, 2020

Conversation

paracycle
Copy link
Collaborator

When detecting the type of an enumerable from an instance, TypedEnumerable tries to match it to a couple of well-known enumerable classes (like Array, Hash, etc). The fallback case, however, tries to match it to the class of the type that the instance is being validated against, which does not make a lot sense.

That behaviour means, if the instance is being compared against T::Array[Foo], we would try to infer the type of the enumerable instance as a kind of T::Array. But, if it was being compared against a T::Set[Foo], then the type of the instance would be inferred to be a T::Set. This leads to the problem outlined in #2808.

In reality, Sorbet should not try to infer the type based on what type the instance is being compared against, but should return the type of the instance as is.

Motivation

Fixes: #2808

Test plan

See included automated tests.

@paracycle paracycle requested a review from a team as a code owner August 11, 2020 22:08
@paracycle paracycle requested review from elliottt and removed request for a team August 11, 2020 22:08
@jez
Copy link
Collaborator
jez commented Aug 11, 2020

Your tests are failing because Enumerable is a module, which might have been mixed into BasicObject which wouldn't necessarily have a .class method.

Probably the easiest way around this would be to add a case for when Object then obj.class and otherwise do something else.

Another trick would be to Object.instance_method(:class).bind(obj).call.

Either one (or something else) works for me.

Copy link
Collaborator
@jez jez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks fine from me, but I'm surprised to hear that it's still a problem on latest versions of sorbet-runtime (i.e., after #3293).

@paracycle
Copy link
Collaborator Author

@jez Thanks for the review, I'll make the relevant changes.

I believe this is still a user-visible error in the latest released version of sorbet-runtime, as can be seen by this repro script:

require "bundler/inline"

gemfile do
  source 'https://rubygems.org'
  gem "sorbet-runtime", "0.5.5866"
end

require "sorbet-runtime"

class Bar
  include Enumerable

  def each
    yield "something"
  end
end

class Foo
  extend T::Sig

  sig { returns(T::Array[String]) }
  def self.foo
    Bar.new
  end
end

p Foo.foo

with the output:

$ ruby foo.rb
Traceback (most recent call last):
	5: from foo.rb:27:in `<main>'
	4: from /Users/ufuk/.gem/ruby/2.6.5/gems/sorbet-runtime-0.5.5866/lib/types/private/methods/_methods.rb:228:in `block in _on_method_added'
	3: from /Users/ufuk/.gem/ruby/2.6.5/gems/sorbet-runtime-0.5.5866/lib/types/private/methods/call_validation.rb:138:in `validate_call'
	2: from /Users/ufuk/.gem/ruby/2.6.5/gems/sorbet-runtime-0.5.5866/lib/types/private/methods/call_validation.rb:1125:in `report_error'
	1: from /Users/ufuk/.gem/ruby/2.6.5/gems/sorbet-runtime-0.5.5866/lib/types/configuration.rb:226:in `call_validation_error_handler'
/Users/ufuk/.gem/ruby/2.6.5/gems/sorbet-runtime-0.5.5866/lib/types/configuration.rb:219:in `call_validation_error_handler_default': Return value: Expected type T::Array[String], got T::Array[String] (TypeError)
Caller: foo.rb:27
Definition: foo.rb:22

@jez
Copy link
Collaborator
jez commented Aug 11, 2020

I believe this is still a user-visible error in the latest released version of sorbet-runtime, as can be seen by this repro script:

This is great, can you add this as a test case?

When detecting the type of an enumerable from an instance, `TypedEnumerable`
tries to match it to a couple of well-known enumerable classes
(like `Array`, `Hash`, etc). The fallback case, however, tries to match it
to the class of the type that the instance is being validated against,
which does not make a lot sense.

That behaviour means, if the instance is being compared against
`T::Array[Foo]`, we would try to infer the type of the enumerable instance
as a kind of `T::Array`. But, if it was being compared against a `T::Set[Foo]`,
then the type of the instance would be inferred to be a `T::Set`. This leads
to the problem outlined in sorbet#2808.

In reality, Sorbet should not try to infer the type based on what
type the instance is being compared against, but should return the type of
the instance as is.
@paracycle paracycle force-pushed the uk-fix-type-name-reporting branch from c7eb400 to 2bf0e64 Compare August 12, 2020 18:25
@paracycle
Copy link
Collaborator Author

@jez I made the changes we discussed above and pushed this branch. Specifically:

  1. I went for the Object.instance_method(:class).bind(obj).call to get the class name of the object
  2. Left the test that I had added to assert the error message in place
  3. Added a test that would exercise the signature validation failing so that we test the user-visible case as well.

Please let me know if more changes are needed. Thanks

Copy link
Collaborator
@jez jez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@jez jez merged commit 73847a1 into sorbet:master Aug 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expected T::Array[X], got T::Array[X]
2 participants
0