Destructure block parameter groups and keep union-typed tuple elements in one position - #60
Open
apiology wants to merge 11 commits into
Open
Destructure block parameter groups and keep union-typed tuple elements in one position#60apiology wants to merge 11 commits into
apiology wants to merge 11 commits into
Conversation
A destructured group (|(a, b), c|) previously produced one garbage Parameter named with the raw sexp text and no local pins at all for the variables inside it, so every reference to them was an unresolved call. The group now registers as a single :mlhs Parameter (holding its position in the block signature), and each variable inside it becomes a local Parameter carrying an mlhs_path - the group's position plus the element index at each nesting level - typed by projecting the group's tuple type per position. typify_parameters also keeps its best partial result instead of discarding everything when a single yield type (e.g. each_with_object's unbound U generic) fails to resolve. SKIP=Solargraph: strong self-typecheck of the touched files carries 16 problems on the parent commit already (fallout of re-enabling tuple inference, which castwide#1223 mitigates); this diff nets that down to 15. PoC branch - the real PR should land atop castwide#1223.
…tion Substituting a generic type parameter with a union (e.g. Hash#each yielding [K, V] where K is 'String, Symbol' or 'String, nil') spliced the union's members into separate tuple positions, inflating Array(K, V) into a 3-arity tuple. That broke block destructuring (arity mismatch) and produced false 'Wrong argument type ... received Array(A, B, C)' errors at strong level. Both rebuild sites - resolve_param_generics_from_context and UniqueType#transform - now rebuild parameters per position, wrapping however many types a position's transformation produces back into that single position. Known limitation: the tag rendering is unchanged, so a multi-item position still PRINTS ambiguously (Array(String, Symbol, Integer)); positions survive in memory but not a to_s/parse round trip.
apiology
added a commit
that referenced
this pull request
Aug 17, 2026
# Conflicts: # lib/solargraph/pin/parameter.rb
The partial-result fallback added in this branch returned whatever
destructure_yield_types produced even when the yielded types could not
be matched to the block's parameters. In that fallback Ruby auto-splats
a single yielded value across several parameters, but the code assigns
the WHOLE value to position 0 and leaves the rest undefined - garbage
the previous all-or-nothing gate happened to discard.
Three real downstream sites regressed from "parameters untyped" to
"parameter 0 wrongly typed as the whole value":
hash[k] = v # Wrong argument type: expected String,
# received Array<Object, NilClass>
proc { |exception, try, elapsed, next_int| ... }
# exception expected Exception, received Array
Only keep a partial result when the positions genuinely correspond -
one yielded type per parameter, or a tuple whose arity matches the
parameter count. Otherwise fall back to undefined, as before.
Four specs pin the plain multi-parameter shapes (Hash#each pair,
Array-of-tuples, 4-parameter proc, non-matching arity).
apiology
added a commit
that referenced
this pull request
Aug 17, 2026
The #60 merge (86ccceb) brought in the partial-result fallback, which regressed three plate-spinner sites from 'parameters untyped' to 'parameter 0 typed as the whole yielded value' (jell.rb:35, my_asana_wip.rb:292, asana_retry.rb:57). Verified against this pin: the fix removes exactly those three errors and introduces none.
yield_types.first is ComplexType, nil; calling tuple?/all_params on it
directly failed strong self-typecheck (block.rb:84-85) once merged into
the integration branch, whose narrowing surfaces it. CI on the
integration merge reported exactly these two problems.
SKIP=Solargraph: the Overcommit hook wrapper errors out ('Solargraph
failed to run') in this worktree; the CI 'Solargraph / strong' job is
the authoritative gate.
apiology
added a commit
that referenced
this pull request
Aug 17, 2026
Fixes the two strong self-typecheck problems the previous merge introduced at pin/block.rb:84-85 (yield_types.first is nilable).
add_mlhs_locals recurses on a nested mlhs child (e.g. |((a, b), c)|); only the single-level group was tested.
Only the all-resolved and none-resolved shapes were tested. When positions correspond one-to-one but one position's type is an unbound generic that never binds, typify_parameters must still keep the sibling position that did resolve, rather than discarding the whole partial result. Split from the 2026-08-04 integration branch's bundled undercover coverage commit 4aab8b2.
lekemula/solargraph-rspec's convention_spec.rb still expects plain
Hash/Array/NilClass, so it fails against this PR's more precise
Hash{Symbol => String}/Array<Integer>/nil literal-inference output.
apiology/solargraph-rspec:fix-hash-literal-type-precision tolerates
both, pending lekemula/solargraph-rspec#36.
apiology
added a commit
that referenced
this pull request
Sep 3, 2026
These examples were marked pending on #53 by #60. The feature that PR was waiting on already landed via castwide#1231, castwide#1258, and castwide#1312, so the pending wrapper now hides passing coverage instead of documenting a known gap. RSpec confirmed each example passes cleanly with the wrapper removed.
apiology
added a commit
that referenced
this pull request
Sep 5, 2026
Brings #60 up to its current head. The lib-side work is already here, so what arrives is spec/type_checker/levels/destructuring_spec.rb - the coverage for the two undercover nodes that PR owns, in args_node.rb and pin/block.rb. One conflict, .github/workflows/plugins.yml, resolved to this branch. That side removes four continue-on-error suppressions and enables the bundler cache; the goal here is to surface problems, not to keep the check green. Both sides clone the same solargraph-rspec branch, so that part of the conflict was cosmetic. 2186 examples, 0 failures, 45 pending.
Resolves the branch conflict blocking the PR. The only conflict was in .github/workflows/plugins.yml, where both sides redirect the run_solargraph_rspec_specs job at apiology/solargraph-rspec branch fix-hash-literal-type-precision, pending lekemula/solargraph-rspec#36. Both spellings clone the same branch of the same fork. Took the base branch version, a single "git clone --branch", over this branch version, a "git clone" followed by a separate "git checkout". The resolved file is byte-identical to the base branch version, so this branch contributes no plugins.yml change of its own any more.
apiology
marked this pull request as ready for review
September 6, 2026 23:43
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.
Base: castwide#1223
Problem: A block parameter written as a destructured group gets no local pins at all, so every reference to it is reported as an unresolved call.
Every block that destructures a hash entry is affected, and any hash whose keys are a union additionally reports wrong-argument errors that are not real.
Solution: Register a destructured group as one
:mlhsparameter holding its signature position, type each variable inside it by projecting the group's tuple type per element index, and rebuild generic type parameters per position so a union binding one tuple slot stays one slot.🤖 Generated with Claude Code