Skip to content

Fully qualify T and NilClass references in generated RBI output#2556

Draft
sambostock wants to merge 1 commit intomainfrom
sam/fully-qualify-t-references
Draft

Fully qualify T and NilClass references in generated RBI output#2556
sambostock wants to merge 1 commit intomainfrom
sam/fully-qualify-t-references

Conversation

@sambostock
Copy link
Copy Markdown
Contributor

@sambostock sambostock commented Mar 27, 2026

Summary

Fully qualifies T references as ::T and NilClass as ::NilClass in all generated RBI output.

When a class defines a type member named T (e.g., via #: [T] in RBS), unqualified T::Boolean, T.nilable(...), etc. in generated RBI resolve to the type member instead of Sorbet's ::T module, causing type-checking errors. These types come from RBS translations (boolT::Boolean, untypedT.untyped, nilNilClass) and unambiguously always mean the top-level constants.

Navigating the diff

Tip

Most of this diff is mechanical T::T and NilClass::NilClass string literal replacements. The interesting logic changes are in a handful of files:

Core qualification logic:

  • lib/tapioca/helpers/rbi_helper.rb — Adds catch-all regex to sanitize_signature_types that qualifies T::::T::, T.::T., and NilClass::NilClass. Updates as_nilable_type to emit ::T.nilable(...).

Places that called .to_s on Sorbet runtime types (producing unqualified strings):

  • lib/tapioca/dsl/helpers/active_model_type_helper.rb — Applies qualification regex to type.to_s output
  • lib/tapioca/dsl/helpers/graphql_type_helper.rb — Includes RBIHelper, routes return_type.to_s through sanitize_signature_types
  • lib/tapioca/gem/listeners/sorbet_required_ancestors.rb — Routes ancestor.to_s through sanitize_signature_types
  • lib/tapioca/sorbet_ext/generic_name_patch.rb — Routes type variable bounds .to_s through sanitize_signature_types

Avoided double-qualification:

  • lib/tapioca/dsl/compilers/identity_cache.rbCOLLECTION_TYPE lambda updated to not blindly prepend :: when the type string is already qualified

Everything else in lib/ and spec/ is mechanical s/T::/::T::/ and s/T\./::T./ in string literals and test expectations.

Test plan

  • All 784 existing tests pass (0 failures, 0 errors)
  • Run tapioca dsl on a project with #: [T] generics to confirm the fix
  • Verify Sorbet accepts the fully-qualified output

🤖 Generated with Claude Code

When a class defines a type member named `T` (e.g., via `#: [T]` in RBS),
Tapioca's generated RBI files contained unqualified `T::Boolean`,
`T.nilable(...)`, etc. These resolve to the type member instead of
Sorbet's `::T` module, causing type-checking errors.

This commit qualifies all `T` references as `::T` and `NilClass` as
`::NilClass` in generated RBI output. These types come from RBS
translations (e.g., `bool` → `T::Boolean`, `nil` → `NilClass`) and
unambiguously always refer to the top-level constants.

Changes:
- Add qualification regex to `sanitize_signature_types` as a catch-all
  for type strings produced by Sorbet's `.to_s`
- Update all hardcoded type string literals across DSL compilers/helpers
- Qualify `.to_s` output on Sorbet types in gem pipeline listeners
- Update `as_nilable_type` to produce `::T.nilable(...)`
- Update all test expectations to match qualified output

Co-Authored-By: Brad Lindsay <brad.lindsay@shopify.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sambostock sambostock force-pushed the sam/fully-qualify-t-references branch from 72556e0 to 32100bd Compare March 27, 2026 21:25
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