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 Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ PATH
bundler (>= 2.2.25)
netrc (>= 0.11.0)
parallel (>= 1.21.0)
rbi (>= 0.4.1)
rbi (>= 0.4.3)
require-hooks (>= 0.2.2)
rubydex (>= 0.1.0.beta10)
sorbet-static-and-runtime (>= 0.6.12698)
Expand Down
28 changes: 2 additions & 26 deletions lib/tapioca/dsl/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,32 +173,8 @@ def compile_method_parameters_to_rbi(method_def)
parameters = method_def.parameters #: Array[[Symbol, Symbol?]]

parameters.each_with_index.map do |(type, name), index|
fallback_arg_name = "_arg#{index}"

sig_name = name ? name.to_s : fallback_arg_name
is_anonymous_parameter = anonymous_parameter_name?(type, sig_name)
sig_name = fallback_arg_name unless is_anonymous_parameter || valid_parameter_name?(sig_name)
param_name = is_anonymous_parameter ? nil : sig_name
method_type = T.must(method_types[index])

case type
when :req
create_param(sig_name, type: method_type)
when :opt
create_opt_param(sig_name, type: method_type, default: "T.unsafe(nil)")
when :rest
create_rest_param(param_name, type: method_type)
when :keyreq
create_kw_param(sig_name, type: method_type)
when :key
create_kw_opt_param(sig_name, type: method_type, default: "T.unsafe(nil)")
when :keyrest
create_kw_rest_param(param_name, type: method_type)
when :block
create_block_param(param_name, type: method_type)
else
raise "Unknown type `#{type}`."
end
parameter, = create_method_parameter(type, name&.to_s, index)
create_typed_param(parameter, T.must(method_types[index]))
end
end

Expand Down
43 changes: 8 additions & 35 deletions lib/tapioca/gem/listeners/methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,8 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public

parameters = method.parameters #: Array[[Symbol, Symbol?]]

sanitized_parameters = parameters.each_with_index.map do |(type, name), index|
fallback_arg_name = "_arg#{index}"

sig_name = if name
compiled_parameters = parameters.each_with_index.map do |(type, name), index|
parameter_name = if name
name.to_s
else
# For attr_writer methods, Sorbet signatures have the name
Expand All @@ -119,21 +117,11 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public
signature.arg_types.size == 1 &&
method_name[-1] == "="

if writer_method_with_sig
method_name.delete_suffix("=")
else
fallback_arg_name
end
method_name.delete_suffix("=") if writer_method_with_sig
end

# Sanitize param names, except for anonymous splat, keyword splat,
# and block parameters. Ruby reflects those as `:*`, `:**`, and `:&`,
# and Sorbet signatures use the same names to store their types.
is_anonymous_parameter = anonymous_parameter_name?(type, sig_name)
sig_name = fallback_arg_name unless is_anonymous_parameter || valid_parameter_name?(sig_name)
param_name = is_anonymous_parameter ? nil : sig_name

[type, param_name, sig_name]
parameter, signature_name = create_method_parameter(type, parameter_name, index)
[type, parameter, signature_name]
end

rbi_method = RBI::Method.new(
Expand All @@ -142,26 +130,11 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public
visibility: visibility,
)

sanitized_parameters.each do |type, param_name, _sig_name|
case type
when :req
rbi_method << RBI::ReqParam.new(param_name)
when :opt
rbi_method << RBI::OptParam.new(param_name, "T.unsafe(nil)")
when :rest
rbi_method << RBI::RestParam.new(param_name)
when :keyreq
rbi_method << RBI::KwParam.new(param_name)
when :key
rbi_method << RBI::KwOptParam.new(param_name, "T.unsafe(nil)")
when :keyrest
rbi_method << RBI::KwRestParam.new(param_name)
when :block
rbi_method << RBI::BlockParam.new(param_name)
end
compiled_parameters.each do |_, parameter, _|
rbi_method << parameter
end

