Fix #12574: Erroneous Offset might not exist#4965
Conversation
…operty access - Extended processSureTypesForConditionalExpressionsAfterAssign and processSureNotTypesForConditionalExpressionsAfterAssign to store conditional expressions for PropertyFetch, ArrayDimFetch, and StaticPropertyFetch in addition to Variable expressions - Previously, when `$notEmpty = isset($a->worlds[0])` was used in a condition like `if ($notEmpty && $a->worlds[0]->x === 1)`, the type narrowing for `$a->worlds` (HasOffsetType) was discarded because only Variable expressions were stored as conditional expressions - New regression test in tests/PHPStan/Rules/Arrays/data/bug-12574c.php Closes phpstan/phpstan#12574
Closes phpstan/phpstan#11870 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#11102 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#10040 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#7716 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#6991 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#6120 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@phpstan-bot rebase this branch and fix tests/PHPStan/Analyser/nsrt/bug-10040.php by putting |
|
Claude finished @staabm's task in 1m 8s —— View job Task completed
The file fix has been pushed. The I was unable to rebase the branch because |
Co-authored-by: Markus Staab <staabm@users.noreply.github.com>
|
@phpstan-bot revert the last commit @VincentLanglet I will fix the tests after merge (after your review/approval) |
|
Claude finished @staabm's task in 49s —— View job Task completed
Reverted the last commit ( |
This reverts commit a538aa4. Co-authored-by: Markus Staab <staabm@users.noreply.github.com>
| continue; | ||
| } | ||
| } elseif ( | ||
| !$expr instanceof PropertyFetch |
There was a problem hiding this comment.
I feel like we're missing a lot:
-
Since PropertyFetch works, I think NullsafePropertyFetch should be added too.
-
CallLike, see:
https://phpstan.org/r/838517f6-ed32-4fab-993a-d54d85ea6986
In the end, I'm not sure which are the skipped $expr
|
cherry picked into #4988 |
Summary
When the result of
isset()on a property-accessed array element (e.g.,$notEmpty = isset($a->worlds[0])) was stored in a variable and later used in a condition, PHPStan failed to narrow the type of the property access. This caused a false positive "Offset might not exist" error. The same code worked correctly when the array was first assigned to a local variable.Changes
processSureTypesForConditionalExpressionsAfterAssigninsrc/Analyser/NodeScopeResolver.phpto also store conditional expressions forPropertyFetch,ArrayDimFetch, andStaticPropertyFetchexpressions (previously onlyVariableexpressions were stored)processSureNotTypesForConditionalExpressionsAfterAssigntests/PHPStan/Rules/Arrays/data/bug-12574c.phpand test methodtestBug12574cintests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.phpRoot cause
When
$notEmpty = isset($a->worlds[0])is processed, theTypeSpecifiercorrectly determines that if the result is truthy,$a->worldshasHasOffsetType(0). This information is stored as a "sure type" with the expression being aPropertyFetchnode. However,processSureTypesForConditionalExpressionsAfterAssignhad an earlycontinuefor any expression that was not aVariable, discarding the narrowing information for property fetches, array dim fetches, and static property fetches. Whenif ($notEmpty)was later evaluated, there was no conditional expression to apply theHasOffsetType(0)narrowing to$a->worlds, resulting in the false positive.Test
The regression test reproduces the exact scenario from the issue:
isset()on$a->worlds[0]stored in a variable, then used in a boolean condition. Both the property-access version (previously failing) and the local-variable version (previously passing) are tested to confirm no errors are reported.Fixes phpstan/phpstan#12574
Closes phpstan/phpstan#10040
Closes phpstan/phpstan#11102
Closes phpstan/phpstan#11870
Closes phpstan/phpstan#6991
Closes phpstan/phpstan#7716