Fix phpstan/phpstan#11129: concat of numeric constant string and int should lead to numeric-string#5413
Closed
phpstan-bot wants to merge 2 commits intophpstan:2.1.xfrom
Closed
Conversation
…cading to ErrorType - When a union type like `int|non-falsy-string|float` is used in arithmetic, `toNumber()` returns ErrorType because ErrorType (from the string member) extends MixedType and absorbs all other types in the union - Added `filterNumberTypeFromUnion()` in `resolveCommonMath()` that extracts valid numeric types from union members, skipping ErrorType only when it comes from string members (since string-to-number conversion always produces a result in PHP) - This prevents loop variable types from degrading to `mixed` when a variable alternates between int and string (e.g., `$pos = '0' . $pos` in a foreach loop) - New regression test in tests/PHPStan/Analyser/nsrt/bug-11129b.php - Removed 3 baseline entries and 1 inline ignore that are no longer needed
Contributor
VincentLanglet
left a comment
There was a problem hiding this comment.
The whole strategy is wrong and should be reverted.
The issue is related to the for loop cause the same code written this way
$pos = 0;
$pos = '0' . $pos;
$pos = '0' . $pos;
++$pos;
produce no errors.
The first '0' . $positiveInt is understood as numeric-string
But then '0' . $numericString is not a numeric-string anymore. '0' . '-0' is not for instance.
While phpstan has positive-int type ; there is no positive-numeric-string.
Not sure if there is an easy fix
…from cascading to ErrorType" This reverts commit 5f3ff04.
Collaborator
Author
|
All good — both background tasks confirmed passing as well. Everything is clean and pushed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a loop variable alternates between int and string types (e.g.,
$pos = '0' . $pos), PHPStan would degrade the variable's type tomixedafter a few loop iterations. This happened becauseUnionType::toNumber()returnedErrorTypewhen any union member couldn't be converted to a number, sinceErrorTypeextendsMixedTypeand absorbs all valid results inTypeCombinator::union(). TheErrorTypethen cascaded through arithmetic operations, causing the entire loop's type inference to collapse.Changes
filterNumberTypeFromUnion()helper method insrc/Reflection/InitializerExprTypeResolver.phpthat extracts valid numeric types from union members whentoNumber()returnsErrorTypeErrorTyperesults from string members (since string-to-number conversion always produces a result in PHP), preserving error detection for genuinely invalid types like arrays and objectsresolveCommonMath()before theErrorTypecheck, so arithmetic on union types with string members no longer cascades toErrorType@phpstan-ignore binaryOp.invalidinline comment on line 1291 (no longer needed)+,-,*operations onbool|float|int|string|nullthat are no longer reportedRoot cause
ErrorTypeextendsMixedType, soTypeCombinator::union(int, ErrorType, float)collapses toErrorType(MixedType absorbs everything). WhenUnionType::toNumber()mapstoNumber()over each member and one returnsErrorType, the entire union'stoNumber()becomesErrorType. InresolveCommonMath(), this triggersreturn new ErrorType(), which then propagates through the rest of the loop iteration, eventually degrading the variable tomixed.The fix extracts valid numeric parts from union members in the arithmetic path, only skipping
ErrorTypefrom string members. This is safe because PHP's string-to-number conversion always produces a result (int or float), even for non-numeric strings.Test
Added
tests/PHPStan/Analyser/nsrt/bug-11129b.phpwith two test methods:sayHello(): original issue reproduction using++$poswith conditional'0' . $posconcatenation in a foreach loopwithPlusOne(): same pattern using$pos + 1instead of++$posBoth verify that $pos has type
0|float|(non-falsy-string&uppercase-string)after the loop instead ofmixed.Fixes phpstan/phpstan#11129