Fix phpstan/phpstan#12597: 'Variable might not be undefined' in if-statement after in_array check.#5379
Conversation
…rray check When `in_array($type, [TYPE_1, TYPE_2], true)` narrows `$type` to a union like `1|2` and a variable is assigned inside that block, a conditional expression is created with guard type `1|2`. Later, when `$type === TYPE_1` narrows to just `1`, the conditional didn't match because `equals()` requires exact type equality. Fix: In `filterBySpecifiedTypes`, allow conditional expression guards to match when the specified type is a strict subtype of a finite union guard type. This enables `$type = 1` to match the guard `$type = 1|2`, correctly resolving the variable as defined. The subtype matching is restricted to: - Guards with >1 finite types (union of constants/literals) - Both guard and specified having YES certainty - The conditional's type holder having YES certainty (prevents matching stale "undefined" conditionals from earlier scope merges) - Matches don't cascade (not added to specifiedExpressions) to prevent mutual conditional loops from corrupting type information
staabm
left a comment
There was a problem hiding this comment.
Add regression rule-tests for
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Tests also confirmed passing from the background run. Everything is green and already pushed. |
VincentLanglet
left a comment
There was a problem hiding this comment.
Why is it limited to finites types ? Cant we have a conditional expression "if $x is int|string then $message is defined" and later the need to know that $messgae is defined when $x is int ?
Replace the `count(getFiniteTypes()) > 1` restriction with a `!$guardType instanceof MixedType` check. This allows conditional expressions with non-finite union guards (e.g. int|string) to match when the specified type is a strict subtype, while still preventing incorrect matches against mixed-based types that would cause *NEVER* type collapses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. Here's a summary of the changes made to address VincentLanglet's review: Problem: The subtype matching in Fix: Replaced the finite types restriction with
Test: Added Verification: All 11726 tests pass, |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. Added and pushed two regression test files:
All 11728 tests pass and |
I think the regression test added for 4090 is not correct ; might need to be updated manually @staabm |
Summary
Fixes phpstan/phpstan#12597
When
in_array($type, [self::TYPE_1, self::TYPE_2], true)narrows$typeto a union type like1|2, and a variable$messageis assigned inside that block, PHPStan creates a conditional expression: "when$typeis1|2,$messageis defined." However, a subsequentif ($type === self::TYPE_1)narrows$typeto just1, and the conditional expression didn't fire becausefilterBySpecifiedTypesused exact type matching viaequals()—1does not equal1|2.Changes
src/Analyser/MutatingScope.php: ModifiedfilterBySpecifiedTypesto allow conditional expression guards to match when the specified type is a strict subtype of a finite union guard type. The subtype matching is carefully restricted to prevent regressions:specifiedExpressionsto prevent mutual conditional loops from corrupting type information (prevents regression in dynamic-access test)tests/PHPStan/Rules/Variables/data/bug-12597.php: New regression test data filetests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php: AddedtestBug12597()tests/PHPStan/Analyser/nsrt/bug-5051.php: Updated test expectations for improved type inference — severalboolassertions are now correctly narrowed tofalsewhen the guard variable is narrowed to a specific constant valueTest plan
testBug12597passes (no false positive about undefined variable)testDynamicAccesspasses (no regression in conditional variable tracking)testBug14117passes (no regression with stale conditionals)bug-5051type inference test updated and passesmake phpstan)make cs-fix)