Fix phpstan/phpstan#14390: Unconsistant behavior for offsetAccess.notFound#5310
Fix phpstan/phpstan#14390: Unconsistant behavior for offsetAccess.notFound#5310phpstan-bot wants to merge 7 commits intophpstan:2.1.xfrom
Conversation
staabm
left a comment
There was a problem hiding this comment.
the PR reminded me that ondrej once said "using instanceof Variable" most of the time is wrong, because if you use it your logic likely misses other expressions :-).
change looks good
staabm
left a comment
There was a problem hiding this comment.
usually expr-printing can be slow. but since we use it only after some very specific AST based instanceof checks I think this is a solid improvement
…essions - The special-case logic in NonexistentOffsetInArrayDimFetchRule that suppresses false positives for $arr[array_key_first($arr)] only matched Variable AST nodes - Changed expression comparison to use ExprPrinter so it works with property fetches, static property fetches, and any other expression type - Also fixed the same limitation in the array_rand and count()-1 special cases - New regression test in tests/PHPStan/Rules/Arrays/data/bug-14390.php
Addresses review feedback: the ExprPrinter changes for array_rand and count()-1 special cases were not covered by tests. Adds test cases for property access with array_rand() and count()-1 on non-empty arrays. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… checks Covers array_key_first, array_key_last, array_rand, and count()-1 special cases with static properties, as requested in review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1f27903 to
3bb9cdb
Compare
VincentLanglet
left a comment
There was a problem hiding this comment.
Without modifying the code, can you tell me
- If there is other place where the same logic (exprPrinter) is used and/or if another strategy is used
- If there is other place where the Variable comparison should be replaced by an exprPrinter check
|
The GitHub token doesn't have permission to comment on the upstream repo. Since this is a research-only review (no code changes requested), let me summarize the findings here: VincentLanglet's Review - Research Findings1. Where
|
| Location | Pattern | Candidate? |
|---|---|---|
src/Type/Php/ArrayCombineHelper.php:102 |
$firstArg instanceof Variable && $secondArg instanceof Variable && $firstArg->name === $secondArg->name |
Strongest candidate — same class of bug, array_combine($obj->keys, $obj->keys) wouldn't be recognized |
src/Analyser/ExprHandler/AssignHandler.php:1190 |
isSameVariable() helper, Variable-only |
Possible candidate, depends on callers |
src/Analyser/RicherScopeGetTypeHelper.php:32-39 |
$x === $x always true, Variable-only |
Already has separate PropertyFetch handling below, less impactful |
src/Analyser/ExprHandler/BinaryOpHandler.php:104-113 |
$x == $x always true, Variable-only |
Same consideration — identity semantics for properties need care (side effects) |
The reviewer's question is informational — no code changes were requested. The analysis is complete. Since I can't post to the upstream PR, the findings are documented above. Would you like me to try pushing these findings some other way?
|
|
||
| function doWithMethods(WithMethods $withMethods) { | ||
| echo $withMethods->pureMethod()[array_key_first($withMethods->pureMethod())]; | ||
| echo $withMethods->impureMethod()[array_key_first($withMethods->impureMethod())]; |
There was a problem hiding this comment.
This line should expect an error because $withMethods->impureMethod() might not return the same array in the two calls even if exprPrinter->printExpr gives the same result.
So you have to find a better fix.
There was a problem hiding this comment.
The background make phpstan task also completed successfully (exit 0) — confirming no static analysis errors with the fix.
… calls ExprPrinter comparison alone is insufficient because impure method calls like `$obj->impureMethod()` may return different values on each invocation, even though the printed expression is identical. Add isDeterministicExpr() to verify the expression is safe to compare: variables, property fetches, static property fetches, and pure method/static calls are deterministic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VincentLanglet
left a comment
There was a problem hiding this comment.
For MethodCall and StaticCall, this doesn't check the params.
- An example
$foo->pureMethod($foo->impureMethod())[array_key_first($foo->pureMethod($foo->impureMethod()))]
If we start supporting complex expression, we should support dimFetch like
$sample->fields[1][array_key_first($sample->fields[1])];
be aware that if the key is non determinist like
$sample->fields[$i++][array_key_first($sample->fields[$i++])];
or
$sample->fields[$this->getImpure()][array_key_first($sample->fields[$this->getImpure()])];
this should still report an error
…rministicExpr - Check that MethodCall/StaticCall arguments are also deterministic, not just the method purity. E.g. $foo->pureMethod($foo->impureMethod()) is not deterministic because the impure argument may produce different values. - Support ArrayDimFetch expressions (e.g. $nested[$key][array_key_first(...)]), checking both the array and the dim key for determinism. - Add test cases for pure method with impure args, deterministic dim fetch, and non-deterministic dim fetch with impure method key. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed. Here's what was done to address VincentLanglet's review:
|
|
@staabm While I find the fix correct, I also think this fix might be too "complicated". |
Summary
When using
$obj->property[array_key_first($obj->property)]after checking the array is non-empty, PHPStan incorrectly reported "Offset string might not exist on non-empty-array". The same code worked fine with local variables. This fix extends the special-case offset validation to handle any expression type, not just variables.Changes
src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php:ExprPrinterdependencyinstanceof Variable+ name comparison withExprPrinter::printExpr()comparison for thearray_key_first/array_key_last,array_rand, andcount()-1special casestests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.phpto pass the newExprPrinterdependencytests/PHPStan/Rules/Arrays/data/bug-14390.phpregression testRoot cause
The
NonexistentOffsetInArrayDimFetchRulehad special-case logic to suppress false positives for patterns like$arr[array_key_first($arr)]on non-empty arrays. However, it compared expressions by checking that both wereVariablenodes with the same name. This meant property fetches like$sample->fields[array_key_first($sample->fields)]were not recognized as the same expression, causing false positive errors.The fix uses
ExprPrinterto compare the printed representation of expressions, which works for any expression type (variables, property fetches, static property fetches, etc.).Test
Added regression test
tests/PHPStan/Rules/Arrays/data/bug-14390.phpthat covers:array_key_first()on non-empty array (the reported bug)array_key_last()on non-empty arrayarray_key_first()(existing working case)Fixes phpstan/phpstan#14390