Skip to content

Commit ea7072c

Browse files
authored
More precise property types after assignment when strict_types=0
1 parent 2ac87fc commit ea7072c

25 files changed

+1040
-21
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5628,24 +5628,25 @@ static function (): void {
56285628
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
56295629
if ($propertyReflection->canChangeTypeAfterAssignment()) {
56305630
if ($propertyReflection->hasNativeType()) {
5631-
$assignedNativeType = $scope->getNativeType($assignedExpr);
56325631
$propertyNativeType = $propertyReflection->getNativeType();
56335632

5634-
$assignedTypeIsCompatible = false;
5635-
foreach (TypeUtils::flattenTypes($propertyNativeType) as $type) {
5636-
if ($type->isSuperTypeOf($assignedNativeType)->yes()) {
5637-
$assignedTypeIsCompatible = true;
5638-
break;
5633+
$assignedTypeIsCompatible = $propertyNativeType->isSuperTypeOf($assignedExprType)->yes();
5634+
if (!$assignedTypeIsCompatible) {
5635+
foreach (TypeUtils::flattenTypes($propertyNativeType) as $type) {
5636+
if ($type->isSuperTypeOf($assignedExprType)->yes()) {
5637+
$assignedTypeIsCompatible = true;
5638+
break;
5639+
}
56395640
}
56405641
}
56415642

56425643
if ($assignedTypeIsCompatible) {
5643-
$scope = $scope->assignExpression($var, $assignedExprType, $assignedNativeType);
5644-
} elseif ($scope->isDeclareStrictTypes()) {
5644+
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5645+
} else {
56455646
$scope = $scope->assignExpression(
56465647
$var,
5647-
TypeCombinator::intersect($assignedExprType->toCoercedArgumentType(true), $propertyNativeType),
5648-
TypeCombinator::intersect($assignedNativeType->toCoercedArgumentType(true), $propertyNativeType),
5648+
TypeCombinator::intersect($assignedExprType->toCoercedArgumentType($scope->isDeclareStrictTypes()), $propertyNativeType),
5649+
TypeCombinator::intersect($scope->getNativeType($assignedExpr)->toCoercedArgumentType($scope->isDeclareStrictTypes()), $propertyNativeType),
56495650
);
56505651
}
56515652
} else {
@@ -5716,24 +5717,25 @@ static function (): void {
57165717
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
57175718
if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) {
57185719
if ($propertyReflection->hasNativeType()) {
5719-
$assignedNativeType = $scope->getNativeType($assignedExpr);
57205720
$propertyNativeType = $propertyReflection->getNativeType();
5721+
$assignedTypeIsCompatible = $propertyNativeType->isSuperTypeOf($assignedExprType)->yes();
57215722

5722-
$assignedTypeIsCompatible = false;
5723-
foreach (TypeUtils::flattenTypes($propertyNativeType) as $type) {
5724-
if ($type->isSuperTypeOf($assignedNativeType)->yes()) {
5725-
$assignedTypeIsCompatible = true;
5726-
break;
5723+
if (!$assignedTypeIsCompatible) {
5724+
foreach (TypeUtils::flattenTypes($propertyNativeType) as $type) {
5725+
if ($type->isSuperTypeOf($assignedExprType)->yes()) {
5726+
$assignedTypeIsCompatible = true;
5727+
break;
5728+
}
57275729
}
57285730
}
57295731

57305732
if ($assignedTypeIsCompatible) {
5731-
$scope = $scope->assignExpression($var, $assignedExprType, $assignedNativeType);
5732-
} elseif ($scope->isDeclareStrictTypes()) {
5733+
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5734+
} else {
57335735
$scope = $scope->assignExpression(
57345736
$var,
5735-
TypeCombinator::intersect($assignedExprType->toCoercedArgumentType(true), $propertyNativeType),
5736-
TypeCombinator::intersect($assignedNativeType->toCoercedArgumentType(true), $propertyNativeType),
5737+
TypeCombinator::intersect($assignedExprType->toCoercedArgumentType($scope->isDeclareStrictTypes()), $propertyNativeType),
5738+
TypeCombinator::intersect($scope->getNativeType($assignedExpr)->toCoercedArgumentType($scope->isDeclareStrictTypes()), $propertyNativeType),
57375739
);
57385740
}
57395741
} else {

src/Type/Accessory/AccessoryLiteralStringType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
3030
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3131
use PHPStan\Type\Type;
32+
use PHPStan\Type\TypeCombinator;
3233
use PHPStan\Type\UnionType;
3334
use PHPStan\Type\VerbosityLevel;
3435

@@ -215,6 +216,10 @@ public function toArrayKey(): Type
215216

216217
public function toCoercedArgumentType(bool $strictTypes): Type
217218
{
219+
if (!$strictTypes) {
220+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
221+
}
222+
218223
return $this;
219224
}
220225

src/Type/Accessory/AccessoryLowercaseStringType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
3030
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3131
use PHPStan\Type\Type;
32+
use PHPStan\Type\TypeCombinator;
3233
use PHPStan\Type\UnionType;
3334
use PHPStan\Type\VerbosityLevel;
3435

@@ -212,6 +213,10 @@ public function toArrayKey(): Type
212213

213214
public function toCoercedArgumentType(bool $strictTypes): Type
214215
{
216+
if (!$strictTypes) {
217+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
218+
}
219+
215220
return $this;
216221
}
217222

src/Type/Accessory/AccessoryNonEmptyStringType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ public function toArrayKey(): Type
213213

214214
public function toCoercedArgumentType(bool $strictTypes): Type
215215
{
216+
if (!$strictTypes) {
217+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
218+
}
219+
216220
return $this;
217221
}
218222

src/Type/Accessory/AccessoryNonFalsyStringType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ public function toArrayKey(): Type
215215

216216
public function toCoercedArgumentType(bool $strictTypes): Type
217217
{
218+
if (!$strictTypes) {
219+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
220+
}
221+
218222
return $this;
219223
}
220224

src/Type/Accessory/AccessoryNumericStringType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ public function toArrayKey(): Type
215215

216216
public function toCoercedArgumentType(bool $strictTypes): Type
217217
{
218+
if (!$strictTypes) {
219+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
220+
}
221+
218222
return $this;
219223
}
220224

src/Type/Accessory/AccessoryUppercaseStringType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
3030
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3131
use PHPStan\Type\Type;
32+
use PHPStan\Type\TypeCombinator;
3233
use PHPStan\Type\UnionType;
3334
use PHPStan\Type\VerbosityLevel;
3435

@@ -212,6 +213,10 @@ public function toArrayKey(): Type
212213

213214
public function toCoercedArgumentType(bool $strictTypes): Type
214215
{
216+
if (!$strictTypes) {
217+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
218+
}
219+
215220
return $this;
216221
}
217222

src/Type/BooleanType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public function toArrayKey(): Type
113113

114114
public function toCoercedArgumentType(bool $strictTypes): Type
115115
{
116+
if (!$strictTypes) {
117+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this->toString(), $this);
118+
}
119+
116120
return $this;
117121
}
118122

src/Type/CallableType.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use PHPStan\Reflection\Php\DummyParameter;
2525
use PHPStan\ShouldNotHappenException;
2626
use PHPStan\TrinaryLogic;
27+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
2728
use PHPStan\Type\Generic\TemplateType;
2829
use PHPStan\Type\Generic\TemplateTypeHelper;
2930
use PHPStan\Type\Generic\TemplateTypeMap;
@@ -331,7 +332,12 @@ public function toArrayKey(): Type
331332

332333
public function toCoercedArgumentType(bool $strictTypes): Type
333334
{
334-
return TypeCombinator::union($this, new StringType(), new ArrayType(new MixedType(true), new MixedType(true)), new ObjectType(Closure::class));
335+
return TypeCombinator::union(
336+
$this,
337+
TypeCombinator::intersect(new StringType(), new AccessoryNonEmptyStringType()),
338+
new ArrayType(new MixedType(true), new MixedType(true)),
339+
new ObjectType(Closure::class),
340+
);
335341
}
336342

337343
public function isOffsetAccessLegal(): TrinaryLogic

src/Type/Constant/ConstantBooleanType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\Type\StaticTypeFactory;
1515
use PHPStan\Type\Traits\ConstantScalarTypeTrait;
1616
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
1718
use PHPStan\Type\VerbosityLevel;
1819

1920
/** @api */
@@ -109,6 +110,10 @@ public function toArrayKey(): Type
109110

110111
public function toCoercedArgumentType(bool $strictTypes): Type
111112
{
113+
if (!$strictTypes) {
114+
return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this->toString(), $this);
115+
}
116+
112117
return $this;
113118
}
114119

0 commit comments

Comments
 (0)