Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Reflection/Annotations/AnnotationPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Dummy/ChangedTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public function isAbstract(): TrinaryLogic
return $this->reflection->isAbstract();
}

public function isFinalByKeyword(): TrinaryLogic
{
return $this->reflection->isFinalByKeyword();
}

public function isFinal(): TrinaryLogic
{
return $this->reflection->isFinal();
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Dummy/DummyPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/ExtendedPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public function getNativeType(): Type;

public function isAbstract(): TrinaryLogic;

public function isFinalByKeyword(): TrinaryLogic;

public function isFinal(): TrinaryLogic;

public function isVirtual(): TrinaryLogic;
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Php/EnumPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 4 additions & 1 deletion src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ private function createProperty(
$types[] = $value;
}

return new PhpPropertyReflection($declaringClassReflection, null, null, TypeCombinator::union(...$types), $classReflection->getNativeReflection()->getProperty($propertyName), null, null, null, false, false, false, false, []);
return new PhpPropertyReflection($declaringClassReflection, null, null, TypeCombinator::union(...$types), $classReflection->getNativeReflection()->getProperty($propertyName), null, null, null, false, false, false, false, [], false);
}
}

Expand All @@ -227,6 +227,7 @@ private function createProperty(
$isDeprecated = $deprecation !== null;
$isInternal = false;
$isReadOnlyByPhpDoc = $classReflection->isImmutable();
$isFinal = $classReflection->isFinal() || $propertyReflection->isFinal();
$isAllowedPrivateMutation = false;

if (
Expand Down Expand Up @@ -308,6 +309,7 @@ private function createProperty(
}
$isInternal = $resolvedPhpDoc->isInternal();
$isReadOnlyByPhpDoc = $isReadOnlyByPhpDoc || $resolvedPhpDoc->isReadOnly();
$isFinal = $isFinal || $resolvedPhpDoc->isFinal();
$isAllowedPrivateMutation = $resolvedPhpDoc->isAllowedPrivateMutation();
}

Expand Down Expand Up @@ -435,6 +437,7 @@ private function createProperty(
$isReadOnlyByPhpDoc,
$isAllowedPrivateMutation,
$this->attributeReflectionFactory->fromNativeReflection($propertyReflection->getAttributes(), InitializerExprContext::fromClass($declaringClassReflection->getName(), $declaringClassReflection->getFileName())),
$isFinal,
);
}

Expand Down
8 changes: 7 additions & 1 deletion src/Reflection/Php/PhpPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function __construct(
private bool $isReadOnlyByPhpDoc,
private bool $isAllowedPrivateMutation,
private array $attributes,
private bool $isFinal,
)
{
}
Expand Down Expand Up @@ -242,11 +243,16 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createFromBoolean($this->reflection->isAbstract());
}

public function isFinal(): TrinaryLogic
public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->reflection->isFinal());
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->isFinal);
}

public function isVirtual(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->reflection->isVirtual());
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Php/SimpleXMLElementProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Php/UniversalObjectCrateProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/ResolvedPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public function isAbstract(): TrinaryLogic
return $this->reflection->isAbstract();
}

public function isFinalByKeyword(): TrinaryLogic
{
return $this->reflection->isFinalByKeyword();
}

public function isFinal(): TrinaryLogic
{
return $this->reflection->isFinal();
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Type/IntersectionTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::lazyMaxMin($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isAbstract());
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::lazyMaxMin($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isFinalByKeyword());
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::lazyMaxMin($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isFinal());
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Type/UnionTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isAbstract());
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isFinalByKeyword());
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (ExtendedPropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isFinal());
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/WrappedExtendedPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know what this class represents, hope it's okay

Expand Down
5 changes: 5 additions & 0 deletions src/Rules/Properties/FoundPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ public function isAbstract(): TrinaryLogic
return $this->originalPropertyReflection->isAbstract();
}

public function isFinalByKeyword(): TrinaryLogic
{
return $this->originalPropertyReflection->isFinalByKeyword();
}

public function isFinal(): TrinaryLogic
{
return $this->originalPropertyReflection->isFinal();
Expand Down
12 changes: 11 additions & 1 deletion src/Rules/Properties/OverridingPropertyRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public function processNode(Node $node, Scope $scope): array
))->identifier('property.visibility')->nonIgnorable()->build();
}

