Skip to content

Commit eb0e0bc

Browse files
committed
Overwrite property expression type only if it's subtype of the native type
1 parent 33dc757 commit eb0e0bc

18 files changed

+393
-21
lines changed

src/Analyser/MutatingScope.php

+9-6
Original file line numberDiff line numberDiff line change
@@ -2119,11 +2119,13 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
21192119
if ($propertyReflection === null) {
21202120
return new ErrorType();
21212121
}
2122-
$nativeType = $propertyReflection->getNativeType();
2123-
if ($nativeType === null) {
2124-
return new ErrorType();
2122+
2123+
if (!$propertyReflection->hasNativeType()) {
2124+
return new MixedType();
21252125
}
21262126

2127+
$nativeType = $propertyReflection->getNativeType();
2128+
21272129
return $this->getNullsafeShortCircuitingType($node->var, $nativeType);
21282130
}
21292131

@@ -2167,11 +2169,12 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
21672169
if ($propertyReflection === null) {
21682170
return new ErrorType();
21692171
}
2170-
$nativeType = $propertyReflection->getNativeType();
2171-
if ($nativeType === null) {
2172-
return new ErrorType();
2172+
if (!$propertyReflection->hasNativeType()) {
2173+
return new MixedType();
21732174
}
21742175

2176+
$nativeType = $propertyReflection->getNativeType();
2177+
21752178
if ($node->class instanceof Expr) {
21762179
return $this->getNullsafeShortCircuitingType($node->class, $nativeType);
21772180
}

src/Analyser/NodeScopeResolver.php

+24-2
Original file line numberDiff line numberDiff line change
@@ -5556,7 +5556,18 @@ static function (): void {
55565556
$assignedExprType = $scope->getType($assignedExpr);
55575557
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
55585558
if ($propertyReflection->canChangeTypeAfterAssignment()) {
5559-
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5559+
if ($propertyReflection->hasNativeType()) {
5560+
$propertyNativeType = $propertyReflection->getNativeType();
5561+
if ($propertyNativeType->isSuperTypeOf($assignedExprType)->yes()) {
5562+
$assignedExprNativeType = $scope->getNativeType($assignedExpr);
5563+
if (!$propertyNativeType->isSuperTypeOf($assignedExprNativeType)->yes()) {
5564+
$assignedExprNativeType = $propertyNativeType;
5565+
}
5566+
$scope = $scope->assignExpression($var, $assignedExprType, $assignedExprNativeType);
5567+
}
5568+
} else {
5569+
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5570+
}
55605571
}
55615572
$declaringClass = $propertyReflection->getDeclaringClass();
55625573
if ($declaringClass->hasNativeProperty($propertyName)) {
@@ -5621,7 +5632,18 @@ static function (): void {
56215632
$assignedExprType = $scope->getType($assignedExpr);
56225633
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
56235634
if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) {
5624-
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5635+
if ($propertyReflection->hasNativeType()) {
5636+
$propertyNativeType = $propertyReflection->getNativeType();
5637+
if ($propertyNativeType->isSuperTypeOf($assignedExprType)->yes()) {
5638+
$assignedExprNativeType = $scope->getNativeType($assignedExpr);
5639+
if (!$propertyNativeType->isSuperTypeOf($assignedExprNativeType)->yes()) {
5640+
$assignedExprNativeType = $propertyNativeType;
5641+
}
5642+
$scope = $scope->assignExpression($var, $assignedExprType, $assignedExprNativeType);
5643+
}
5644+
} else {
5645+
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
5646+
}
56255647
}
56265648
} else {
56275649
// fallback

src/Reflection/Annotations/AnnotationPropertyReflection.php

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Reflection\ExtendedPropertyReflection;
88
use PHPStan\ShouldNotHappenException;
99
use PHPStan\TrinaryLogic;
10+
use PHPStan\Type\MixedType;
1011
use PHPStan\Type\Type;
1112

1213
final class AnnotationPropertyReflection implements ExtendedPropertyReflection
@@ -42,6 +43,26 @@ public function isPublic(): bool
4243
return true;
4344
}
4445

46+
public function hasPhpDocType(): bool
47+
{
48+
return true;
49+
}
50+
51+
public function getPhpDocType(): Type
52+
{
53+
return $this->readableType;
54+
}
55+
56+
public function hasNativeType(): bool
57+
{
58+
return false;
59+
}
60+
61+
public function getNativeType(): Type
62+
{
63+
return new MixedType();
64+
}
65+
4566
public function getReadableType(): Type
4667
{
4768
return $this->readableType;

src/Reflection/Dummy/ChangedTypePropertyReflection.php

+21-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
final class ChangedTypePropertyReflection implements WrapperPropertyReflection
1313
{
1414

15-
public function __construct(private ClassReflection $declaringClass, private ExtendedPropertyReflection $reflection, private Type $readableType, private Type $writableType)
15+
public function __construct(private ClassReflection $declaringClass, private ExtendedPropertyReflection $reflection, private Type $readableType, private Type $writableType, private Type $phpDocType, private Type $nativeType)
1616
{
1717
}
1818

@@ -41,6 +41,26 @@ public function getDocComment(): ?string
4141
return $this->reflection->getDocComment();
4242
}
4343

44+
public function hasPhpDocType(): bool
45+
{
46+
return $this->reflection->hasPhpDocType();
47+
}
48+
49+
public function getPhpDocType(): Type
50+
{
51+
return $this->phpDocType;
52+
}
53+
54+
public function hasNativeType(): bool
55+
{
56+
return $this->reflection->hasNativeType();
57+
}
58+
59+
public function getNativeType(): Type
60+
{
61+
return $this->nativeType;
62+
}
63+
4464
public function getReadableType(): Type
4565
{
4666
return $this->readableType;

src/Reflection/Dummy/DummyPropertyReflection.php

+20
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ public function isPublic(): bool
3737
return true;
3838
}
3939

40+
public function hasPhpDocType(): bool
41+
{
42+
return false;
43+
}
44+
45+
public function getPhpDocType(): Type
46+
{
47+
return new MixedType();
48+
}
49+
50+
public function hasNativeType(): bool
51+
{
52+
return false;
53+
}
54+
55+
public function getNativeType(): Type
56+
{
57+
return new MixedType();
58+
}
59+
4060
public function getReadableType(): Type
4161
{
4262
return new MixedType();

src/Reflection/ExtendedPropertyReflection.php

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Reflection;
44

55
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\Type;
67

78
/**
89
* The purpose of this interface is to be able to
@@ -25,6 +26,14 @@ interface ExtendedPropertyReflection extends PropertyReflection
2526

2627
public const HOOK_SET = 'set';
2728

29+
public function hasPhpDocType(): bool;
30+
31+
public function getPhpDocType(): Type;
32+
33+
public function hasNativeType(): bool;
34+
35+
public function getNativeType(): Type;
36+
2837
public function isAbstract(): TrinaryLogic;
2938

3039
public function isFinal(): TrinaryLogic;

src/Reflection/Php/EnumPropertyReflection.php

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Reflection\ExtendedPropertyReflection;
88
use PHPStan\ShouldNotHappenException;
99
use PHPStan\TrinaryLogic;
10+
use PHPStan\Type\MixedType;
1011
use PHPStan\Type\Type;
1112

1213
final class EnumPropertyReflection implements ExtendedPropertyReflection
@@ -41,6 +42,26 @@ public function getDocComment(): ?string
4142
return null;
4243
}
4344

45+
public function hasPhpDocType(): bool
46+
{
47+
return false;
48+
}
49+
50+
public function getPhpDocType(): Type
51+
{
52+
return new MixedType();
53+
}
54+
55+
public function hasNativeType(): bool
56+
{
57+
return false;
58+
}
59+
60+
public function getNativeType(): Type
61+
{
62+
return new MixedType();
63+
}
64+
4465
public function getReadableType(): Type
4566
{
4667
return $this->type;

src/Reflection/Php/SimpleXMLElementProperty.php

+21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Type\BooleanType;
1111
use PHPStan\Type\FloatType;
1212
use PHPStan\Type\IntegerType;
13+
use PHPStan\Type\MixedType;
1314
use PHPStan\Type\StringType;
1415
use PHPStan\Type\Type;
1516
use PHPStan\Type\TypeCombinator;
@@ -44,6 +45,26 @@ public function isPublic(): bool
4445
return true;
4546
}
4647

48+
public function hasPhpDocType(): bool
49+
{
50+
return false;
51+
}
52+
53+
public function getPhpDocType(): Type
54+
{
55+
return new MixedType();
56+
}
57+
58+
public function hasNativeType(): bool
59+
{
60+
return false;
61+
}
62+
63+
public function getNativeType(): Type
64+
{
65+
return new MixedType();
66+
}
67+
4768
public function getReadableType(): Type
4869
{
4970
return $this->type;

src/Reflection/Php/UniversalObjectCrateProperty.php

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Reflection\ExtendedPropertyReflection;
88
use PHPStan\ShouldNotHappenException;
99
use PHPStan\TrinaryLogic;
10+
use PHPStan\Type\MixedType;
1011
use PHPStan\Type\Type;
1112

1213
final class UniversalObjectCrateProperty implements ExtendedPropertyReflection
@@ -40,6 +41,26 @@ public function isPublic(): bool
4041
return true;
4142
}
4243

44+
public function hasPhpDocType(): bool
45+
{
46+
return false;
47+
}
48+
49+
public function getPhpDocType(): Type
50+
{
51+
return new MixedType();
52+
}
53+
54+
public function hasNativeType(): bool
55+
{
56+
return false;
57+
}
58+
59+
public function getNativeType(): Type
60+
{
61+
return new MixedType();
62+
}
63+
4364
public function getReadableType(): Type
4465
{
4566
return $this->readableType;

src/Reflection/ResolvedPropertyReflection.php

+20
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ public function isPublic(): bool
5959
return $this->reflection->isPublic();
6060
}
6161

62+
public function hasPhpDocType(): bool
63+
{
64+
return $this->reflection->hasPhpDocType();
65+
}
66+
67+
public function getPhpDocType(): Type
68+
{
69+
return $this->reflection->getPhpDocType();
70+
}
71+
72+
public function hasNativeType(): bool
73+
{
74+
return $this->reflection->hasNativeType();
75+
}
76+
77+
public function getNativeType(): Type
78+
{
79+
return $this->reflection->getNativeType();
80+
}
81+
6282
public function getReadableType(): Type
6383
{
6484
$type = $this->readableType;

src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,10 @@ private function transformPropertyWithStaticType(ClassReflection $declaringClass
7979
{
8080
$readableType = $this->transformStaticType($property->getReadableType());
8181
$writableType = $this->transformStaticType($property->getWritableType());
82+
$phpDocType = $this->transformStaticType($property->getPhpDocType());
83+
$nativeType = $this->transformStaticType($property->getNativeType());
8284

83-
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType);
85+
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType, $phpDocType, $nativeType);
8486
}
8587

8688
private function transformStaticType(Type $type): Type

src/Reflection/Type/CalledOnTypeUnresolvedPropertyPrototypeReflection.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ private function transformPropertyWithStaticType(ClassReflection $declaringClass
7474
{
7575
$readableType = $this->transformStaticType($property->getReadableType());
7676
$writableType = $this->transformStaticType($property->getWritableType());
77+
$phpDocType = $this->transformStaticType($property->getPhpDocType());
78+
$nativeType = $this->transformStaticType($property->getNativeType());
7779

78-
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType);
80+
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType, $phpDocType, $nativeType);
7981
}
8082

8183
private function transformStaticType(Type $type): Type

src/Reflection/Type/IntersectionTypePropertyReflection.php

+20
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ public function getDocComment(): ?string
8080
return null;
8181
}
8282

83+
public function hasPhpDocType(): bool
84+
{
85+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->hasPhpDocType());
86+
}
87+
88+
public function getPhpDocType(): Type
89+
{
90+
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getPhpDocType(), $this->properties));
91+
}
92+
93+
public function hasNativeType(): bool
94+
{
95+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->hasNativeType());
96+
}
97+
98+
public function getNativeType(): Type
99+
{
100+
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getNativeType(), $this->properties));
101+
}
102+
83103
public function getReadableType(): Type
84104
{
85105
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getReadableType(), $this->properties));

0 commit comments

Comments
 (0)