Skip to content

Commit b82230a

Browse files
committed
Virtual property cannot be uninitialized
1 parent 743f1ab commit b82230a

8 files changed

+64
-6
lines changed

src/Analyser/MutatingScope.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2633,7 +2633,7 @@ public function hasPropertyNativeType($propertyFetch): bool
26332633
return false;
26342634
}
26352635

2636-
return !$propertyReflection->getNativeType() instanceof MixedType;
2636+
return $propertyReflection->hasNativeType();
26372637
}
26382638

26392639
private function getTypeFromArrayDimFetch(

src/Node/ClassPropertiesNode.php

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ public function getUninitializedProperties(
131131
$is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait());
132132
if (!$is->yes() && $classReflection->hasNativeProperty($property->getName())) {
133133
$propertyReflection = $classReflection->getNativeProperty($property->getName());
134+
if ($propertyReflection->isVirtual()->yes()) {
135+
continue;
136+
}
134137

135138
foreach ($extensions as $extension) {
136139
if (!$extension->isInitialized($propertyReflection, $property->getName())) {

src/Rules/IssetCheck.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PHPStan\Analyser\Scope;
88
use PHPStan\Rules\Properties\PropertyDescriptor;
99
use PHPStan\Rules\Properties\PropertyReflectionFinder;
10-
use PHPStan\Type\MixedType;
1110
use PHPStan\Type\NeverType;
1211
use PHPStan\Type\Type;
1312
use PHPStan\Type\VerbosityLevel;
@@ -143,8 +142,7 @@ public function check(Expr $expr, Scope $scope, string $operatorDescription, str
143142
return null;
144143
}
145144

146-
$nativeType = $propertyReflection->getNativeType();
147-
if (!$nativeType instanceof MixedType) {
145+
if ($propertyReflection->hasNativeType() && !$propertyReflection->isVirtual()->yes()) {
148146
if (!$scope->hasExpressionType($expr)->yes()) {
149147
if ($expr instanceof Node\Expr\PropertyFetch) {
150148
return $this->checkUndefined($expr->var, $scope, $operatorDescription, $identifier);

src/Rules/Properties/DefaultValueTypesAssignedToPropertiesRule.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PHPStan\Rules\Rule;
99
use PHPStan\Rules\RuleErrorBuilder;
1010
use PHPStan\Rules\RuleLevelHelper;
11-
use PHPStan\Type\MixedType;
1211
use PHPStan\Type\VerbosityLevel;
1312
use function sprintf;
1413

@@ -38,7 +37,7 @@ public function processNode(Node $node, Scope $scope): array
3837

3938
$propertyReflection = $classReflection->getNativeProperty($node->getName());
4039
$propertyType = $propertyReflection->getWritableType();
41-
if ($propertyReflection->getNativeType() instanceof MixedType) {
40+
if (!$propertyReflection->hasNativeType()) {
4241
if ($default instanceof Node\Expr\ConstFetch && $default->name->toLowerString() === 'null') {
4342
return [];
4443
}

tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,13 @@ public function testBug12336(): void
226226
$this->analyse([__DIR__ . '/data/bug-12336.php'], []);
227227
}
228228

229+
public function testBug12547(): void
230+
{
231+
if (PHP_VERSION_ID < 80400) {
232+
$this->markTestSkipped('Test requires PHP 8.4.');
233+
}
234+
235+
$this->analyse([__DIR__ . '/data/bug-12547.php'], []);
236+
}
237+
229238
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php // lint >= 8.4
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug12547;
6+
7+
class Example {
8+
public \DateTimeImmutable $noon {
9+
get => new \DateTimeImmutable('12:00');
10+
}
11+
}

tests/PHPStan/Rules/Variables/IssetRuleTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -449,4 +449,19 @@ public function testBug10064(): void
449449
$this->analyse([__DIR__ . '/data/bug-10064.php'], []);
450450
}
451451

452+
public function testVirtualProperty(): void
453+
{
454+
if (PHP_VERSION_ID < 80400) {
455+
$this->markTestSkipped('Test requires PHP 8.4.');
456+
}
457+
458+
$this->treatPhpDocTypesAsCertain = true;
459+
$this->analyse([__DIR__ . '/data/isset-virtual-property.php'], [
460+
[
461+
'Property IssetVirtualProperty\Example::$noon (DateTimeImmutable) in isset() is not nullable.',
462+
16,
463+
],
464+
]);
465+
}
466+
452467
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php // lint >= 8.4
2+
3+
namespace IssetVirtualProperty;
4+
5+
class Example {
6+
public \DateTimeImmutable $noon {
7+
get => new \DateTimeImmutable('12:00');
8+
}
9+
10+
public ?\DateTimeImmutable $nullableNoon {
11+
get => new \DateTimeImmutable('12:00');
12+
}
13+
14+
public function doFoo(): void
15+
{
16+
if (isset($this->noon)) {
17+
18+
}
19+
if (isset($this->nullableNoon)) {
20+
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)