diff --git a/src/Reflection/Annotations/AnnotationPropertyReflection.php b/src/Reflection/Annotations/AnnotationPropertyReflection.php
index 8f4f322d08..a79ffaaf3b 100644
--- a/src/Reflection/Annotations/AnnotationPropertyReflection.php
+++ b/src/Reflection/Annotations/AnnotationPropertyReflection.php
@@ -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();
diff --git a/src/Reflection/Dummy/ChangedTypePropertyReflection.php b/src/Reflection/Dummy/ChangedTypePropertyReflection.php
index 3bf6a6eb84..b43cf53453 100644
--- a/src/Reflection/Dummy/ChangedTypePropertyReflection.php
+++ b/src/Reflection/Dummy/ChangedTypePropertyReflection.php
@@ -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();
diff --git a/src/Reflection/Dummy/DummyPropertyReflection.php b/src/Reflection/Dummy/DummyPropertyReflection.php
index ca828a53fe..c90f546344 100644
--- a/src/Reflection/Dummy/DummyPropertyReflection.php
+++ b/src/Reflection/Dummy/DummyPropertyReflection.php
@@ -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();
diff --git a/src/Reflection/ExtendedPropertyReflection.php b/src/Reflection/ExtendedPropertyReflection.php
index 1027c193f1..ad8b5a26d8 100644
--- a/src/Reflection/ExtendedPropertyReflection.php
+++ b/src/Reflection/ExtendedPropertyReflection.php
@@ -38,6 +38,8 @@ public function getNativeType(): Type;
 
 	public function isAbstract(): TrinaryLogic;
 
+	public function isFinalByKeyword(): TrinaryLogic;
+
 	public function isFinal(): TrinaryLogic;
 
 	public function isVirtual(): TrinaryLogic;
diff --git a/src/Reflection/Php/EnumPropertyReflection.php b/src/Reflection/Php/EnumPropertyReflection.php
index 912b779e67..891491a1c5 100644
--- a/src/Reflection/Php/EnumPropertyReflection.php
+++ b/src/Reflection/Php/EnumPropertyReflection.php
@@ -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();
diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php
index fc0dd70dbe..19144986c4 100644
--- a/src/Reflection/Php/PhpClassReflectionExtension.php
+++ b/src/Reflection/Php/PhpClassReflectionExtension.php
@@ -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);
 			}
 		}
 
@@ -227,6 +227,7 @@ private function createProperty(
 		$isDeprecated = $deprecation !== null;
 		$isInternal = false;
 		$isReadOnlyByPhpDoc = $classReflection->isImmutable();
+		$isFinal = $classReflection->isFinal() || $propertyReflection->isFinal();
 		$isAllowedPrivateMutation = false;
 
 		if (
@@ -308,6 +309,7 @@ private function createProperty(
 			}
 			$isInternal = $resolvedPhpDoc->isInternal();
 			$isReadOnlyByPhpDoc = $isReadOnlyByPhpDoc || $resolvedPhpDoc->isReadOnly();
+			$isFinal = $isFinal || $resolvedPhpDoc->isFinal();
 			$isAllowedPrivateMutation = $resolvedPhpDoc->isAllowedPrivateMutation();
 		}
 
@@ -435,6 +437,7 @@ private function createProperty(
 			$isReadOnlyByPhpDoc,
 			$isAllowedPrivateMutation,
 			$this->attributeReflectionFactory->fromNativeReflection($propertyReflection->getAttributes(), InitializerExprContext::fromClass($declaringClassReflection->getName(), $declaringClassReflection->getFileName())),
+			$isFinal,
 		);
 	}
 
diff --git a/src/Reflection/Php/PhpPropertyReflection.php b/src/Reflection/Php/PhpPropertyReflection.php
index 8307f36d7b..8c4ea07439 100644
--- a/src/Reflection/Php/PhpPropertyReflection.php
+++ b/src/Reflection/Php/PhpPropertyReflection.php
@@ -44,6 +44,7 @@ public function __construct(
 		private bool $isReadOnlyByPhpDoc,
 		private bool $isAllowedPrivateMutation,
 		private array $attributes,
+		private bool $isFinal,
 	)
 	{
 	}
