Fix #9146: TypeError dead catch when assigning mixed to int in property#4961
Fix #9146: TypeError dead catch when assigning mixed to int in property#4961phpstan-bot wants to merge 4 commits into2.1.xfrom
Conversation
… property - Changed property assignment TypeError throw point check from accepts() to isSuperTypeOf() using native types - MixedType::isAcceptedBy() always returns yes, causing accepts() to miss that mixed-to-int can throw TypeError - Using isSuperTypeOf() on native types correctly identifies mixed as not guaranteed to be compatible - Widened property type check to include int when property type contains float, preserving PHP's int-to-float coercion - Added regression test with final classes to verify behavior independent of property hooks Closes phpstan/phpstan#9146
Automated fix attempt 1 for CI failures.
Automated fix attempt 2 for CI failures.
|
@phpstan-bot implement |
|
Claude finished @staabm's task in 14s —— View job Implemented
|
|
@staabm How different would these two methods be? |
I did not yet think thru all the types, but at least |
|
But arguments are exactly the same |
|
at least the impl does not reflect that: phpstan-src/src/Type/FloatType.php Line 151 in 8e131a9 |
Move int-to-float property coercion from NodeScopeResolver into the Type system as toCoercedPropertyType(), following the same polymorphic pattern as toCoercedArgumentType(). FloatType returns float|int; all other types return $this. Composite types (UnionType, IntersectionType) delegate to their members. Co-authored-by: Markus Staab <staabm@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
In my opinion the logic would be in IntegerType, not FloatType. |
|
superseded by #4981 |
Summary
When assigning a
mixedtyped value to a natively typed property (e.g.,int,string,float), PHPStan incorrectly reported thecatch (TypeError)as dead code. At runtime, PHP throwsTypeErrorwhen a value incompatible with the property's native type is assigned, so the catch block is valid.Changes
src/Analyser/NodeScopeResolver.php(line ~6369):accepts($assignedExprType, true)toisSuperTypeOf($assignedNativeType)using the native type of the assigned expressiontests/PHPStan/Rules/Exceptions/data/bug-9146.phpwithfinalclasses to isolate the fix from property hook implicit throw pointstestBug9146intests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.phpRoot cause
The previous check used
$nativeProperty->getNativeType()->accepts($assignedExprType, true). Theaccepts()method delegates toMixedType::isAcceptedBy()when the assigned type ismixed, which unconditionally returnsAcceptsResult::createYes()(because mixed is a supertype of the accepting type). This causedint->accepts(mixed, true)to returnyes, preventing the TypeError throw point from being created.The fix uses
isSuperTypeOf()on the native type of the assigned expression instead, which correctly returnsmaybeforint->isSuperTypeOf(mixed), causing the throw point to be created. To preserve PHP's int-to-float coercion behavior (assigningintto afloatproperty does not throw), the property type is widened to includeintwhen it containsfloat.Test
The regression test (
bug-9146.php) covers three cases:mixedto anintproperty (constructor)mixedto astringproperty (method)mixedto afloatproperty (method)All classes are
finalto ensure the test is not influenced by PHP 8.4+ property hook implicit throw points (which would mask the bug on non-final classes).Fixes phpstan/phpstan#9146