Aliasing `.new` doesn't reuse the signature from `#initialize`
I'm running into this with the money gem:
# .rb
class Money
class << self
alias_method :from_cents, :new
end
def initialize( obj, currency = Money.default_currency, options = {})
# ...
end
end
# generated .rbi
class Money
# source://money//lib/money/money.rb#341
def initialize(obj, currency = T.unsafe(nil), options = T.unsafe(nil)); end
class << self
def from_cents(*_arg0); end
end
end
Also note that no source:// pointer comment is generated for the .new alias.
I get that technically you could override both .new and #initialize to have different signatures, but I'd be surprised if that's a common pattern. AFAICT Sorbet uses #initialize's signature to validate calls to .new.
What do you mean by signature in this case?
The method signature? Tapioca doesn't generate a signature for .new, but it does for the method aliasing .new. The issue is that it doesn't use #initialize's signature for that, which makes Sorbet complain about mismatched arguments when trying to improve the method signature in a shim.
I'm not too familiar with tapioca internals, but I think what would be needed would be to add something like the following:
# UnboundMethod method -> UnboundMethod?
def fallback(method) # there's most likely a better name for this?
return unless method.source_location.nil?
return unless method.original_name == :new
initialize = method.receiver.instance_method(:initialize)
return if initialize.source_location.nil?
initialize
end
# in Tapioca::Gem::Listeners::Methods#compile_method
parameters = T.let(fallback(method) || method).parameters, T::Array[[Symbol, T.nilable(Symbol)]])
# in Tapioca::Gem::Listeners::SourceLocation#on_method
file, line = (fallback(event.method) || event.method).source_location
In Tapioca and static typing context signature usually means sig { } so I was confused. Looks like what you want is from_cents having the same parameter list as initialize, def from_cents(obj, currency = Money.default_currency, options = {})
Yes we can special case new like in your example. I don't think we support aliased methods at all so you can first build out setting the parameter list from method.original_name but then do the special case for new/initialize.
I don't think this is a feature we'll prioritize but if you want to explore this feel free to go ahead.