parameters_for_signature = sanitized_parameters.map { |type, _param_name, sig_name| [type, sig_name] }
parameters_for_signature = compiled_parameters.map { |type, _, name| [type, name] }
@pipeline.push_method(symbol_name, constant, method, rbi_method, signature, parameters_for_signature)
tree << rbi_method
end
Expand Down
35 changes: 33 additions & 2 deletions lib/tapioca/helpers/rbi_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,45 @@ def extract_type_parameters(type_strings)
type_strings.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq
end

#: (Symbol type, String? name, Integer index) -> [RBI::Param, String]
def create_method_parameter(type, name, index)
is_anonymous_parameter = name && anonymous_parameter_name?(type, name)
name = "_arg#{index}" unless is_anonymous_parameter || valid_parameter_name?(name)
name = T.must(name)
parameter_name = is_anonymous_parameter ? nil : name

parameter = case type
when :req
RBI::ReqParam.new(name)
when :opt
RBI::OptParam.new(name, "T.unsafe(nil)")
when :rest
RBI::RestParam.new(parameter_name)
when :keyreq
RBI::KwParam.new(name)
when :key
RBI::KwOptParam.new(name, "T.unsafe(nil)")
when :keyrest
RBI::KwRestParam.new(parameter_name)
when :block
RBI::BlockParam.new(parameter_name)
when :nokey
RBI::NoKwParam.new
else
Kernel.raise "Unknown type `#{type}`."

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.

TIL, agent found that :nokey is a valid parameter kind for: def foo(*args, **nil). Apparently it's used to reject kwargs. It's not supported in rbi but Sorbet doesn't support it either so not raising might be enough.

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.

There will also be a :noblock version of it in Ruby 4.1: ruby/ruby@9967418

def foo(*args, &nil) = 42

method(:foo).parameters # => [[:rest, :args], [:noblock]]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added support to rbi: Shopify/rbi#639

end

[parameter, name]
end

#: (String name) -> bool
def valid_method_name?(name)
Prism.parse_success?("def self.#{name}(a); end")
end

#: (String name) -> bool
#: (String? name) -> bool
def valid_parameter_name?(name)
Prism.parse_success?("def sentinel_method_name(#{name}:); end")
name ? Prism.parse_success?("def sentinel_method_name(#{name}:); end") : false
end

#: (Symbol type, String name) -> bool
Expand Down
16 changes: 16 additions & 0 deletions spec/tapioca/gem/pipeline_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,22 @@ def add(a, b:); end
assert_equal(output, compile)
end

it "compiles methods that reject keyword arguments" do
add_ruby_file("foo.rb", <<~RUBY)
class Foo
def no_kwargs(**nil); end
end
RUBY

output = template(<<~RBI)
class Foo
def no_kwargs(**nil); end
end
RBI

assert_equal(output, compile)
end

it "compiles default arguments" do
add_ruby_file("foo.rb", <<~RUBY)
class Foo
Expand Down
4 changes: 4 additions & 0 deletions spec/tapioca/helpers/rbi_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ class Tapioca::RBIHelperSpec < Minitest::Spec
end
end

it "rejects missing parameter names" do
refute(valid_parameter_name?(nil))
end

it "rejects invalid parameter names" do
[
"",
Expand Down
6 changes: 3 additions & 3 deletions tapioca.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ Gem::Specification.new do |spec|
spec.add_dependency("sorbet-static-and-runtime", ">= 0.6.12698")
spec.add_dependency("thor", ">= 1.2.0")

# Tapioca requires a specific minimum versions of RBI and Spoom
# to ensure that the RBS comments are translated correctly.
spec.add_dependency("rbi", ">= 0.4.1")
# Tapioca requires specific minimum versions of RBI and Spoom
# to support the APIs used when generating RBIs.
spec.add_dependency("rbi", ">= 0.4.3")
spec.add_dependency("spoom", ">= 1.7.16")
# We need this to be ported to the RBS 4.0 branch before we can remove this dependency:
# https://github.com/ruby/rbs/pull/2601
Expand Down
Loading