Bug report
This is a follow-up to #14807 ("Object property narrowing leaks out of a previous if when a 2-case enum is narrowed with !=="), which was closed by phpstan/phpstan-src#5848 and released in 2.2.3.
The minimal repro from #14807 is indeed fixed on 2.2.3/2.2.4. However, the fix only covers the narrow reduced shape. The same underlying leak in MutatingScope::createConditionalExpressions() still fires on a slightly richer shape, and 2.2.3 additionally introduced a new symptom on in_array() set-narrowing.
The extra ingredient that defeats the fix: an intermediate if between the two enum ifs that narrows another value with === false inside a disjunction (below: $extra === false). With it present:
- The original property-narrowing false positive (
identical.alwaysFalse on $flags->flagA === false) comes back — it was never fixed for this shape (present in 2.2.2, 2.2.3 and 2.2.4).
- A new false positive appears:
in_array($enum, [...], true) in the second if is reported as function.alreadyNarrowedType ("will always evaluate to true"), because the enum-set narrowing from the first if also leaks. This one is a regression introduced in 2.2.3 (absent in 2.2.2).
Version matrix (same repro below)
| Version |
L31 $flags->flagA === false |
L34 in_array($kind, ..., true) |
| 2.2.2 |
❌ false positive |
✅ no error |
| 2.2.3 |
❌ false positive |
❌ new false positive |
| 2.2.4 |
❌ false positive |
❌ false positive |
Expected output
No errors. The first if can be skipped for reasons unrelated to $flags->flagA or to $kind (e.g. the $grade condition, $cond, or $flags->flagA itself being false), so neither $flags->flagA === false nor the in_array() set-membership check in the later block is statically decidable.
Actual output (2.2.4)
L31 Strict comparison using === between true and false will always evaluate to false. (identical.alwaysFalse)
L34 Call to function in_array() with arguments Kind::K1|Kind::K2|Kind::K3, array{Kind::K1, Kind::K2} and true will always evaluate to true. (function.alreadyNarrowedType)
Reproduces on 2.2.3 and 2.2.4 without bleeding edge. See #14807 for the original (now partially-fixed) report.
Code snippet that reproduces the problem
https://phpstan.org/r/ae549d6e-b042-42b9-9138-c81e8adcbee6
Did PHPStan help you today? Did it make you happy in any way?
No response
Bug report
This is a follow-up to #14807 ("Object property narrowing leaks out of a previous if when a 2-case enum is narrowed with
!=="), which was closed by phpstan/phpstan-src#5848 and released in 2.2.3.The minimal repro from #14807 is indeed fixed on 2.2.3/2.2.4. However, the fix only covers the narrow reduced shape. The same underlying leak in
MutatingScope::createConditionalExpressions()still fires on a slightly richer shape, and 2.2.3 additionally introduced a new symptom onin_array()set-narrowing.The extra ingredient that defeats the fix: an intermediate
ifbetween the two enumifs that narrows another value with=== falseinside a disjunction (below:$extra === false). With it present:identical.alwaysFalseon$flags->flagA === false) comes back — it was never fixed for this shape (present in 2.2.2, 2.2.3 and 2.2.4).in_array($enum, [...], true)in the secondifis reported asfunction.alreadyNarrowedType("will always evaluate to true"), because the enum-set narrowing from the firstifalso leaks. This one is a regression introduced in 2.2.3 (absent in 2.2.2).Version matrix (same repro below)
$flags->flagA === falsein_array($kind, ..., true)Expected output
No errors. The first
ifcan be skipped for reasons unrelated to$flags->flagAor to$kind(e.g. the$gradecondition,$cond, or$flags->flagAitself beingfalse), so neither$flags->flagA === falsenor thein_array()set-membership check in the later block is statically decidable.Actual output (2.2.4)
Reproduces on 2.2.3 and 2.2.4 without bleeding edge. See #14807 for the original (now partially-fixed) report.
Code snippet that reproduces the problem
https://phpstan.org/r/ae549d6e-b042-42b9-9138-c81e8adcbee6
Did PHPStan help you today? Did it make you happy in any way?
No response