Keep a block's reassignment of an outer local visible after the block - #1319
Draft
apiology wants to merge 1 commit into
Draft
Keep a block's reassignment of an outer local visible after the block#1319apiology wants to merge 1 commit into
apiology wants to merge 1 commit into
Conversation
An assignment inside a block wrote a LocalVariable pin whose closure was
the Pin::Block and whose presence stopped at the block's closing token, so
a read after the block saw only the pre-block pin. `x = nil; [1].each { x
= ['a'] }; x` inferred `nil`, and any call on x after the block was
unresolved.
The pin now belongs to whichever enclosing closure already has a variable
of that name in scope at that point, with presence running to that
closure's end - the same shape an assignment inside an `if` branch already
produces. The read after the block infers `nil, Array`: a block may run
zero times, so the pre-block value stays in the union.
A name the block introduces itself - a block parameter, a shadow arg, or a
first assignment inside the block - keeps the block closure and stays
invisible outside.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXmnT5gSB1PheL9UbiGEVA
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 21, 2026
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 21, 2026
process_macro/process_directive's return values were assigned to result, shadowing the outer pins.map block's own result= target. castwide#1319's flow-sensitive reassignment tracking picked up the inner block-local as though it were the outer one, making the outer result unresolvable at its own use site (line 432) and its downstream nil-guards report as unneeded.
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 local assigned inside a block is invisible once the block ends, so code that
is correct at runtime reports an unresolved call:
The same assignment inside an
ifalready infers the unionnil, ::Array.Only the block form loses it.
Cause.
LvasgnNode#processset the pin's presence toregion.closure.location.range.ending. Inside a block that closure is thePin::Block, so presence stops at the block's closing token:Fix.
LvasgnNodewalks outward for an enclosing closure that already hasthat name in scope at that position, and attaches the pin there. A name the
block introduces itself ��� block parameter, shadow arg, or first assignment in
the block ��� keeps the block closure and still shadows correctly.
This deliberately does not silence downstream nil errors. Post-block inference
becomes
nil, ::Array, not::Array, because the block may run zero times;chomped.rejecton that union remains correctly unresolved.Adds 5 examples in
spec/pin/local_variable_spec.rb.Measured against
8fda63384: rubocop 70 offences before and after, outputbyte-identical; self-hosted
typecheck --level strong525 problems in 89 of250 files before and after; rspec unchanged at 1624 examples / 1 pre-existing
failure / 60 pending.
This PR was written by Claude Code on behalf of @apiology.