Prefer a block signature that declares parameters - #61
Draft
apiology wants to merge 2 commits into
Draft
Conversation
When a method has more than one block-carrying signature, Chain::Call
selected the first one in source order. A signature built from a bare
`&block` parameter yields `{ () -> }` - it says the method takes a block
but nothing about what the block receives - so when it sorted ahead of a
sibling signature carrying @yieldparam types, the block's parameters
resolved as undefined.
This shows up wherever a gem's own yardoc and a workspace `@!parse` stub
both declare the same method: ApiMap::Store combines them into one pin
with two signatures, the gem's `&block` signature first, and the
annotation's @yieldparam types were never consulted.
Within the block-carrying group, signatures whose block declares
parameters are now tried before those whose block declares none. This
only changes which signature wins where both already match the call's
arity and argument types, and the previous winner was whichever happened
to be first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AsvDi68YqsKoBtS2kg9ch
Chain::Call tries a method pin's signatures in preference order and stops as soon as one produces a defined return type. When none does - every signature returning untyped, as RBS `untyped` maps to ComplexType::UNDEFINED - the loop ran to the end and the last signature to match supplied the pin passed to `with_single_signature`, discarding the better earlier match. A blockless signature matches a call that passes a block, since Ruby accepts a block for any method. So where a gem's bare `def build; end` combines with a workspace `@!parse` stub declaring @yieldparam types, the blockless signature was tried second and won, and the block's parameters resolved as undefined. A later signature now replaces an earlier match only when it produces a return type. This is complementary to the preference ordering added in the previous commit: that orders the block-carrying group internally, which is a no-op when the group holds a single signature, as it does here. The full spec suite is unchanged (1631 -> 1632 examples with the new spec, 0 failures) and `solargraph typecheck --level strong` over this codebase reports the same 577 problems, so nothing here relied on a later overload overwriting an earlier one under an undefined-return tie. The added lines are kept tight to stay under the existing Metrics/BlockLength limit for this block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AsvDi68YqsKoBtS2kg9ch
apiology
added a commit
that referenced
this pull request
Aug 22, 2026
…-08-04 # Conflicts: # lib/solargraph/source/chain/call.rb
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 block parameter resolves as
undefinedwhen a gem's yardoc and a workspace@!parsestub both declare a method and only the stub declares the yield:ApiMap::Storecombines the pins into one carrying two signatures. The gem'sbare
&blockyields{ () -> }, andChain::Calltook the first, neverreading the stub's
@yieldparam.Signatures whose block declares parameters now sort ahead. That changes the
outcome only where both already matched the call's arity and arguments and the
winner was arbitrary.
Two adjacent shapes remain uncovered: a stub without
@return, whosesignatures
combine_signaturesdiscards as all-undefined, and a gem usingdef self.buildagainst a stub usingclass << self, where the pins nevercombine.
Based on castwide#1288, whose store combining produces the two-signature pin. On
masterthe@!parsepin is discarded by[stack.first].compactbeforeselection runs, so the two-signature pin does not exist and this sort has
nothing to reorder — the symptom is the same there, by a different mechanism.
This PR was written by Claude Code on behalf of @apiology.