Skip to content

Fix nested declarations in rbs prototype rbi#3029

Open
infiton wants to merge 2 commits into
ruby:masterfrom
infiton:fix/rbi-nested-declarations
Open

Fix nested declarations in rbs prototype rbi#3029
infiton wants to merge 2 commits into
ruby:masterfrom
infiton:fix/rbi-nested-declarations

Conversation

@infiton

@infiton infiton commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Fix rbs prototype rbi so nested Sorbet RBI declarations produce valid, semantically accurate RBS.

The converter previously flattened every class and module into a top-level declaration while leaving relative references unchanged. For example:

module Demo
  class Parent; end
  class Child < Parent; end
end

was emitted as class Demo::Child < Parent, where Parent no longer resolves relative to Demo. The same problem affected mixins and types in method signatures.

This PR preserves the RBI's lexical declaration nesting using the existing nested-declaration representation in the RBS AST. It also fixes the related conversion bugs found in the same reproduction.

Changes

  • Preserve nested class and module declarations instead of flattening them.
    • Relative superclasses, mixins, and signature types retain their lexical resolution context.
    • Absolute and explicitly qualified declaration names remain intact.
  • Emit nested constant declarations in their containing declaration.
    • This prevents duplicated paths such as Demo::Demo::Modes::VALUE.
  • Ignore the Sorbet-only T::Helpers extension, alongside T::Sig and T::Generic.
  • Convert T::Class[...] to untyped.
    • RBS has no exact generic equivalent for an arbitrary class object whose instances have the specified type.
    • Falling back avoids emitting an unresolved T::Class while not inventing an unsupported structural constraint.
  • Track class << self context so contained definitions become singleton methods.
  • Convert attr_reader, attr_writer, and attr_accessor, consuming their pending sig.
    • This prevents an attribute signature from becoming an overload on the following method.
  • Track declaration visibility and emit private/public transitions.
    • RBS does not model protected, so it uses the conservative private fallback.

Compatibility

This only changes RBI-to-RBS prototype conversion. It does not change the RBS language, parser, validator, or the meaning of existing .rbs files.

RBS::Prototype::RBI#decls now returns a nested AST for nested RBI declarations instead of a flattened list. For an empty declaration such as module Foo; module Bar; end; end, both forms define the same RBS constants, but callers that inspect the AST structure directly will observe the corrected shape. For declarations containing relative references, retaining that nesting is required for semantic correctness.

Tests

Regression tests cover each behavior independently and load the complete reported reproduction into an RBS environment, building its instance and singleton definitions.

bundle exec rake test TEST=test/rbs/rbi_prototype_test.rb
33 tests, 33 assertions, 0 failures, 0 errors

bundle exec rubocop lib/rbs/prototype/rbi.rb test/rbs/rbi_prototype_test.rb
2 files inspected, no offenses detected

infiton added 2 commits July 21, 2026 13:21
Assisted-By: devx/464d5233-705f-406e-a5d8-ad2479522aa9
Assisted-By: devx/464d5233-705f-406e-a5d8-ad2479522aa9
@infiton
infiton marked this pull request as ready for review July 21, 2026 22:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant