Skip to content
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

Merged
merged 1 commit into from
Jul 9, 2024

Conversation

andyw8
Copy link
Contributor

@andyw8 andyw8 commented Jul 2, 2024

Motivation

In this PR we are beginning fuller indexing of RBS, by handline multiple signatures per method.

Implementation

Previously, arguments was a field of Entry::Method. Now an Entry::Method has a signatures field (of type array), and each signature has the array of arguments.

Not covered yet:

  • Return types
  • Individual block arguments

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.

@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch 2 times, most recently from ea7c625 to c9a3dca Compare July 3, 2024 14:53
@andyw8 andyw8 added enhancement New feature or request server This pull request should be included in the server gem's release notes labels Jul 3, 2024
@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch from 2f4c24e to 640f9fc Compare July 4, 2024 14:23
@andyw8
Copy link
Contributor Author

andyw8 commented Jul 4, 2024

(have moved the Ruby LSP changes out to andyw8/support-multiple-signatures)

@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch from 640f9fc to cd5f555 Compare July 4, 2024 14:26
@andyw8
Copy link
Contributor Author

andyw8 commented Jul 4, 2024

@soutaro this is still WIP, but thought you may be interested to see our approach.

@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch 2 times, most recently from bfaca26 to 4a2b446 Compare July 5, 2024 19:32
# 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?
Copy link
Contributor Author

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.

@andyw8 andyw8 changed the title WIP: Parse RBS overloads into signatures Parse RBS overloads into signatures Jul 5, 2024
@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch from 4a2b446 to 4b683e4 Compare July 5, 2024 19:36
assert_equal(1, entry.parameters.length)
parameter = entry.parameters.first
parameters = entry.signatures.first.parameters
assert_equal(1, parameters.length)
Copy link
Contributor Author

@andyw8 andyw8 Jul 5, 2024

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
Copy link
Contributor Author

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.

Copy link
Member

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
Copy link
Contributor Author

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.

@andyw8
Copy link
Contributor Author

andyw8 commented Jul 5, 2024

There's one thing I need to fix but I'd like to start getting reviews on this.

@andyw8 andyw8 marked this pull request as ready for review July 5, 2024 19:44
@andyw8 andyw8 requested a review from a team as a code owner July 5, 2024 19:44
@andyw8 andyw8 requested review from st0012 and vinistock July 5, 2024 19:44
Copy link
Member

@vinistock vinistock left a comment

Choose a reason for hiding this comment

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

This is great

lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb Outdated Show resolved Hide resolved
lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb Outdated Show resolved Hide resolved
lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb Outdated Show resolved Hide resolved
end

sig { params(function: RBS::Types::Function).returns(T::Array[Entry::OptionalParameter]) }
def process_trailing_positionals(function)
Copy link
Member

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.

Copy link
Contributor Author

@andyw8 andyw8 Jul 9, 2024

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.

lib/ruby_indexer/test/rbs_indexer_test.rb Outdated Show resolved Hide resolved

first_signature = signatures.first

# (::string salt_str) -> ::String
Copy link
Member

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 👍

lib/ruby_indexer/test/rbs_indexer_test.rb Show resolved Hide resolved
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
Copy link
Member

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)]
Copy link
Member

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
Copy link
Member

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)
Copy link
Contributor Author

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|
Copy link
Contributor Author

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.

@andyw8 andyw8 force-pushed the andyw8/parse-rbs-overloads-into-signatures branch from 2a377fa to c272cf4 Compare July 9, 2024 19:51
@andyw8 andyw8 enabled auto-merge (squash) July 9, 2024 19:52
@andyw8 andyw8 merged commit e27dec2 into main Jul 9, 2024
33 checks passed
@andyw8 andyw8 deleted the andyw8/parse-rbs-overloads-into-signatures branch July 9, 2024 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request server This pull request should be included in the server gem's release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants