Skip to content

Commit 6bf3ce7

Browse files
committed
Fix false positive about assign to protected property from a subclass
1 parent 5a50985 commit 6bf3ce7

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

src/Analyser/MutatingScope.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5655,7 +5655,7 @@ public function canWriteProperty(ExtendedPropertyReflection $propertyReflection)
56555655

56565656
if (
56575657
$classReflection->getName() === $propertyDeclaringClass->getName()
5658-
|| $classReflection->isSubclassOfClass($propertyDeclaringClass)
5658+
|| $classReflection->isSubclassOfClass($propertyDeclaringClass->removeFinalKeywordOverride())
56595659
) {
56605660
return true;
56615661
}
@@ -5712,7 +5712,7 @@ private function canAccessClassMember(ClassMemberReflection $classMemberReflecti
57125712

57135713
if (
57145714
$classReflection->getName() === $classMemberDeclaringClass->getName()
5715-
|| $classReflection->isSubclassOfClass($classMemberDeclaringClass)
5715+
|| $classReflection->isSubclassOfClass($classMemberDeclaringClass->removeFinalKeywordOverride())
57165716
) {
57175717
return true;
57185718
}

src/Reflection/ClassReflection.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,48 @@ public function asFinal(): self
17021702
);
17031703
}
17041704

1705+
public function removeFinalKeywordOverride(): self
1706+
{
1707+
if ($this->getNativeReflection()->isFinal()) {
1708+
return $this;
1709+
}
1710+
if ($this->finalByKeywordOverride === false) {
1711+
return $this;
1712+
}
1713+
if (!$this->isClass()) {
1714+
return $this;
1715+
}
1716+
if ($this->isAbstract()) {
1717+
return $this;
1718+
}
1719+
1720+
return new self(
1721+
$this->reflectionProvider,
1722+
$this->initializerExprTypeResolver,
1723+
$this->fileTypeMapper,
1724+
$this->stubPhpDocProvider,
1725+
$this->phpDocInheritanceResolver,
1726+
$this->phpVersion,
1727+
$this->signatureMapProvider,
1728+
$this->deprecationProvider,
1729+
$this->attributeReflectionFactory,
1730+
$this->propertiesClassReflectionExtensions,
1731+
$this->methodsClassReflectionExtensions,
1732+
$this->allowedSubTypesClassReflectionExtensions,
1733+
$this->requireExtendsPropertiesClassReflectionExtension,
1734+
$this->requireExtendsMethodsClassReflectionExtension,
1735+
$this->displayName,
1736+
$this->reflection,
1737+
$this->anonymousFilename,
1738+
$this->resolvedTemplateTypeMap,
1739+
$this->stubPhpDocBlock,
1740+
$this->universalObjectCratesClasses,
1741+
null,
1742+
$this->resolvedCallSiteVarianceMap,
1743+
false,
1744+
);
1745+
}
1746+
17051747
public function getResolvedPhpDoc(): ?ResolvedPhpDocBlock
17061748
{
17071749
if ($this->stubPhpDocBlock !== null) {

tests/PHPStan/Rules/Properties/AccessPropertiesInAssignRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,9 @@ public function testAsymmetricVisibility(): void
177177
]);
178178
}
179179

180+
public function testBug13123(): void
181+
{
182+
$this->analyse([__DIR__ . '/data/bug-13123.php'], []);
183+
}
184+
180185
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Bug13123;
4+
5+
class ControlGroup {}
6+
7+
class Container {
8+
protected ?ControlGroup $currentGroup = null;
9+
}
10+
11+
class Multiplier extends Container {
12+
protected function createContainer(): Container
13+
{
14+
$control = new Container();
15+
$control->currentGroup = $this->currentGroup;
16+
17+
return $control;
18+
}
19+
20+
protected function createContainer2(Container $control): Container
21+
{
22+
$control->currentGroup = $this->currentGroup;
23+
24+
return $control;
25+
}
26+
27+
28+
}

0 commit comments

Comments
 (0)