rbi icon indicating copy to clipboard operation
rbi copied to clipboard

Bug: Double Parentheses in Union Types with positional_names: false

Open emalinegayhart opened this issue 1 month ago • 1 comments

When converting Sorbet signatures to RBS format with positional_names: false, union types in required parameters are wrapped in double parentheses.

Running dev rbs should turn: sig { params(x: T.any(Symbol, String)).returns(Integer) }def foo(x); end

into: def foo: (Symbol | String) -> Integer

but it actually does this: def foo: ((Symbol | String)) -> Integer

Root Cause The TypePrinter#visit_any method always wraps union types in parentheses (A | B). When print_sig_param prints parameters without positional names, these parentheses are combined with the method signature parameter list parentheses, creating ((A | B)).

Fix?? strip outer parentheses from union types for required parameters only (optional and rest parameters need the inner parens for clarity: ?(A | B) and *(A | B)).

Related file: rbsprinter.rb line

emalinegayhart avatar Nov 19 '25 23:11 emalinegayhart

I believe this is still valid with the parenthesis right?

See on sorbet.run, all these examples are valid syntax:

#: ((Symbol | String)) -> void
def foo(x); end

#: (Symbol | String) -> void
def bar(x); end

#: ((Symbol | String) x) -> void
def baz(x); end

#: (Symbol | String x) -> void
def qux(x); end)

We could try to omit the parenthesis but it becomes tricky when the type is used in return position (sorbet.run):

#: -> (Symbol | String)
def foo; T.unsafe(nil); end

#: -> Symbol | String
#            ^ error: Failed to parse RBS signature (expected a token `pEOF`)
def bar(x); T.unsafe(nil); end

I don't see an easy fix unless the RBS parser starts to accept return union without parenthesis.

Maybe you could bring it to https://github.com/ruby/rbs/issues?

Morriar avatar Nov 20 '25 17:11 Morriar