-
Notifications
You must be signed in to change notification settings - Fork 172
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
Parse RBS overloads into signatures #2243
Conversation
ea7c625
to
c9a3dca
Compare
2f4c24e
to
640f9fc
Compare
(have moved the Ruby LSP changes out to |
640f9fc
to
cd5f555
Compare
@soutaro this is still WIP, but thought you may be interested to see our approach. |
bfaca26
to
4a2b446
Compare
# There are no methods in Core that have required keyword arguments, | ||
# so we test against RBS directly | ||
|
||
# TODO: why does removing the block break this? |
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.
Need to figure out why
def foo: (::Numeric a, ::Numeric b) -> self
is failing with:
Minitest::Assertion: Expected #<RubyIndexer::Entry::RequiredParameter:0x0000000129a54158 @name=:a>
to be a kind of RubyIndexer::Entry::KeywordParameter, not RubyIndexer::Entry::RequiredParameter.
4a2b446
to
4b683e4
Compare
assert_equal(1, entry.parameters.length) | ||
parameter = entry.parameters.first | ||
parameters = entry.signatures.first.parameters | ||
assert_equal(1, parameters.length) |
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.
These kind of length assertions were useful during development, but we could remove them to make the tests a little shorter.
|
||
first_signature = signatures.first | ||
|
||
# (::string salt_str) -> ::String |
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 copied in the RBS definitions for easy reference, but we could remove them.
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 like those, it's easier to understand 👍
end | ||
|
||
def test_rbs_method_with_optional_keywords | ||
entries = @index["step"] # https://www.rubydoc.info/stdlib/core/Numeric#step-instance_method |
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.
step
is an interesting one since it can called with either positional or keyword args.
There's one thing I need to fix but I'd like to start getting reviews on this. |
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 great
end | ||
|
||
sig { params(function: RBS::Types::Function).returns(T::Array[Entry::OptionalParameter]) } | ||
def process_trailing_positionals(function) |
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'll defer to you if you prefer this style, but I'm not a fan of this tiny methods that just map through one parameter type. I would just inline them all.
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 do generally prefer this way, I picked it from the guidance in Clean Code that methods should have only one level of abstraction.
|
||
first_signature = signatures.first | ||
|
||
# (::string salt_str) -> ::String |
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 like those, it's easier to understand 👍
function.required_positionals.map do |param| | ||
# Some parameters don't have names, e.g. | ||
# def self.try_convert: [U] (untyped) -> ::Array[U]? | ||
name = param.name || :object |
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.
Let's also use arg{index}
here instead of just object
.
|
||
name = param.name || Entry::KeywordRestParameter::DEFAULT_NAME | ||
|
||
[Entry::KeywordRestParameter.new(name: 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.
I'm disliking this pattern of tiny methods more and more. Can we at least redesign this so that we are not forced to create arrays just to concatenate in the call site?
sig { params(function: RBS::Types::Function).returns(T::Array[Entry::Parameter]) } | ||
def parse_arguments(function) | ||
parameters = [] | ||
parameters += process_required_positionals(function) if function.required_positionals |
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.
All of these +=
will generate intermediate arrays. Let's switch them all for concat
.
end | ||
|
||
sig { params(function: RBS::Types::Function).returns(T::Array[Entry::RequiredParameter]) } | ||
def process_required_and_optional_positionals(function) |
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 was prevously two methods, I combined them because we need a way to keep track of an index to use for arg0
etc, and this seemed the simplest way.
def process_required_and_optional_positionals(function) | ||
argument_offset = 0 | ||
|
||
required = function.required_positionals.map.with_index(argument_offset) do |param, i| |
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.
argument_offset
would default to 0 here, but I added the argument for symmetry with the other loop.
2a377fa
to
c272cf4
Compare
Motivation
In this PR we are beginning fuller indexing of RBS, by handline multiple signatures per method.
Implementation
Previously,
arguments
was a field ofEntry::Method
. Now anEntry::Method
has asignatures
field (of type array), and each signature has the array of arguments.Not covered yet:
Automated Tests
The tests are mostly using a selection of methods from Ruby core to verify the various forms of signatures in use.
Manual Tests
There is no integration with the LSP features yet so this cannot be easily manually tested.