AaronC81 / sord

Convert YARD docs to Sorbet RBI and Ruby 3/Steep RBS files
https://sord.aaronc.cc
MIT License
299 stars 18 forks source link

How to ensure valid RBS for `include Enumerable`? #148

Open thomthom opened 2 years ago

thomthom commented 2 years ago

Given a class that implement Enumerable:

class Something

  include Enumerable

  # @yield [key, value]
  # @yieldparam [Integer] key
  # @yieldparam [String] value
  # @return void
  def each
    yield 123
  end

end

The generated output is:

class Something
  include Enumerable

  # _@return_ — void
  def each: () ?{ (Integer key, String value) -> void } -> untyped
end

If I then run steep check on this I get errors:

# Type checking files:

............................................F..............F.......

src/enum.rb:1:0: [error] UnexpectedError: sig/example.rbs:1:0...6:3: ::Enumerable expects parameters [Elem], but given args []
│ Diagnostic ID: Ruby::UnexpectedError
│
└ class Something
  ~~~~~~~~~~~~~~~

sig/example.rbs:1:0: [error] Type `::Enumerable` is generic but used as a non generic type
│ Diagnostic ID: RBS::InvalidTypeApplication
│
└ class Something
  ~~~~~~~~~~~~~~~

Detected 2 problems from 2 files

If I alter the RBS to include a param for the Enumerable:

class Something
  include Enumerable[untyped]

  # _@return_ — void
  def each: () ?{ (Integer key, String value) -> void } -> untyped
end

Then it passes:

C:\Users\Thomas\SourceTree\sord-test>steep check
# Type checking files:

...................................................................

No type error detected. 🍵

I'm not actually 100% sure what param to give Enumerable in the case of each yielding key and value params. hence the untyped in this case. But given that you have a collection object that return String objects, then I presume it should be include Enumerable[String]. (?) Is there a way I can make sord generate an RBS that will not cause an error in steep?

AaronC81 commented 1 year ago

Not currently, sorry... I'm not sure how exactly this could be encoded either, since I'm not aware of any way to specify this in YARD. Looking up the type of #each feels possible but a bit hacky and fragile! Do you have any suggestions?

tk0miya commented 1 year ago

I also met a similar case with Struct.

Input:

require 'struct'

Foo = Struct.new(:bar)

Generated:

class Foo < Struct
  # Returns the value of attribute bar
  attr_accessor bar: Object
end

Result of rbs validate command:

/Users/tkomiya/.dotfiles/_rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/rbs-3.1.1/lib/rbs/errors.rb:90:in `check!': sig/sord.rbs:1:0...4:3: ::Struct expects parameters [Elem], but given args [] (RBS::InvalidTypeApplicationError)

It would be nice if we can give a hint to the sord via comments or annotations.