diff --git a/src/Analyser/ExprHandler/NewHandler.php b/src/Analyser/ExprHandler/NewHandler.php index 67cc17797d5..3b23d8201ee 100644 --- a/src/Analyser/ExprHandler/NewHandler.php +++ b/src/Analyser/ExprHandler/NewHandler.php @@ -416,10 +416,13 @@ private function exactInstantiation(MutatingScope $scope, New_ $node, Name $clas $classTemplateTypes = $traverser->getClassTemplateTypes(); if (count($classTemplateTypes) === count($originalClassTemplateTypes)) { - $propertyType = TypeCombinator::removeNull($scope->getType($assignedToProperty)); - $nonFinalObjectType = $isStatic ? new StaticType($nonFinalClassReflection) : new ObjectType($resolvedClassName, classReflection: $nonFinalClassReflection); - if ($nonFinalObjectType->isSuperTypeOf($propertyType)->yes()) { - return $propertyType; + $propertyType = $this->resolveAssignedPropertyDeclaredType($assignedToProperty, $scope); + if ($propertyType !== null) { + $propertyType = TypeCombinator::removeNull($propertyType); + $nonFinalObjectType = $isStatic ? new StaticType($nonFinalClassReflection) : new ObjectType($resolvedClassName, classReflection: $nonFinalClassReflection); + if ($nonFinalObjectType->isSuperTypeOf($propertyType)->yes()) { + return $propertyType; + } } } } @@ -590,4 +593,25 @@ classReflection: $classReflection->withTypes($types)->asFinal(), return TypeTraverser::map($newGenericType, new GenericTypeTemplateTraverser($resolvedTemplateTypeMap)); } + private function resolveAssignedPropertyDeclaredType(Node\Expr $propertyFetch, MutatingScope $scope): ?Type + { + if ($propertyFetch instanceof Node\Expr\PropertyFetch && $propertyFetch->name instanceof Node\Identifier) { + $holderType = $scope->getType($propertyFetch->var); + $propertyName = $propertyFetch->name->name; + if ($holderType->hasInstanceProperty($propertyName)->yes()) { + return $holderType->getInstanceProperty($propertyName, $scope)->getReadableType(); + } + } elseif ($propertyFetch instanceof Node\Expr\StaticPropertyFetch && $propertyFetch->name instanceof Node\VarLikeIdentifier && $propertyFetch->class instanceof Name) { + $className = $scope->resolveName($propertyFetch->class); + if ($this->reflectionProvider->hasClass($className)) { + $classRefl = $this->reflectionProvider->getClass($className); + $propertyName = $propertyFetch->name->name; + if ($classRefl->hasStaticProperty($propertyName)) { + return $classRefl->getStaticProperty($propertyName)->getReadableType(); + } + } + } + return null; + } + } diff --git a/tests/PHPStan/Rules/TooWideTypehints/TooWidePropertyTypeRuleTest.php b/tests/PHPStan/Rules/TooWideTypehints/TooWidePropertyTypeRuleTest.php index 4ddf860b4de..cab35b9a6ae 100644 --- a/tests/PHPStan/Rules/TooWideTypehints/TooWidePropertyTypeRuleTest.php +++ b/tests/PHPStan/Rules/TooWideTypehints/TooWidePropertyTypeRuleTest.php @@ -138,4 +138,10 @@ public function testBug13624(): void $this->analyse([__DIR__ . '/data/bug-13624.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug11844(): void + { + $this->analyse([__DIR__ . '/data/bug-11844.php'], []); + } + } diff --git a/tests/PHPStan/Rules/TooWideTypehints/data/bug-11844.php b/tests/PHPStan/Rules/TooWideTypehints/data/bug-11844.php new file mode 100644 index 00000000000..95a0416bbad --- /dev/null +++ b/tests/PHPStan/Rules/TooWideTypehints/data/bug-11844.php @@ -0,0 +1,48 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug11844; + +class StaticPropertyCase +{ + /** + * @var \WeakMap|null + */ + private static ?\WeakMap $map = null; + + public static function init(): void + { + if (self::$map === null) { + self::$map = new \WeakMap(); + } + } +} + +class InstancePropertyCase +{ + /** + * @var \WeakMap|null + */ + private ?\WeakMap $map = null; + + public function init(): void + { + if ($this->map === null) { + $this->map = new \WeakMap(); + } + } +} + +class DirectAssignCase +{ + /** + * @var \WeakMap|null + */ + private ?\WeakMap $map = null; + + public function initAlways(): void + { + $this->map = new \WeakMap(); + } +}