@@ -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());
diff --git a/src/Reflection/Php/SimpleXMLElementProperty.php b/src/Reflection/Php/SimpleXMLElementProperty.php
index 29975a5e3f..6fb9316fb5 100644
--- a/src/Reflection/Php/SimpleXMLElementProperty.php
+++ b/src/Reflection/Php/SimpleXMLElementProperty.php
@@ -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();
diff --git a/src/Reflection/Php/UniversalObjectCrateProperty.php b/src/Reflection/Php/UniversalObjectCrateProperty.php
index 564613e219..924b249e6f 100644
--- a/src/Reflection/Php/UniversalObjectCrateProperty.php
+++ b/src/Reflection/Php/UniversalObjectCrateProperty.php
@@ -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();
diff --git a/src/Reflection/ResolvedPropertyReflection.php b/src/Reflection/ResolvedPropertyReflection.php
index df7a33f84d..8b54c0785a 100644
--- a/src/Reflection/ResolvedPropertyReflection.php
+++ b/src/Reflection/ResolvedPropertyReflection.php
@@ -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();
diff --git a/src/Reflection/Type/IntersectionTypePropertyReflection.php b/src/Reflection/Type/IntersectionTypePropertyReflection.php
index 71de28e1e6..1e3e4d3179 100644
--- a/src/Reflection/Type/IntersectionTypePropertyReflection.php
+++ b/src/Reflection/Type/IntersectionTypePropertyReflection.php
@@ -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());
diff --git a/src/Reflection/Type/UnionTypePropertyReflection.php b/src/Reflection/Type/UnionTypePropertyReflection.php
index 77e1ed0397..1862d3059f 100644
--- a/src/Reflection/Type/UnionTypePropertyReflection.php
+++ b/src/Reflection/Type/UnionTypePropertyReflection.php
@@ -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());
diff --git a/src/Reflection/WrappedExtendedPropertyReflection.php b/src/Reflection/WrappedExtendedPropertyReflection.php
index 04eceb4848..ffb2a34363 100644
--- a/src/Reflection/WrappedExtendedPropertyReflection.php
+++ b/src/Reflection/WrappedExtendedPropertyReflection.php
@@ -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();
diff --git a/src/Rules/Properties/FoundPropertyReflection.php b/src/Rules/Properties/FoundPropertyReflection.php
index 1b14b785aa..b1a890b6be 100644
--- a/src/Rules/Properties/FoundPropertyReflection.php
+++ b/src/Rules/Properties/FoundPropertyReflection.php
@@ -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();
diff --git a/src/Rules/Properties/OverridingPropertyRule.php b/src/Rules/Properties/OverridingPropertyRule.php
index c1806565e2..8f4e23a861 100644
--- a/src/Rules/Properties/OverridingPropertyRule.php
+++ b/src/Rules/Properties/OverridingPropertyRule.php
@@ -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(),
@@ -145,6 +145,15 @@ 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.parentPropertyFinalByPhpDoc')
+				->build();
 		}
 
 		$typeErrors = [];
diff --git a/src/Rules/RestrictedUsage/RewrittenDeclaringClassPropertyReflection.php b/src/Rules/RestrictedUsage/RewrittenDeclaringClassPropertyReflection.php
index 69eb2dbf4f..d6b0424be9 100644
--- a/src/Rules/RestrictedUsage/RewrittenDeclaringClassPropertyReflection.php
+++ b/src/Rules/RestrictedUsage/RewrittenDeclaringClassPropertyReflection.php
@@ -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();
diff --git a/src/Type/ObjectShapePropertyReflection.php b/src/Type/ObjectShapePropertyReflection.php
index 594c3278ca..1d022cb726 100644
--- a/src/Type/ObjectShapePropertyReflection.php
+++ b/src/Type/ObjectShapePropertyReflection.php
@@ -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();
diff --git a/tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php b/tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php
index c5f5cd929c..dd9749e079 100644
--- a/tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php
+++ b/tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php
@@ -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,
 			],
 		]);
 	}
diff --git a/tests/PHPStan/Rules/Properties/data/overriding-final-property.php b/tests/PHPStan/Rules/Properties/data/overriding-final-property.php
index 02d7467e57..b41b531c98 100644
--- a/tests/PHPStan/Rules/Properties/data/overriding-final-property.php
+++ b/tests/PHPStan/Rules/Properties/data/overriding-final-property.php
@@ -13,6 +13,12 @@ class Foo
 
 	protected private(set) int $d;
 
+	/** @final */
+	public $e;
+
+	/** @final */
+	protected $f;
+
 }
 
 class Bar extends Foo
@@ -26,4 +32,8 @@ class Bar extends Foo
 
 	public int $d;
 
+	public $e;
+
+	protected $f;
+
 }
diff --git a/tests/PHPStan/Rules/Variables/UnsetRuleTest.php b/tests/PHPStan/Rules/Variables/UnsetRuleTest.php
index df34c15d50..cfe63c0dd7 100644
--- a/tests/PHPStan/Rules/Variables/UnsetRuleTest.php
+++ b/tests/PHPStan/Rules/Variables/UnsetRuleTest.php
@@ -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,
 			],
 		]);
 	}
diff --git a/tests/PHPStan/Rules/Variables/data/unset-hooked-property.php b/tests/PHPStan/Rules/Variables/data/unset-hooked-property.php
index d98eed672a..97ba5781cd 100644
--- a/tests/PHPStan/Rules/Variables/data/unset-hooked-property.php
+++ b/tests/PHPStan/Rules/Variables/data/unset-hooked-property.php
@@ -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);
 
@@ -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);
@@ -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);