Skip to content

Commit 77d9a41

Browse files
github-actions[bot]VincentLanglet
authored andcommitted
Fix phpstan/phpstan#11314: Template of imported type breaks imported type
- When a class has both @phpstan-import-type and @template with the imported type as bound, resolving the template bound triggered a cyclic dependency in FileTypeMapper - The cycle occurred because resolving the type alias went through ClassReflection::getTypeAliases() which needed the full resolved PHPDoc, but the PHPDoc was still being built - Fix: store a partial NameScope (with type aliases but before template resolution) and return it on cycle detection instead of throwing NameScopeAlreadyBeingCreatedException - New regression test in tests/PHPStan/Analyser/nsrt/bug-11314.php
1 parent a8e2d31 commit 77d9a41

2 files changed

Lines changed: 60 additions & 0 deletions

File tree

src/Type/FileTypeMapper.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ final class FileTypeMapper
6666
/** @var array<string, true> */
6767
private array $inProcess = [];
6868

69+
/** @var array<string, NameScope> */
70+
private array $inProcessNameScopes = [];
71+
6972
/** @var array<string, ResolvedPhpDocBlock> */
7073
private array $resolvedPhpDocBlockCache = [];
7174

@@ -200,6 +203,9 @@ public function getNameScope(
200203
{
201204
$nameScopeKey = $this->getNameScopeKey($fileName, $className, $traitName, $functionName);
202205
if (isset($this->inProcess[$nameScopeKey])) {
206+
if (isset($this->inProcessNameScopes[$nameScopeKey])) {
207+
return $this->inProcessNameScopes[$nameScopeKey];
208+
}
203209
throw new NameScopeAlreadyBeingCreatedException();
204210
}
205211

@@ -288,6 +294,8 @@ public function getNameScope(
288294
continue;
289295
}
290296

297+
$this->inProcessNameScopes[$nameScopeKey] = $nameScope;
298+
291299
$templateTags = $this->phpDocNodeResolver->resolveTemplateTags($parent->getTemplatePhpDocNodes(), $nameScope);
292300
$templateTypeMap = new TemplateTypeMap(array_map(static fn (TemplateTag $tag): Type => TemplateTypeFactory::fromTemplateTag($templateTypeScope, $tag), $templateTags));
293301
$nameScope = $nameScope->withTemplateTypeMap($templateTypeMap, $templateTags);
@@ -319,6 +327,7 @@ public function getNameScope(
319327
);
320328
} finally {
321329
unset($this->inProcess[$nameScopeKey]);
330+
unset($this->inProcessNameScopes[$nameScopeKey]);
322331
}
323332
}
324333

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11314;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @phpstan-type Breed 'Siamese'|'British Shorthair'|'Maine Coon'
9+
*/
10+
class Cat
11+
{
12+
/**
13+
* @var Breed
14+
*/
15+
public string $breed;
16+
}
17+
18+
/**
19+
* @phpstan-import-type Breed from Cat
20+
*
21+
* @template T of Breed
22+
*/
23+
class Cat2
24+
{
25+
/**
26+
* @var Breed
27+
*/
28+
public string $breed;
29+
}
30+
31+
/**
32+
* @phpstan-import-type Breed from Cat
33+
*/
34+
class Cat3
35+
{
36+
/**
37+
* @var Breed
38+
*/
39+
public string $breed;
40+
}
41+
42+
function () {
43+
$cat = new Cat();
44+
assertType("'British Shorthair'|'Maine Coon'|'Siamese'", $cat->breed);
45+
46+
$cat2 = new Cat2();
47+
assertType("'British Shorthair'|'Maine Coon'|'Siamese'", $cat2->breed);
48+
49+
$cat3 = new Cat3();
50+
assertType("'British Shorthair'|'Maine Coon'|'Siamese'", $cat3->breed);
51+
};

0 commit comments

Comments
 (0)