Search code examples
ruby-on-railsrubyruby-on-rails-6sorbetruby-2.7

Running srb rbi hidden-definitions gives unclear error


Context

  • Our team recently upgrade to rails 6 (from 5.2). AFAIK, things were working pretty well up to that point.
  • We have a slightly unconventional file structure so we opted to use the :classic rails loader instead of :zeitwerk

Problem

Whenever we run srb rbi hidden-definitions now we get the following:

Got LoadError when trying to get nested name Tilt::PandocTemplate
Got LoadError when trying to get nested name Tilt::AsciidoctorTemplate
Got LoadError when trying to get nested name Tilt::CreoleTemplate
Got LoadError when trying to get nested name Tilt::LessTemplate
Got LoadError when trying to get nested name Tilt::LiquidTemplate
Got LoadError when trying to get nested name Tilt::LiveScriptTemplate
Got LoadError when trying to get nested name Tilt::MarkabyTemplate
Got LoadError when trying to get nested name Tilt::PrawnTemplate
Got LoadError when trying to get nested name Tilt::RadiusTemplate
Got LoadError when trying to get nested name Tilt::RedClothTemplate
Got LoadError when trying to get nested name Tilt::RstPandocTemplate
Got LoadError when trying to get nested name Tilt::TypeScriptTemplate
Got LoadError when trying to get nested name Tilt::WikiClothTemplate
Got LoadError when trying to get nested name Tilt::YajlTemplate
Naming WebConsoleGot NameError when trying to get nested name WebConsole::SourceLocation_
Generating /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi with 20603 modules and 305 aliases
Printing your code's symbol table into /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/from-source.json
Printing /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi's symbol table into /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.json
Traceback (most recent call last):
        5: from /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/bin/srb-rbi:237:in `<main>'
        4: from /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/bin/srb-rbi:224:in `main'
        3: from /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/bin/srb-rbi:232:in `block in make_step'
        2: from /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/lib/hidden-definition-finder.rb:38:in `main'
        1: from /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/lib/hidden-definition-finder.rb:47:in `main'
/usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/sorbet-0.5.5891/lib/hidden-definition-finder.rb:151:in `write_constants': /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi had unexpected errors. Check this file for a clue: /var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.json.err (RuntimeError)

reflection.json.err (which the error points is to) is littered with countless errors that all look something like this:

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707623: dynamic constant assignment https://srb.help/2001
      707623 |  REFERENCE = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707624: dynamic constant assignment https://srb.help/2001
      707624 |  SETUTITSBUS = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707625: dynamic constant assignment https://srb.help/2001
      707625 |  SLAICEPS = ::T.let(nil, ::T.untyped)
                ^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707626: dynamic constant assignment https://srb.help/2001
      707626 |  SPECIALS = ::T.let(nil, ::T.untyped)
                ^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707627: dynamic constant assignment https://srb.help/2001
      707627 |  SUBSTITUTES = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707628: dynamic constant assignment https://srb.help/2001
      707628 |  VALID_CHAR = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707629: dynamic constant assignment https://srb.help/2001
      707629 |  VALID_XML_CHARS = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707618: class definition in method body https://srb.help/2001
      707618 |class REXML::Text < REXML::Child
              ^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707801: class definition in method body https://srb.help/2001
      707801 |class REXML::UndefinedNamespaceException < REXML::ParseException
              ^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707813: module definition in method body https://srb.help/2001
      707813 |module REXML::Validation
              ^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707817: class definition in method body https://srb.help/2001
      707817 |class REXML::Validation::ValidationException < RuntimeError
              ^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707829: dynamic constant assignment https://srb.help/2001
      707829 |  DEFAULT_ENCODING = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707830: dynamic constant assignment https://srb.help/2001
      707830 |  DEFAULT_STANDALONE = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^^^^^^^^

/var/folders/w4/01z4c79j22l59fhcgyl43f8w0000gn/T/d20200911-19233-vy4tg3/reflection.rbi:707831: dynamic constant assignment https://srb.help/2001
      707831 |  DEFAULT_VERSION = ::T.let(nil, ::T.untyped)
                ^^^^^^^^^^^^^^^

Question

Any suggestions on how to move forward? One hunch is that we need to migrate to :zeitwerk to resolve this, but that's a pretty large refactor for us so I'm hesitant to explore that without confirming that that is a potential source for this failure.


Solution

  • EDIT: A variant of this patch has now been upstreamed into Sorbet and will be available in versions >= 0.5.5911. For anyone using those versions, this answer is no longer relevant.


    I doubt that the error you are seeing is caused by any Rails configuration. The lines that you have posted from reflection.json.err do not show any real errors, either.

    It looks like you are on Ruby 2.7, so I suspect that the error you are seeing is because of how the argument forwarding is done in 2.7 via def foo(...) method definitions. At runtime these turn into def foo(**, &&) in Ruby, and when Sorbet tries to generate the argument names, it fails, since * and & are not valid argument names.

    You can try to patch the relevant method in sorbet gem using the diff I include below:

    diff --git a/lib/serialize.rb b/lib/serialize.rb
    index aec19ccb2..301793856 100644
    --- a/lib/serialize.rb
    +++ b/lib/serialize.rb
    @@ -337,6 +337,14 @@ class Sorbet::Private::Serialize
               uniq += 1
             end
           end
    +
    +      # Sanitize parameter names that are not valid
    +      name = name.to_s.gsub(/[^a-zA-Z0-9_]/) do
    +        result = '_' + (uniq == 0 ? '' : uniq.to_s)
    +        uniq += 1
    +        result
    +      end
    +
           [kind, name]
         end
       end
    

    You can use bundle open sorbet to open the sorbet gem installed as part of your Gemfile in your editor, apply the diff above and re-run the hidden definitions generator. If that solves your problem, I'd be happy to PR it upstream to get it in the next sorbet release.