Skip to content

Commit fe36e29

Browse files
committed
Even further optimize FileTypeMapper - everything is a callback at first
1 parent 3dcc305 commit fe36e29

File tree

2 files changed

+32
-57
lines changed

2 files changed

+32
-57
lines changed

src/Type/FileTypeMapper.php

Lines changed: 22 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class FileTypeMapper
4343
private const SKIP_NODE = 1;
4444
private const POP_TYPE_MAP_STACK = 2;
4545

46-
/** @var NameScope[][] */
46+
/** @var array<string, array<string, callable(): NameScope>> */
4747
private array $memoryCache = [];
4848

4949
private int $memoryCacheCount = 0;
@@ -98,24 +98,18 @@ public function getResolvedPhpDoc(
9898
return $this->createResolvedPhpDocBlock($phpDocKey, new NameScope(null, []), $docComment, null);
9999
}
100100

101-
$nameScopeMap = [];
102-
103-
if (!isset($this->inProcess[$fileName])) {
101+
if (!isset($this->inProcess[$fileName][$nameScopeKey])) {
104102
$nameScopeMap = $this->getNameScopeMap($fileName);
105-
}
106-
107-
if (isset($nameScopeMap[$nameScopeKey])) {
108-
return $this->createResolvedPhpDocBlock($phpDocKey, $nameScopeMap[$nameScopeKey], $docComment, $fileName);
109-
}
103+
if (!array_key_exists($nameScopeKey, $nameScopeMap)) {
104+
return ResolvedPhpDocBlock::createEmpty();
105+
}
110106

111-
if (!isset($this->inProcess[$fileName][$nameScopeKey])) { // wrong $fileName due to traits
112-
return ResolvedPhpDocBlock::createEmpty();
107+
$this->inProcess[$fileName][$nameScopeKey] = $nameScopeMap[$nameScopeKey];
113108
}
114109

115110
if ($this->inProcess[$fileName][$nameScopeKey] === true) { // PHPDoc has cyclic dependency
116111
return ResolvedPhpDocBlock::createEmpty();
117112
}
118-
119113
if (is_callable($this->inProcess[$fileName][$nameScopeKey])) {
120114
$resolveCallback = $this->inProcess[$fileName][$nameScopeKey];
121115
$this->inProcess[$fileName][$nameScopeKey] = true;
@@ -165,12 +159,13 @@ private function createResolvedPhpDocBlock(string $phpDocKey, NameScope $nameSco
165159
}
166160

167161
/**
168-
* @return NameScope[]
162+
* @return array<string, callable(): NameScope>
169163
*/
170164
private function getNameScopeMap(string $fileName): array
171165
{
172166
if (!isset($this->memoryCache[$fileName])) {
173-
$map = $this->createResolvedPhpDocMap($fileName);
167+
$phpDocNodeMap = $this->createPhpDocNodeMap($fileName, null, $fileName, [], $fileName);
168+
$map = $this->createNameScopeMap($fileName, null, null, [], $fileName, $phpDocNodeMap);
174169
if ($this->memoryCacheCount >= 2048) {
175170
$this->memoryCache = array_slice(
176171
$this->memoryCache,
@@ -188,31 +183,6 @@ private function getNameScopeMap(string $fileName): array
188183
return $this->memoryCache[$fileName];
189184
}
190185

191-
/**
192-
* @return NameScope[]
193-
*/
194-
private function createResolvedPhpDocMap(string $fileName): array
195-
{
196-
$phpDocNodeMap = $this->createPhpDocNodeMap($fileName, null, $fileName, [], $fileName);
197-
$nameScopeMap = $this->createNameScopeMap($fileName, null, null, [], $fileName, $phpDocNodeMap);
198-
$resolvedNameScopeMap = [];
199-
200-
try {
201-
$this->inProcess[$fileName] = $nameScopeMap;
202-
203-
foreach ($nameScopeMap as $nameScopeKey => $resolveCallback) {
204-
$this->inProcess[$fileName][$nameScopeKey] = true;
205-
$this->inProcess[$fileName][$nameScopeKey] = $data = $resolveCallback();
206-
$resolvedNameScopeMap[$nameScopeKey] = $data;
207-
}
208-
209-
} finally {
210-
unset($this->inProcess[$fileName]);
211-
}
212-
213-
return $resolvedNameScopeMap;
214-
}
215-
216186
/**
217187
* @param array<string, string> $traitMethodAliases
218188
*/
@@ -382,7 +352,7 @@ static function (Node $node) use (&$namespace, &$functionStack, &$classStack): v
382352

383353
/**
384354
* @param array<string, string> $traitMethodAliases
385-
* @return (callable(): NameScope)[]
355+
* @return array<string, callable(): NameScope>
386356
*/
387357
private function createNameScopeMap(
388358
string $fileName,
@@ -399,14 +369,14 @@ private function createNameScopeMap(
399369
/** @var (callable(): TemplateTypeMap)[] $typeMapStack */
400370
$typeMapStack = [];
401371

402-
/** @var array<int, array<string, true>> $typeAliasStack */
372+
/** @var array<int, callable(): array<string, true>> $typeAliasStack */
403373
$typeAliasStack = [];
404374

405375
/** @var string[] $classStack */
406376
$classStack = [];
407377
if ($lookForTrait !== null && $traitUseClass !== null) {
408378
$classStack[] = $traitUseClass;
409-
$typeAliasStack[] = [];
379+
$typeAliasStack[] = static fn () => [];
410380
}
411381
$namespace = null;
412382

@@ -435,9 +405,9 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
435405
$traitFound = true;
436406
$traitNameScopeKey = $this->getNameScopeKey($originalClassFileName, $classStack[count($classStack) - 1] ?? null, $lookForTrait, null);
437407
if ($phpDocNodeMap->has($traitNameScopeKey)) {
438-
$typeAliasStack[] = $this->getTypeAliasesMap($phpDocNodeMap->get($traitNameScopeKey));
408+
$typeAliasStack[] = fn () => $this->getTypeAliasesMap($phpDocNodeMap->get($traitNameScopeKey));
439409
} else {
440-
$typeAliasStack[] = [];
410+
$typeAliasStack[] = static fn () => [];
441411
}
442412
$functionStack[] = null;
443413
} else {
@@ -458,9 +428,9 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
458428
$classStack[] = $className;
459429
$classNameScopeKey = $this->getNameScopeKey($originalClassFileName, $className, $lookForTrait, null);
460430
if ($phpDocNodeMap->has($classNameScopeKey)) {
461-
$typeAliasStack[] = $this->getTypeAliasesMap($phpDocNodeMap->get($classNameScopeKey));
431+
$typeAliasStack[] = fn () => $this->getTypeAliasesMap($phpDocNodeMap->get($classNameScopeKey));
462432
} else {
463-
$typeAliasStack[] = [];
433+
$typeAliasStack[] = static fn () => [];
464434
}
465435
$functionStack[] = null;
466436
}
@@ -480,12 +450,13 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
480450

481451
if ($node instanceof Node\Stmt\ClassLike || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Stmt\Function_) {
482452
if ($phpDocNodeMap->has($nameScopeKey)) {
483-
$phpDocNode = $phpDocNodeMap->get($nameScopeKey);
484-
$typeMapStack[] = function () use ($namespace, $uses, $className, $lookForTrait, $functionName, $phpDocNode, $typeMapStack, $typeAliasStack, $constUses): TemplateTypeMap {
453+
$typeMapStack[] = function () use ($namespace, $uses, $className, $lookForTrait, $functionName, $phpDocNodeMap, $nameScopeKey, $typeMapStack, $typeAliasStack, $constUses): TemplateTypeMap {
485454
$typeMapCb = $typeMapStack[count($typeMapStack) - 1] ?? null;
486455
$currentTypeMap = $typeMapCb !== null ? $typeMapCb() : null;
487-
$typeAliasesMap = $typeAliasStack[count($typeAliasStack) - 1] ?? [];
456+
$typeAliasesMapCb = $typeAliasStack[count($typeAliasStack) - 1] ?? null;
457+
$typeAliasesMap = $typeAliasesMapCb !== null ? $typeAliasesMapCb() : [];
488458
$nameScope = new NameScope($namespace, $uses, $className, $functionName, $currentTypeMap, $typeAliasesMap, false, $constUses, $lookForTrait);
459+
$phpDocNode = $phpDocNodeMap->get($nameScopeKey);
489460
$templateTags = $this->phpDocNodeResolver->resolveTemplateTags($phpDocNode, $nameScope);
490461
$templateTypeScope = $nameScope->getTemplateTypeScope();
491462
if ($templateTypeScope === null) {
@@ -505,7 +476,7 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
505476
}
506477

507478
$typeMapCb = $typeMapStack[count($typeMapStack) - 1] ?? null;
508-
$typeAliasesMap = $typeAliasStack[count($typeAliasStack) - 1] ?? [];
479+
$typeAliasesMapCb = $typeAliasStack[count($typeAliasStack) - 1] ?? null;
509480

510481
if (
511482
$node instanceof Node\Stmt
@@ -527,7 +498,7 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
527498
$className,
528499
$functionName,
529500
($typeMapCb !== null ? $typeMapCb() : TemplateTypeMap::createEmpty()),
530-
$typeAliasesMap,
501+
($typeAliasesMapCb !== null ? $typeAliasesMapCb() : []),
531502
false,
532503
$constUses,
533504
$lookForTrait,

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,13 +1025,17 @@ public function testBug3865(): void
10251025
public function testBug5312(): void
10261026
{
10271027
$errors = $this->runAnalyse(__DIR__ . '/data/bug-5312.php');
1028-
$this->assertCount(3, $errors);
1029-
$this->assertSame('Parameter $object of method Bug5312\Updatable::update() has invalid type Bug5312\T.', $errors[0]->getMessage());
1030-
$this->assertSame(13, $errors[0]->getLine());
1031-
$this->assertSame('Type Bug5312\T in generic type Bug5312\Updatable<Bug5312\T> in PHPDoc tag @param for parameter $object is not subtype of template type T of Bug5312\Updatable<Bug5312\T> of interface Bug5312\Updatable.', $errors[1]->getMessage());
1032-
$this->assertSame(13, $errors[1]->getLine());
1033-
$this->assertSame('Type Bug5312\T in generic type Bug5312\Updatable<Bug5312\T> in PHPDoc tag @param for parameter $object is not subtype of template type T of Bug5312\Updatable<Bug5312\T> of interface Bug5312\Updatable.', $errors[2]->getMessage());
1028+
$this->assertCount(5, $errors);
1029+
$this->assertSame('PHPDoc tag @template T for interface Bug5312\Updatable has invalid bound type Bug5312\T.', $errors[0]->getMessage());
1030+
$this->assertSame(8, $errors[0]->getLine());
1031+
$this->assertSame('Type Bug5312\T in generic type Bug5312\Updatable<Bug5312\T> in PHPDoc tag @template T is not subtype of template type T of Bug5312\Updatable<Bug5312\T> of interface Bug5312\Updatable.', $errors[1]->getMessage());
1032+
$this->assertSame(8, $errors[1]->getLine());
1033+
$this->assertSame('Parameter $object of method Bug5312\Updatable::update() has invalid type Bug5312\T.', $errors[2]->getMessage());
10341034
$this->assertSame(13, $errors[2]->getLine());
1035+
$this->assertSame('Type Bug5312\T in generic type Bug5312\Updatable<Bug5312\T> in PHPDoc tag @param for parameter $object is not subtype of template type T of Bug5312\Updatable<Bug5312\T> of interface Bug5312\Updatable.', $errors[3]->getMessage());
1036+
$this->assertSame(13, $errors[3]->getLine());
1037+
$this->assertSame('Type Bug5312\T in generic type Bug5312\Updatable<Bug5312\T> in PHPDoc tag @param for parameter $object is not subtype of template type T of Bug5312\Updatable<Bug5312\T> of interface Bug5312\Updatable.', $errors[4]->getMessage());
1038+
$this->assertSame(13, $errors[4]->getLine());
10351039
}
10361040

10371041
public function testBug5390(): void

0 commit comments

Comments
 (0)