Shopify/tapioca

`tapioca gem` misses sig for module_function methods of the class variety

ryanong opened this issue · 1 comments

When generating sorbet/rbi/gems/code_ownership@1.37.0.rbi with tapioca gem code_ownership module_function defined methods will be created without signatures.

# Gemfile
source "https://rubygems.org"

gem 'tapioca', require: false, :group => [:development, :test]
gem "code_ownership", "1.37.0"

Specifically

# code_ownership.rb

module CodeOwnership
  module_function
  sig { params(klass: T.nilable(T.any(::Module, T::Class[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }
  def for_class(klass); end
end

will be turned into

# code_ownership.rbi

module CodeOwnership
  sig { params(klass: T.nilable(T.any(::Module, T::Class[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }; def for_class(klass); end

  class << self
    def for_class(klass); end
  end
end

should be turned into

# code_ownership.rbi

module CodeOwnership
  sig { params(klass: T.nilable(T.any(::Module, T::Class[T.anything]))).returns(T.nilable(::CodeTeams::Team)); def for_class(klass); end

  class << self
    sig { params(klass: T.nilable(T.any(::Module, T::Class[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }; def for_class(klass); end
  end
end

OR

# code_ownership.rbi

module CodeOwnership
  module_function
  sig { params(klass: T.nilable(T.any(::Module, T::Class[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }; def for_class(klass); end
end

I tried testing this in the rbi gem but it correctly parsed the module to the last one above

I'll gladly dive into the code to fix this if somebody could point out where this is an issue. I did some code diving as you can see already but I'm a bit lost where

I don't think the pipeline listens to method calls (send nodes) so we need to have the method definition in tapioca. I think this could be done in rbi although I don't think module_function is very popular so it might not be worth it.

I assume rbi parses "correctly" because it doesn't deal with runtime, so module_function is just a method call and didn't define anything during its execution. rbi would need to be aware of module_function and convert that call into a method node. Similar to how attr_* works https://github.com/Shopify/rbi/blob/main/lib/rbi/rewriters/attr_to_methods.rb (cc @Morriar if you have any thoughts)