diff --git a/src/Type/FileTypeMapper.php b/src/Type/FileTypeMapper.php index ddd2032d62f..ddc6dc87af6 100644 --- a/src/Type/FileTypeMapper.php +++ b/src/Type/FileTypeMapper.php @@ -66,6 +66,9 @@ final class FileTypeMapper /** @var array */ private array $inProcess = []; + /** @var array */ + private array $inProcessNameScopes = []; + /** @var array */ private array $resolvedPhpDocBlockCache = []; @@ -200,6 +203,9 @@ public function getNameScope( { $nameScopeKey = $this->getNameScopeKey($fileName, $className, $traitName, $functionName); if (isset($this->inProcess[$nameScopeKey])) { + if (isset($this->inProcessNameScopes[$nameScopeKey])) { + return $this->inProcessNameScopes[$nameScopeKey]; + } throw new NameScopeAlreadyBeingCreatedException(); } @@ -288,6 +294,8 @@ public function getNameScope( continue; } + $this->inProcessNameScopes[$nameScopeKey] = $nameScope; + $templateTags = $this->phpDocNodeResolver->resolveTemplateTags($parent->getTemplatePhpDocNodes(), $nameScope); $templateTypeMap = new TemplateTypeMap(array_map(static fn (TemplateTag $tag): Type => TemplateTypeFactory::fromTemplateTag($templateTypeScope, $tag), $templateTags)); $nameScope = $nameScope->withTemplateTypeMap($templateTypeMap, $templateTags); @@ -319,6 +327,7 @@ public function getNameScope( ); } finally { unset($this->inProcess[$nameScopeKey]); + unset($this->inProcessNameScopes[$nameScopeKey]); } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-11314.php b/tests/PHPStan/Analyser/nsrt/bug-11314.php new file mode 100644 index 00000000000..e541bb8691c --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11314.php @@ -0,0 +1,51 @@ +breed); + + $cat2 = new Cat2(); + assertType("'British Shorthair'|'Maine Coon'|'Siamese'", $cat2->breed); + + $cat3 = new Cat3(); + assertType("'British Shorthair'|'Maine Coon'|'Siamese'", $cat3->breed); +}; diff --git a/tests/PHPStan/Analyser/nsrt/bug-13332.php b/tests/PHPStan/Analyser/nsrt/bug-13332.php new file mode 100644 index 00000000000..83224eb3ace --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13332.php @@ -0,0 +1,65 @@ += 8.1 + +declare(strict_types = 1); + +namespace Bug13332; + +use function PHPStan\Testing\assertType; + +enum TestEnum { + case A; + case B; +} + +/** + * @phpstan-type KeyType string|int|\UnitEnum|object + * + * @template K of KeyType + */ +class TestError +{ + /** @param K $key */ + public function __construct(private readonly mixed $key) + { + } + + /** @return self */ + public static function makeEnum(): self + { + return new self(TestEnum::A); + } + + /** @return self */ + public static function makeString(): self + { + return new self('foo'); + } +} + +/** + * @template K of string|int|\UnitEnum|object + */ +class TestOk +{ + /** @param K $key */ + public function __construct(private readonly mixed $key) + { + } + + /** @return self */ + public static function makeEnum(): self + { + return new self(TestEnum::A); + } +} + +function () { + $error = TestError::makeEnum(); + assertType('Bug13332\TestError', $error); + + $errorStr = TestError::makeString(); + assertType('Bug13332\TestError', $errorStr); + + $ok = TestOk::makeEnum(); + assertType('Bug13332\TestOk', $ok); +}; diff --git a/tests/PHPStan/Analyser/nsrt/bug-7152.php b/tests/PHPStan/Analyser/nsrt/bug-7152.php new file mode 100644 index 00000000000..f14035fc411 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-7152.php @@ -0,0 +1,41 @@ + + */ +class Root +{ + /** @var T */ + public array $value; +} + +/** + * @phpstan-type Foo array + * @template T of Foo + * @extends Root + */ +class Middle extends Root +{ +} + +/** + * @template T of array + * @extends Root + */ +class Middle2 extends Root +{ +} + +function () { + /** @var Middle> $m */ + $m = new Middle(); + assertType('array', $m->value); + + /** @var Middle2> $m2 */ + $m2 = new Middle2(); + assertType('array', $m2->value); +}; diff --git a/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php b/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php index d98f5438694..85120fe0d2f 100644 --- a/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php +++ b/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php @@ -290,4 +290,9 @@ public function testBug7021(): void $this->analyse([__DIR__ . '/data/bug-7021.php'], []); } + public function testBug7152(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-7152.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php b/tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php index 7c231876c93..2632e3334df 100644 --- a/tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php +++ b/tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php @@ -151,4 +151,20 @@ public function testBug10049(): void ]); } + public function testBug11314(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-11314.php'], []); + } + + #[RequiresPhp('>= 8.1.0')] + public function testBug13332(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-13332.php'], []); + } + + public function testBug7152(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-7152.php'], []); + } + }