Repository navigation
Conversation
resolveScopeStateType() already resolves late-resolvable types for a tracked expression, but returned a variable's type as stored. A parameter typed as a conditional type intersected with an array shape therefore reached specifyExpressionTypeInPlace() unresolved: narrowing one of its offsets intersected HasOffsetValueType with the unresolved conditional and dropped the shape. Mirrored in the turbo extension. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes phpstan/phpstan#15644
Disclosure: I worked this out with an AI assistant (Claude). The diagnosis, the fix and the test runs below were all done and checked for real.
resolveScopeStateType()resolves late-resolvable types for a tracked expression (TypeUtils::resolveLateResolvableTypes($trackedType)), but returned a variable's type as stored. A parameter typed as a conditional type intersected with an array shape, such astherefore reached
specifyExpressionTypeInPlace()unresolved. Narrowing one of its offsets ($row['a'] ?: null) intersectedHasOffsetValueTypewith the unresolved conditional, and the shape was lost: every later read of$row['b']wasmixed. Before 2.3.0, this path read the variable throughgetType(), which resolves it.The variable branch now resolves late-resolvable types too, the same as the tracked-expression branch above it. The same change is mirrored in
turbo-ext/src/MutatingScope.cpp.Testing
tests/PHPStan/Analyser/nsrt/bug-15644.php, the issue's playground snippet withassertType(): fails without the fix (line 18non-empty-array{recordinfo?: array{id: string}, ...<string, mixed>}, line 19mixed) and passes with it.PHP_INI_SCAN_DIR. The 2 deprecations areReflectionProperty::setAccessible()inObjectTypeTestandForeachHandlerTest, and show up both ways.php:8.5-cliwithmake WARN_FLAGS="$(make -s print-warn-flags)":tests/smoke.phpALL OK,bin/side-by-side.phpOK,tests/signature-parity.phpOK (16,504 members).bin/phpstan analyse --error-format=raw src/Analysergives identical output with the extension loaded (diagnose:enabled (version 67acd19)) and without it.MutatingScope.phpare clean.I didn't run
make lint-turbo(clang-tidy) ormake sanitize-turbolocally.🤖 Generated with Claude Code