Skip to content

Commit

Permalink
Fix access to uninitialized property via extension in additional cons…
Browse files Browse the repository at this point in the history
…tructor
  • Loading branch information
ondrejmirtes committed Jul 12, 2023
1 parent 8d5deb9 commit 218aad0
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 19 deletions.
36 changes: 17 additions & 19 deletions src/Node/ClassPropertiesNode.php
Expand Up @@ -109,6 +109,9 @@ public function getUninitializedProperties(
$originalProperties = [];
$initialInitializedProperties = [];
$initializedProperties = [];
if ($extensions === null) {
$extensions = $this->readWritePropertiesExtensionProvider->getExtensions();
}
foreach ($this->getProperties() as $property) {
if ($property->isStatic()) {
continue;
Expand All @@ -121,34 +124,29 @@ public function getUninitializedProperties(
}
$originalProperties[$property->getName()] = $property;
$is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait());
if (!$is->yes()) {
foreach ($extensions as $extension) {
if (!$classReflection->hasNativeProperty($property->getName())) {
continue;
}
$propertyReflection = $classReflection->getNativeProperty($property->getName());
if (!$extension->isInitialized($propertyReflection, $property->getName())) {
continue;
}
$is = TrinaryLogic::createYes();
break;
}
}
$initialInitializedProperties[$property->getName()] = $is;
foreach ($constructors as $constructor) {
$initializedProperties[$constructor][$property->getName()] = $is;
}
if ($property->isPromoted() && !$property->isPromotedFromTrait()) {
if ($is->yes()) {
continue;
}
$uninitializedProperties[$property->getName()] = $property;
}

if ($extensions === null) {
$extensions = $this->readWritePropertiesExtensionProvider->getExtensions();
}

foreach (array_keys($uninitializedProperties) as $name) {
foreach ($extensions as $extension) {
if (!$classReflection->hasNativeProperty($name)) {
continue;
}
$propertyReflection = $classReflection->getNativeProperty($name);
if (!$extension->isInitialized($propertyReflection, $name)) {
continue;
}
unset($uninitializedProperties[$name]);
break;
}
}

if ($constructors === []) {
return [$uninitializedProperties, [], []];
}
Expand Down
28 changes: 28 additions & 0 deletions tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Reflection\PropertyReflection;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use function strpos;

/**
* @extends RuleTestCase<UninitializedPropertyRule>
Expand All @@ -20,6 +21,7 @@ protected function getRule(): Rule
self::getContainer(),
[
'UninitializedProperty\\TestCase::setUp',
'Bug9619\\AdminPresenter::startup',
],
),
);
Expand All @@ -46,6 +48,27 @@ public function isInitialized(PropertyReflection $property, string $propertyName
}

},

// bug-9619
new class() implements ReadWritePropertiesExtension {

public function isAlwaysRead(PropertyReflection $property, string $propertyName): bool
{
return false;
}

public function isAlwaysWritten(PropertyReflection $property, string $propertyName): bool
{
return $this->isInitialized($property, $propertyName);
}

public function isInitialized(PropertyReflection $property, string $propertyName): bool
{
return $property->isPublic() &&
strpos($property->getDocComment() ?? '', '@inject') !== false;
}

},
];
}

Expand Down Expand Up @@ -156,4 +179,9 @@ public function testEfabricaLatteBug(): void
$this->analyse([__DIR__ . '/data/efabrica-latte-bug.php'], []);
}

public function testBug9619(): void
{
$this->analyse([__DIR__ . '/data/bug-9619.php'], []);
}

}
23 changes: 23 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-9619.php
@@ -0,0 +1,23 @@
<?php // lint >= 7.4

namespace Bug9619;

interface User
{

public function isLoggedIn(): bool;

}

class AdminPresenter
{
/** @inject */
public User $user;

public function startup()
{
if (!$this->user->isLoggedIn()) {
// do something
}
}
}

0 comments on commit 218aad0

Please sign in to comment.