if ($prototype->isFinal()->yes()) {
if ($prototype->isFinalByKeyword()->yes()) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Property %s::$%s overrides final property %s::$%s.',
$classReflection->getDisplayName(),
Expand All @@ -145,6 +145,16 @@ public function processNode(Node $node, Scope $scope): array
))->identifier('property.parentPropertyFinal')
->nonIgnorable()
->build();
} elseif ($prototype->isFinal()->yes()) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Property %s::$%s overrides @final property %s::$%s.',
$classReflection->getDisplayName(),
$node->getName(),
$prototype->getDeclaringClass()->getDisplayName(),
$node->getName(),
))->identifier('property.parentPropertyAnnotatedFinal')
->nonIgnorable()
->build();
}

$typeErrors = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function isAbstract(): TrinaryLogic
return $this->propertyReflection->isAbstract();
}

public function isFinalByKeyword(): TrinaryLogic
{
return $this->propertyReflection->isFinalByKeyword();
}

public function isFinal(): TrinaryLogic
{
return $this->propertyReflection->isFinal();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ObjectShapePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public function isAbstract(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isFinalByKeyword(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
16 changes: 12 additions & 4 deletions tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,19 +187,27 @@ public function testFinal(): void
$this->analyse([__DIR__ . '/data/overriding-final-property.php'], [
[
'Property OverridingFinalProperty\Bar::$a overrides final property OverridingFinalProperty\Foo::$a.',
21,
27,
],
[
'Property OverridingFinalProperty\Bar::$b overrides final property OverridingFinalProperty\Foo::$b.',
23,
29,
],
[
'Property OverridingFinalProperty\Bar::$c overrides final property OverridingFinalProperty\Foo::$c.',
25,
31,
],
[
'Property OverridingFinalProperty\Bar::$d overrides final property OverridingFinalProperty\Foo::$d.',
27,
33,
],
[
'Property OverridingFinalProperty\Bar::$e overrides @final property OverridingFinalProperty\Foo::$e.',
35,
],
[
'Property OverridingFinalProperty\Bar::$f overrides @final property OverridingFinalProperty\Foo::$f.',
37,
],
]);
}
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Rules/Properties/data/overriding-final-property.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class Foo

protected private(set) int $d;

/** @final */
public $e;

/** @final */
protected $f;

}

class Bar extends Foo
Expand All @@ -26,4 +32,8 @@ class Bar extends Foo

public int $d;

public $e;

protected $f;

}
26 changes: 13 additions & 13 deletions tests/PHPStan/Rules/Variables/UnsetRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,55 +167,55 @@ public function testUnsetHookedProperty(): void
],
[
'Cannot unset property UnsetHookedProperty\NonFinalClass::$publicProperty because it might have hooks in a subclass.',
13,
14,
],
[
'Cannot unset property UnsetHookedProperty\ContainerClass::$finalClass because it might have hooks in a subclass.',
83,
86,
],
[
'Cannot unset property UnsetHookedProperty\ContainerClass::$nonFinalClass because it might have hooks in a subclass.',
87,
91,
],
[
'Cannot unset hooked UnsetHookedProperty\Foo::$iii property.',
89,
93,
],
[
'Cannot unset property UnsetHookedProperty\ContainerClass::$foo because it might have hooks in a subclass.',
90,
94,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$name property.',
92,
96,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$fullName property.',
93,
97,
],
[
'Cannot unset property UnsetHookedProperty\ContainerClass::$user because it might have hooks in a subclass.',
94,
98,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$name property.',
96,
100,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$name property.',
97,
101,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$fullName property.',
98,
102,
],
[
'Cannot unset hooked UnsetHookedProperty\User::$fullName property.',
99,
103,
],
[
'Cannot unset property UnsetHookedProperty\ContainerClass::$arrayOfUsers because it might have hooks in a subclass.',
100,
104,
],
]);
}
Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/Variables/data/unset-hooked-property.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function doUnset(Foo $foo, User $user, NonFinalClass $nonFinalClass, FinalClass
unset($foo->ii);
unset($foo->iii);

unset($nonFinalClass->publicAnnotatedFinalProperty);
unset($nonFinalClass->publicFinalProperty);
unset($nonFinalClass->publicProperty);

Expand Down Expand Up @@ -49,6 +50,8 @@ class NonFinalClass {
private string $privateProperty;
public string $publicProperty;
final public string $publicFinalProperty;
/** @final */
public string $publicAnnotatedFinalProperty;

function doFoo() {
unset($this->privateProperty);
Expand Down Expand Up @@ -82,6 +85,7 @@ function dooNestedUnset(ContainerClass $containerClass) {
unset($containerClass->finalClass->publicProperty);
unset($containerClass->finalClass);

unset($containerClass->nonFinalClass->publicAnnotatedFinalProperty);
unset($containerClass->nonFinalClass->publicFinalProperty);
unset($containerClass->nonFinalClass->publicProperty);
unset($containerClass->nonFinalClass);
Expand Down
Loading