-
Notifications
You must be signed in to change notification settings - Fork 114
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
Add support for overloaded signatures and add overloaded ActiveRecordRelations sigs #1799
Add support for overloaded signatures and add overloaded ActiveRecordRelations sigs #1799
Conversation
Getting an unrelated test failure on Rails 7 for Rubies 3.0-3.3, but not Ruby head for |
@bdewater it's resolved on main, you can rebase. |
ac18693
to
43f8e1a
Compare
@KaanOzkan thanks, only failing check now is for missing labels which I can't add :) |
@bdewater This will need to be rebased on Otherwise, we would need to do feature checking to detect if the version of Sorbet that we are operating against has support for that or not. |
43f8e1a
to
5af5e29
Compare
Thanks for the feedback @paracycle! Rebased and incorporated your suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
8e689eb
to
46e6ae7
Compare
46e6ae7
to
4457b30
Compare
common_relation_methods_module.create_sig( | ||
parameters: [create_param("args", type: args_type)], | ||
return_type: "T::Enumerable[#{constant_name}]", | ||
), | ||
common_relation_methods_module.create_sig( | ||
parameters: [create_param("args", type: "T.untyped")], | ||
return_type: constant_name, | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern doesn't seem to work
# test.rbi
class Foo
sig { returns(T.untyped) }
def foo; end
sig { returns(T::Array[T.untyped]) }
def bar; end
end
sig { params(args: T::Array[T.untyped]).returns(T::Enumerable[Foo]) }
sig { params(args: T.untyped).returns(Foo) }
def find(args); end
# test.rb
T.reveal_type(find(Foo.new.foo)) # T::Enumerable[Foo]
T.reveal_type(find(Foo.new.bar)) # T::Enumerable[Foo]
First sig always takes precedence. I'm not sure why, maybe it's a sorbet bug. We could open a ticket and revert this change until its fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, that does feel like a bug. T::Array[T.untyped]
has to be first since T.untyped
otherwise would match everything, except in this particular case when it doesn't match T.untyped
🙈
We could also generates a bit more strict sigs and flip the order so that the common case is first in case Sorbet cannot resolve the correct sig:
sig { params(args: T.any(String, Integer)).returns(Foo) }
sig { params(args: T::Array[T.any(String, Integer)]).returns(T::Enumerable[Foo]) }
def find(args); end
I think this would cover ~all cases (UUID and other string PKs, (big)int PKs)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think that's good. I assume bug doesn't happen then. I feel like String and Integer is enough but I don't have much context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T.untyped argument would be matched against the first signature since it's a subtype of everything, as Jez pointed out in the linked Sorbet issue.
Also noticed this particular issue was documented on https://sorbet.org/docs/overloads 😅
In the presence of untyped arguments, chances are high that the first overload is selected, which might not be desired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8896389
to
455239b
Compare
The problem is that T.untyped is both the superclass and the subclass of all types in Sorbet, so that when given an untyped parameter the first signature (returning an array) would be selected whereas probably a single model instance would be intended (see the removed note in a previous commit about find being untyped in the first place). Integer and String (think UUID) should cover the most common cases, but it is technically possible to pass anything that can be quoted by Active Record except nil (which is disallowed by ActiveRecord::FinderMethods#find_with_ids).
455239b
to
d357096
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retried CI, strange failure.
sigs = [ | ||
common_relation_methods_module.create_sig( | ||
parameters: [create_param("args", type: id_types)], | ||
return_type: constant_name, | ||
), | ||
common_relation_methods_module.create_sig( | ||
parameters: [create_param("args", type: array_type)], | ||
return_type: "T::Enumerable[#{constant_name}]", | ||
), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing one overload:
find
can be passed a block, in which case it delegates to Enumerable#find
, which returns a nilable result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this covers it #1844
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's spot on. Thanks @KaanOzkan!
Motivation
Close #1789
Implementation
Based on the patch in aforementioned issue.
Tests
Updated tests.