Open marknuzz opened 4 days ago
I have something crude as a proof of concept for setting the type of a let method. I am not sure if this is the right approach though, it is slow, and as the DSL compilers run with RAILS_ENV=development, I don't think it's a good idea to use outright. If there was a way to inject a method sig to the generated method, that could be better. Or maybe the method source can be analyzed in some manner similar to what sorbet-runtime does, to extract the intended return type which can be set explicitly.
modules.each do |mod|
scope = root.create_module(T.must(mod.name))
let_defs_module = ::RSpec::Core::MemoizedHelpers.module_for(constant)
methods_types = T.let({}, T::Hash[Symbol, String])
return_value = T.unsafe(nil)
::RSpec::Mocks.with_temporary_scope do
direct_public_instance_methods_for(mod).each do |method_name|
let_method = let_defs_module.instance_method(method_name)
bound_method = let_method.bind(constant.new)
return_value = bound_method.call
methods_types[method_name] = return_value.class.to_s
rescue StandardError, NotImplementedError
methods_types[method_name] = 'T.untyped'
end
end
direct_public_instance_methods_for(mod).each do |method_name|
method_def = mod.instance_method(method_name)
scope.create_method(
method_def.name.to_s,
parameters: compile_method_parameters_to_rbi(method_def),
return_type: methods_types[method_name],
class_method: false
)
end
Here's my work in progress for improving it to allow for sigs to be defined on let blocks. It's hacky and needs to be cleaned up but it seems like it will work, and I've been wanting to write an AST processor for awhile now. I was able to build a signature dynamically and will be able to identify it by location on the AST, then after matching the two, use Tapioca's sanitize_signature_types(signature.return_type.to_s)
and duplicate the signature's return type correctly in the RBI.
https://gist.github.com/marknuzz/c7598efa664fe944d790e16464280cd1
This is a great tool, I didn't know about it until now. It took me a couple hours to set up but I can say you've done a great job with it.
It would be great if we could specify types for the let blocks so that referencing those will get you the type originally returned. It can infer the type, but should also let you specify the type as well. I don't know if either of them will require a monkey patch to rspec itself though, or an alternative method to let or let! that takes an extra argument for the type.