Skip to content

Fix initialize return type when sig uses .void.checked(:tests)#2579

Open
dduugg wants to merge 1 commit intoShopify:mainfrom
dduugg:de/fix-initialize-void-checked-tests
Open

Fix initialize return type when sig uses .void.checked(:tests)#2579
dduugg wants to merge 1 commit intoShopify:mainfrom
dduugg:de/fix-initialize-void-checked-tests

Conversation

@dduugg
Copy link
Copy Markdown
Contributor

@dduugg dduugg commented Apr 3, 2026

Motivation

When a Sorbet sig uses .void.checked(:tests), the Sorbet runtime introspects the return_type as T::Types::Anything outside of test mode. Tapioca then serializes this as returns(T.anything) in the generated gem RBI, which is invalid — initialize must always return void in Sorbet.

This produces RBI files that fail the Sorbet/InitializeShouldReturnVoid RuboCop cop, causing pre-commit hook failures for anyone running tapioca gems on a codebase that uses .void.checked(:tests) on initialize.

Repro:

class Foo
  extend T::Sig
  sig { params(x: Integer).void.checked(:tests) }
  def initialize(x); end
end

sig = T::Utils.signature_for_method(Foo.instance_method(:initialize))
sig.return_type.class #=> T::Types::Anything  (not T::Private::Types::Void)
sig.return_type.to_s  #=> "T.anything"

Note: .void.checked(:tests) causes T.anything to be introspected as the return type for any method with that sig pattern, not just initialize. However, only initialize produces a hard srb tc failure (Sorbet requires it to return void). There is no stable Sorbet API to distinguish "declared void, introspected as T.anything" for other methods, so the broader case is out of scope here.

Implementation

In SorbetSignatures#on_method, after compiling the sig, unconditionally set return_type to "void" when the method is initialize. Since Sorbet requires initialize to always return void, this is always correct regardless of what the signature's return type introspects to.

Tests

Added a spec case to pipeline_spec.rb covering an initialize method with .void.checked(:tests), asserting the generated RBI emits void.

When a Sorbet sig uses `.void.checked(:tests)`, the Sorbet runtime
introspects the return type as `T::Types::Anything` outside of test
mode. Tapioca then serialized this as `returns(T.anything)` in the
generated gem RBI, which is invalid — `initialize` must always return
void.

Fix `SorbetSignatures#on_method` to always emit `void` as the return
type for `initialize` methods, regardless of what the signature's
return type introspects to.
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