Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.0.0
3.4.4
20 changes: 19 additions & 1 deletion lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ def rewrite_def(def_node, comments)

sig = translator.result

# When the method uses Ruby 3.1+ anonymous block forwarding (`&` with no name),
# the RBI translator names the block param `&block`, producing `&block:` inside
# `params()` which is a syntax error. Detect that case and rename it to `block`
# (without the `&`) so the generated sig is valid Ruby.
if anonymous_block_param?(def_node)
sig.params.each do |param|
param.name = param.name.delete_prefix("&") if param.name.start_with?("&")
end
end
Comment on lines +191 to +195

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am not sure if this is the correct place to fix this problem. We need to solve it at the source.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah the correct place to fix this is in the RBI gem that Spoom uses for this translation:

https://github.com/Shopify/rbi/blob/4a4f30b9ca743f7723ce451dc06a9b7cf62a42b0/lib/rbi/rbs/method_type_translator.rb#L46


apply_member_annotations(comments.method_annotations, sig)

# Sorbet runtime doesn't support `sig` on `method_added` or
Expand Down Expand Up @@ -348,6 +358,14 @@ def apply_member_annotations(annotations, sig)
end
end

# Returns true if the def node uses an anonymous block parameter (`&` with no name),
# i.e. Ruby 3.1+ anonymous block forwarding like `def foo(&); end`.
#: (Prism::DefNode) -> bool
def anonymous_block_param?(def_node)
block_param = def_node.parameters&.block
block_param.is_a?(Prism::BlockParameterNode) && block_param.name.nil?
end

#: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode, Regexp) -> bool
def already_extends?(node, constant_regex)
node.child_nodes.any? do |c|
Expand Down Expand Up @@ -438,4 +456,4 @@ def apply_type_aliases(comments)
end
end
end
end
end
39 changes: 39 additions & 0 deletions test/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,45 @@ def rbs_comments_to_sorbet_sigs(ruby_contents, max_line_length: nil, overloads_s
overloads_strategy: overloads_strategy,
).rewrite
end

def test_rbs_comments_to_sorbet_sigs_anonymous_block_param
res = Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(<<~RUBY, file: "test.rb")
# typed: true
class Foo
#: (String) ?{ (String) -> void } -> String
def bar(request, &); end
end
RUBY

assert_equal(<<~RUBY, res)
# typed: true
class Foo
sig { params(request: String, block: ::T.nilable(::T.proc.params(arg0: String).void)).returns(String) }
def bar(request, &); end
Comment on lines +906 to +907

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

end
RUBY

# Must also be valid Ruby
assert RubyVM::InstructionSequence.compile(res)
Comment on lines +910 to +912

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is overkill, we don't need to waste time compiling it. Perhaps assert Prism.parse_success?(res) at most, but we match the exact expected source we want, so there's no need to confirm this here

Suggested change
# Must also be valid Ruby
assert RubyVM::InstructionSequence.compile(res)

end

def test_rbs_comments_to_sorbet_sigs_named_block_param_unchanged
res = Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(<<~RUBY, file: "test.rb")
# typed: true
class Foo
#: (String) ?{ (String) -> void } -> String
def bar(request, &block); end
end
RUBY

assert_equal(<<~RUBY, res)
# typed: true
class Foo
sig { params(request: String, block: ::T.nilable(::T.proc.params(arg0: String).void)).returns(String) }
def bar(request, &block); end
end
RUBY
end
end
end
end
Expand Down
Loading