A prepended module can cause Tapioca to not find the signature for a method, leading to it generating an untyped signature. Worse yet, this can cause a difference between how tapioca dsl and tapioca dsl Foo work:
tapioca dsl does an eager load, which can load the Ruby file which prepends the module onto Foo. The RBI will have an untyped signature.
tapioca dsl Foo doesn't eager load, so it might not load the file which has the prepended module. The RBI will have a typed signature.
Cause
This stems from the behaviour of T::Utils.signature_for_method, which looks up a signature for a given owner_id/method_name pair. If a prepended module doesn't have a sig, it'll return nil` (even if the method it overrides does have a sig).
require "sorbet-runtime"
class Foo
extend T::Sig
sig { returns(String) }
def foo = "original"
end
p T::Utils.signature_for_instance_method(Foo, :foo) # => the real sig
module M
def foo = "prepended"
end
Foo.prepend(M)
p T::Utils.signature_for_instance_method(Foo, :foo) # => nil
Potential solutions
Keep searching until we find a sig
#: (UnboundMethod) -> [UnboundMethod, T::Private::Methods::Signature]
def first_signature(method)
while method
signature = T::Utils.signature_for_method(method)
return [method, signature] if signature
method = method.super_method
end
nil
end
method, sig = first_signature(Foo.instance_method(:foo))
p sig
This might be desirable in this case, but cause weirdness in other cases (e.g. when an method override doesn't have a sig, but its method parameter declaration differs from that of its parent method). Needs investigation.
A prepended module can cause Tapioca to not find the signature for a method, leading to it generating an untyped signature. Worse yet, this can cause a difference between how
tapioca dslandtapioca dsl Foowork:tapioca dsldoes an eager load, which can load the Ruby file which prepends the module ontoFoo. The RBI will have an untyped signature.tapioca dsl Foodoesn't eager load, so it might not load the file which has the prepended module. The RBI will have a typed signature.Cause
This stems from the behaviour of T::Utils.signature_for_method
, which looks up a signature for a given owner_id/method_name pair. If a prepended module doesn't have a sig, it'll returnnil` (even if the method it overrides does have a sig).Potential solutions
Keep searching until we find a sig
This might be desirable in this case, but cause weirdness in other cases (e.g. when an method override doesn't have a sig, but its method parameter declaration differs from that of its parent method). Needs investigation.