Resolve a self return type per union arm - #1314
Draft
apiology wants to merge 3 commits into
Draft
Conversation
A method whose RBS return type is `self` must resolve to the arm of a
union receiver that supplied the pin, not to the whole union.
# @PARAM check_name [String, Symbol]
# @return [Symbol]
def to_sym_union(check_name)
check_name.to_sym # inferred ::Symbol, ::String
end
`String#to_sym` is `-> ::Symbol` and `Symbol#to_sym` is `-> self`.
Chain::Call#resolve split the binder into arms only to collect method
pins, then flattened them and called #inferred_pins once with a name_pin
still bound to the whole union, so `self` expanded to `String, Symbol`.
Resolve each arm's pins against that arm, then dedup on both path and
resolved return type so a shared self-returning pin (e.g. Kernel#itself)
still contributes every arm.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
The per-arm resolution added in the previous commit is not RBS-specific:
`self_to_type` runs on the pin's parsed `ComplexType`, so a YARD
`@return [self]` tag reaches the same code as an RBS `-> self`. On a
pure-YARD file with no RBS involved, `solargraph typecheck --level
strong` reports on master:
Declared return type ::Symbol, ::Beta does not match inferred type
::Symbol, ::Alpha, ::Beta for #probe_union
and reports no problems here.
Add two specs pinning that path: `@return [self]` on one arm of a
two-class union, and `@return [Array<self>]` shared by both arms, where
`self` inside a generic distributes to `Array<Alpha>, Array<Beta>`.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CXmnT5gSB1PheL9UbiGEVA
`Diagnostics::TypeCheck#diagnose` computes `level` as `String, Symbol`
(`args.reverse.find { ... } || :normal`) and passes `level.to_sym` to
`TypeChecker.new`. `String#to_sym` is `-> ::Symbol` and `Symbol#to_sym`
is `-> self`, so while `self` resolved against the whole union the call
inferred `Symbol, String` and the result needed suppressing.
Resolving each arm's pin against that arm alone narrows it to `Symbol`.
The comment now suppresses nothing, and `--level strong` reports it as
an unneeded `@sg-ignore`. Removing it brings this branch to 529
problems in 90 of 250 files, matching master at 8fda633.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CXmnT5gSB1PheL9UbiGEVA
self return type per union arm
Contributor
Author
|
Yes ��� YARD reaches this too, and the fix covers it without a second change.
class Alpha
# @return [Symbol]
def to_thing; end
end
class Beta
# @return [self]
def to_thing; end
end
# @param x [Alpha, Beta]
# @return [Symbol, Beta]
def probe_union(x)
x.to_thing
end
Same file at Two further shapes, both pure YARD, inferred type before and after:
The first is the dedup guard holding ��� a method shared by every arm is not over-split. The second is the generic case distributing. This comment was written by Claude Code on behalf of @apiology. |
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 18, 2026
One conflict in diagnostics/type_check.rb: the PR predates the workspace keyword this branch passes to TypeChecker.new, and its side would have dropped it. Kept this branch call.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A method that declares a
selfreturn type, called on a union receiver, resolvesselfagainst the whole union instead of the arm that supplied the method.Chain::Call#resolvesplit the binder into arms only to collect method pins, then flattened them and called#inferred_pinsonce withname_pinstill bound to the whole union. Each arm's pin is now resolved against that arm alone.This covers
selfin any position ���@return [Array<self>]on anAlpha, Betareceiver now infers::Array<::Alpha>, ::Array<::Beta>. Results dedupe on path and resolved return type, so aself-returning method shared by every arm still yields each arm:(String | Symbol)#itselfstaysString, Symbol.It also removes an
@sg-ignorehere:Diagnostics::TypeCheck#diagnosecallslevel.to_symwherelevelisString, Symbol, which needed suppressing and now resolves toSymbol.This PR was written by Claude Code on behalf of @apiology.