Fix imprecise types after assignment when strict-types=1#3945
Fix imprecise types after assignment when strict-types=1#3945ondrejmirtes merged 16 commits intophpstan:2.1.xfrom
Conversation
| self::$i = getInt(); | ||
| assertType('float|int', self::$i); // could be int | ||
| assertNativeType('float|int', self::$i); // could be int | ||
|
|
||
| $this->impureCall(); | ||
| assertType('float|int', self::$i); | ||
| assertNativeType('float|int', self::$i); | ||
| } |
There was a problem hiding this comment.
the impureCall does not make the scope forget about already narrowed types of static property fetches, therefore we don't narrow in this scenario with this PR.
-> we only improve the instance-call case, because non-static fetches will be forgotten as the test in NarrowsNativeUnion proves
|
This pull request has been marked as ready for review. |
|
I've analyzed this situation. It's much more clear what's going on in this code sample: https://phpstan.org/r/903891e3-323a-494b-83a0-27aff806eb88 It's happening because IntegerType::toCoercedArgumentType() returns But what should happen first is even before we call toCoercedArgumentType, we should check whether the original value (ConstantIntegerType 1) is a subtype of the native property type.
Because 1) matches, we don't need to call toCoercedArgumentType at all. We simply assign the value as it is. To get a list of types to compare, we can call TypeUtils::flattenTypes on the native property type. That way we can compare it in a foreach even when there's just a single type like What's nice about this is that it can solve the problem for strict_types = 0 as well. The workflow should be:
If done correctly, this will remove the need for #3943 as well. |
src/Analyser/NodeScopeResolver.php
Outdated
| TypeCombinator::intersect($assignedNativeType->toCoercedArgumentType(true), $propertyNativeType), | ||
| ); | ||
| } else { | ||
| $scope = $scope->assignExpression($var, $assignedExprType, $assignedNativeType); |
There was a problem hiding this comment.
This is gonna get executed if assigned type is compatible or if we're not in declare strict types. That's not what we want. We only want to execute this if assigned type is compatible.
There was a problem hiding this comment.
so what to execute if not compatible and not strict types?
There was a problem hiding this comment.
I think it works now as you imagined.
There was a problem hiding this comment.
If not compatible and not strict types, we currently can't do anything. Once we properly implement toCoercedArgumentType in the whole typesystem, then we can use it same way we use it now with strict_types=1.
| $this->impureCall(); | ||
| assertType('int', self::$i); // should be float|int | ||
| assertNativeType('int', self::$i); // should be float|int |
There was a problem hiding this comment.
this is a unrelated bug, right?
| @fclose($f); | ||
|
|
||
| if (preg_match('~<?php\\s*\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m) === 1) { | ||
| if (preg_match('~<?php\\s*(?:declare\\s*\([^)]+\)\\s*;\\s*)?\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m) === 1) { |
There was a problem hiding this comment.
This isn't compatible with how php-parallel-lint works.
| @@ -0,0 +1,88 @@ | |||
| <?php declare(strict_types = 0); // lint >= 8.1 | |||
There was a problem hiding this comment.
The first line has to be <?php // lint >= 8.1 and the declare statement has to be on line 3.
| $this->impureCall(); | ||
| assertType('int', self::$i); // should be float|int | ||
| assertNativeType('int', self::$i); // should be float|int |
|
Thank you! |
|
I am in the process of updating PHPStan to 2.1.13 and stumble now again over the problem, I initially tried to fix with my first iteration of this PR in #3943 see https://phpstan.org/r/aa5d5753-eca2-4735-bebf-6479e1ab1f42 do you think we could remove nullability from the property-type in scope, as the method we call returns a native non-nullable type, even if strict-types=0? |
|
proposed a new PR so we can discuss whether its possible and my idea is clear: #3965 |
work with coecered type only when the assigned one is incompatible with the natively defined property type
closes phpstan/phpstan#12902
supercedes #3943