Skip to content

Commit 439efe7

Browse files
committed
PhpPropertyReflection can have different readable/writable type when amended by @property
1 parent 5a29d55 commit 439efe7

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

src/Reflection/Php/PhpClassReflectionExtension.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ private function createProperty(
220220
$types[] = $value;
221221
}
222222

223-
return new PhpPropertyReflection($declaringClassReflection, null, null, TypeCombinator::union(...$types), $classReflection->getNativeReflection()->getProperty($propertyName), null, null, null, false, false, false, false, [], false, true, false, false, true);
223+
$phpDocType = TypeCombinator::union(...$types);
224+
225+
return new PhpPropertyReflection($declaringClassReflection, null, null, $phpDocType, $phpDocType, $classReflection->getNativeReflection()->getProperty($propertyName), null, null, null, false, false, false, false, [], false, true, false, false, true);
224226
}
225227
}
226228

@@ -405,6 +407,7 @@ private function createProperty(
405407
$declaringTrait,
406408
$nativeType,
407409
$phpDocType,
410+
$phpDocType,
408411
$propertyReflection,
409412
$getHook,
410413
$setHook,
@@ -451,7 +454,8 @@ private function createProperty(
451454
$annotationProperty->getDeclaringClass(),
452455
$declaringTrait,
453456
$nativeType,
454-
$annotationProperty->getPhpDocType(),
457+
$annotationProperty->getReadableType(),
458+
$annotationProperty->getWritableType(),
455459
$propertyReflection,
456460
$getHook,
457461
$setHook,

src/Reflection/Php/PhpPropertyReflection.php

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Reflection\MissingMethodFromReflectionException;
1414
use PHPStan\TrinaryLogic;
1515
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\NeverType;
1617
use PHPStan\Type\Type;
1718
use PHPStan\Type\TypehintHelper;
1819
use function sprintf;
@@ -25,7 +26,9 @@ final class PhpPropertyReflection implements ExtendedPropertyReflection
2526

2627
private ?Type $finalNativeType = null;
2728

28-
private ?Type $type = null;
29+
private ?Type $readableType = null;
30+
31+
private ?Type $writableType = null;
2932

3033
/**
3134
* @param list<AttributeReflection> $attributes
@@ -34,7 +37,8 @@ public function __construct(
3437
private ClassReflection $declaringClass,
3538
private ?ClassReflection $declaringTrait,
3639
private ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null $nativeType,
37-
private ?Type $phpDocType,
40+
private ?Type $readablePhpDocType,
41+
private ?Type $writablePhpDocType,
3842
private ReflectionProperty $reflection,
3943
private ?ExtendedMethodReflection $getHook,
4044
private ?ExtendedMethodReflection $setHook,
@@ -105,9 +109,9 @@ public function isReadOnlyByPhpDoc(): bool
105109

106110
public function getReadableType(): Type
107111
{
108-
return $this->type ??= TypehintHelper::decideTypeFromReflection(
112+
return $this->readableType ??= TypehintHelper::decideTypeFromReflection(
109113
$this->nativeType,
110-
$this->phpDocType,
114+
$this->readablePhpDocType,
111115
$this->declaringClass,
112116
);
113117
}
@@ -122,13 +126,36 @@ public function getWritableType(): Type
122126
}
123127
}
124128

125-
return $this->getReadableType();
129+
if ($this->writableType !== null) {
130+
return $this->writableType;
131+
}
132+
133+
if ($this->writablePhpDocType === null || $this->writablePhpDocType instanceof NeverType) {
134+
return $this->writableType = TypehintHelper::decideTypeFromReflection(
135+
$this->nativeType,
136+
$this->readablePhpDocType,
137+
$this->declaringClass,
138+
);
139+
}
140+
141+
if (
142+
$this->readablePhpDocType !== null
143+
&& !$this->readablePhpDocType->equals($this->writablePhpDocType)
144+
) {
145+
return $this->writableType = $this->writablePhpDocType;
146+
}
147+
148+
return $this->writableType = TypehintHelper::decideTypeFromReflection(
149+
$this->nativeType,
150+
$this->writablePhpDocType,
151+
$this->declaringClass,
152+
);
126153
}
127154

128155
public function canChangeTypeAfterAssignment(): bool
129156
{
130157
if ($this->isStatic()) {
131-
return true;
158+
return $this->getReadableType()->equals($this->getWritableType());
132159
}
133160

134161
if ($this->isVirtual()->yes()) {
@@ -143,7 +170,7 @@ public function canChangeTypeAfterAssignment(): bool
143170
return false;
144171
}
145172

146-
return true;
173+
return $this->getReadableType()->equals($this->getWritableType());
147174
}
148175

149176
public function isPromoted(): bool
@@ -153,13 +180,13 @@ public function isPromoted(): bool
153180

154181
public function hasPhpDocType(): bool
155182
{
156-
return $this->phpDocType !== null;
183+
return $this->readablePhpDocType !== null;
157184
}
158185

159186
public function getPhpDocType(): Type
160187
{
161-
if ($this->phpDocType !== null) {
162-
return $this->phpDocType;
188+
if ($this->readablePhpDocType !== null) {
189+
return $this->readablePhpDocType;
163190
}
164191

165192
return new MixedType();

0 commit comments

Comments
 (0)