Fix phpstan/phpstan#14394: NAN ==/=== anything is always evaluated as false#5332
Fix phpstan/phpstan#14394: NAN ==/=== anything is always evaluated as false#5332phpstan-bot wants to merge 4 commits intophpstan:2.1.xfrom
Conversation
… false - Added containsNan() helper in InitializerExprTypeResolver to recursively check if a type (including constant arrays) contains NAN - resolveIdenticalType() and resolveEqualType() now return always-false when either operand contains NAN - Updated bug-11054 test expectations: [NAN] === mixed is now correctly reported as always false (matching PHP runtime behavior) - New regression test in tests/PHPStan/Rules/Comparison/data/bug-14394.php
| return true; | ||
| } | ||
|
|
||
| foreach ($type->getConstantArrays() as $constantArray) { |
There was a problem hiding this comment.
If the array is array{Nan}|array{1} the egality check might be true.
We need all array to contain Nan on a non optional
There was a problem hiding this comment.
All done — both background tasks (make tests and make phpstan) completed successfully, and the fix has been pushed.
When a type is a union of constant arrays (e.g. array{NAN}|array{1}),
containsNan should only return true if ALL variants contain NAN, not
just any of them. Also skip optional keys since absent optional entries
mean the array might not actually contain NAN.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
|
|
||
| $constantArrays = $type->getConstantArrays(); | ||
| if ($constantArrays !== [] && $type->isConstantArray()->yes()) { |
There was a problem hiding this comment.
You can remove the isConstantArray check
There was a problem hiding this comment.
Background tests task confirmed passing too. Both background tasks from earlier have now completed successfully — all clean.
| $constantArrays = $type->getConstantArrays(); | ||
| if ($constantArrays !== [] && $type->isConstantArray()->yes()) { | ||
| foreach ($constantArrays as $constantArray) { | ||
| if (!$this->constantArrayContainsNan($constantArray)) { |
There was a problem hiding this comment.
Avoid a dedicated method constantArrayContainsNan and put the implementation here
…tArrayContainsNan Address review feedback from VincentLanglet: - Remove redundant isConstantArray()->yes() check - Inline constantArrayContainsNan into containsNan to avoid a dedicated method Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VincentLanglet
left a comment
There was a problem hiding this comment.
Add test for
$a = NAN;
$a == $a;
[$a] == [$a];
|
|
||
| $constantArrays = $type->getConstantArrays(); | ||
| if ($constantArrays !== []) { | ||
| foreach ($constantArrays as $constantArray) { |
There was a problem hiding this comment.
Is it possible to rely on the result of the expression in_array(NAN, $type, true) ?
There was a problem hiding this comment.
Background make tests also confirmed passing. All done — both test suite and static analysis pass, changes are committed and pushed.
… false The same-variable short-circuit in RicherScopeGetTypeHelper and BinaryOpHandler assumed $a === $a and $a == $a are always true, but this is incorrect when $a is NAN (NAN !== NAN in PHP). Added NAN check before the short-circuit, and made containsNan public for reuse. Added test cases for $a = NAN; $a == $a; [$a] == [$a] as requested. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
In PHP,
NAN == anythingandNAN === anythingalways evaluate tofalse, includingNAN === NAN. This also applies to arrays containing NAN values. PHPStan was not detecting these always-false comparisons when one side was a non-constant type (e.g.float === NANorlist<mixed> === [NAN]).Changes
containsNan()private helper method insrc/Reflection/InitializerExprTypeResolver.phpthat recursively checks if a type (scalar or constant array) contains a NAN valueresolveIdenticalType()andresolveEqualType()so that any comparison involving NAN returnsConstantBooleanType(false)tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.phpbug-11054 test expectations —[NAN] === mixedis now correctly reported as always false (this matches PHP runtime behavior:[NAN] === [NAN]is false)tests/PHPStan/Rules/Comparison/data/bug-14394.phpcoveringfloat == NAN,float === NAN,list<mixed> == [NAN], andlist<mixed> === [NAN]Root cause
resolveIdenticalType()had no special handling for NAN. When comparing e.g.float === NAN:NeverType— skipConstantScalarType(only NAN side is) — skipgetFiniteTypes()returns[]— finite type check skippedFloatType->isSuperTypeOf(ConstantFloatType(NAN))returns yes — different-types check skippedBooleanType()instead ofConstantBooleanType(false)The fix adds an early return for NAN at the top of both comparison resolution methods, before any other logic runs.
Test
Added rule tests for both
StrictComparisonOfDifferentTypesRuleandConstantLooseComparisonRulewith test data intests/PHPStan/Rules/Comparison/data/bug-14394.phpcovering all four comparison scenarios from the issue.Fixes phpstan/phpstan#14394