diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 837bbd5aa3..9480b925af 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1419,7 +1419,7 @@ private function resolveType(Expr $node): Type return new ErrorType(); } - return new StaticType($this->getClassReflection()->getName()); + return new StaticType($this->getClassReflection()); } if ($lowercasedClassName === 'parent') { return new NonexistentParentClassType(); @@ -1760,7 +1760,7 @@ private function resolveType(Expr $node): Type $namesToResolve[] = 'static'; } elseif (strtolower($constantClass) === 'static') { if (strtolower($constantName) === 'class') { - return new GenericClassStringType(new StaticType($this->getClassReflection()->getName())); + return new GenericClassStringType(new StaticType($this->getClassReflection())); } return new MixedType(); } @@ -2452,7 +2452,7 @@ public function resolveTypeByName(Name $name): TypeWithClassName if ($name->toLowerString() === 'static' && $this->isInClass()) { $classReflection = $this->getClassReflection(); - return new StaticType($classReflection->getName()); + return new StaticType($classReflection); } $originalClass = $this->resolveName($name); if ($this->isInClass()) { diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 9c7c74023e..5b165dc931 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -119,7 +119,7 @@ public function specifyTypesInCondition( if ($lowercasedClassName === 'self' && $scope->isInClass()) { $type = new ObjectType($scope->getClassReflection()->getName()); } elseif ($lowercasedClassName === 'static' && $scope->isInClass()) { - $type = new StaticType($scope->getClassReflection()->getName()); + $type = new StaticType($scope->getClassReflection()); } elseif ($lowercasedClassName === 'parent') { if ( $scope->isInClass() diff --git a/src/Type/StaticType.php b/src/Type/StaticType.php index 0fb01a6124..9c30f4df1b 100644 --- a/src/Type/StaticType.php +++ b/src/Type/StaticType.php @@ -2,12 +2,15 @@ namespace PHPStan\Type; +use PHPStan\Broker\Broker; use PHPStan\Reflection\ClassMemberAccessAnswerer; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ConstantReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\PropertyReflection; use PHPStan\TrinaryLogic; +use PHPStan\Type\Generic\GenericObjectType; +use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; @@ -17,13 +20,22 @@ class StaticType implements TypeWithClassName use NonGenericTypeTrait; use UndecidedComparisonTypeTrait; - private string $baseClass; + private ClassReflection $classReflection; private ?\PHPStan\Type\ObjectType $staticObjectType = null; - public function __construct(string $baseClass) + private string $baseClass; + + /** + * @param string|ClassReflection $classReflection + */ + public function __construct($classReflection) { - $this->baseClass = $baseClass; + if (is_string($classReflection)) { + $classReflection = Broker::getInstance()->getClass($classReflection); + } + $this->classReflection = $classReflection; + $this->baseClass = $classReflection->getName(); } public function getClassName(): string @@ -39,7 +51,17 @@ public function getAncestorWithClassName(string $className): ?ObjectType public function getStaticObjectType(): ObjectType { if ($this->staticObjectType === null) { - $this->staticObjectType = new ObjectType($this->baseClass); + if ($this->classReflection->isGeneric()) { + $typeMap = $this->classReflection->getTemplateTypeMap()->map(static function (string $name, Type $type): Type { + return TemplateTypeHelper::toArgument($type); + }); + return $this->staticObjectType = new GenericObjectType( + $this->classReflection->getName(), + $this->classReflection->typeMapToList($typeMap) + ); + } + + return $this->staticObjectType = new ObjectType($this->classReflection->getName(), null, $this->classReflection); } return $this->staticObjectType; @@ -155,7 +177,7 @@ public function getConstant(string $constantName): ConstantReflection public function changeBaseClass(ClassReflection $classReflection): self { - return new self($classReflection->getName()); + return new self($classReflection); } public function isIterable(): TrinaryLogic diff --git a/src/Type/ThisType.php b/src/Type/ThisType.php index 8654a7ddc8..d8084b8601 100644 --- a/src/Type/ThisType.php +++ b/src/Type/ThisType.php @@ -2,49 +2,11 @@ namespace PHPStan\Type; -use PHPStan\Broker\Broker; use PHPStan\Reflection\ClassReflection; -use PHPStan\Type\Generic\GenericObjectType; -use PHPStan\Type\Generic\TemplateTypeHelper; class ThisType extends StaticType { - private ClassReflection $classReflection; - - private ?\PHPStan\Type\ObjectType $staticObjectType = null; - - /** - * @param string|ClassReflection $classReflection - */ - public function __construct($classReflection) - { - if (is_string($classReflection)) { - $classReflection = Broker::getInstance()->getClass($classReflection); - } - parent::__construct($classReflection->getName()); - $this->classReflection = $classReflection; - } - - public function getStaticObjectType(): ObjectType - { - if ($this->staticObjectType === null) { - if ($this->classReflection->isGeneric()) { - $typeMap = $this->classReflection->getTemplateTypeMap()->map(static function (string $name, Type $type): Type { - return TemplateTypeHelper::toArgument($type); - }); - return $this->staticObjectType = new GenericObjectType( - $this->classReflection->getName(), - $this->classReflection->typeMapToList($typeMap) - ); - } - - return $this->staticObjectType = new ObjectType($this->classReflection->getName(), null, $this->classReflection); - } - - return $this->staticObjectType; - } - public function changeBaseClass(ClassReflection $classReflection): StaticType { return new self($classReflection); diff --git a/tests/PHPStan/Analyser/data/generic-parent.php b/tests/PHPStan/Analyser/data/generic-parent.php index 93ca0ad274..4e444c91c1 100644 --- a/tests/PHPStan/Analyser/data/generic-parent.php +++ b/tests/PHPStan/Analyser/data/generic-parent.php @@ -39,3 +39,20 @@ public function doFoo() } } + +class E {} + +/** + * @template T of E + */ +class R { + + /** @return T */ + function ret() { return $this->e; } // nonsense, to silence missing return + + function test(): void { + assertType('T of GenericParent\E (class GenericParent\R, argument)', self::ret()); + assertType('T of GenericParent\E (class GenericParent\R, argument)', $this->ret()); + assertType('T of GenericParent\E (class GenericParent\R, argument)', static::ret()); + } +}