Skip to content

Commit d8ec11f

Browse files
committed
FileTypeMapper - output of createPhpDocNodeMap is cacheable
1 parent 6373c84 commit d8ec11f

7 files changed

Lines changed: 474 additions & 198 deletions

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser;
4+
5+
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
6+
7+
final class IntermediaryNameScope
8+
{
9+
10+
/**
11+
* @api
12+
* @param non-empty-string|null $namespace
13+
* @param array<string, string> $uses alias(string) => fullName(string)
14+
* @param array<string, array{string, TemplateTagValueNode}> $templatePhpDocNodes
15+
* @param array<string, string> $constUses alias(string) => fullName(string)
16+
* @param array<string, true> $typeAliasesMap
17+
* @param array{string, string, string, string|null, string|null}|null $traitData
18+
*/
19+
public function __construct(
20+
private ?string $namespace,
21+
private array $uses,
22+
private ?string $className = null,
23+
private ?string $functionName = null,
24+
private array $templatePhpDocNodes = [],
25+
private ?self $parent = null,
26+
private array $typeAliasesMap = [],
27+
private bool $bypassTypeAliases = false,
28+
private array $constUses = [],
29+
private ?string $typeAliasClassName = null,
30+
private ?array $traitData = null,
31+
)
32+
{
33+
}
34+
35+
/**
36+
* @return non-empty-string|null
37+
*/
38+
public function getNamespace(): ?string
39+
{
40+
return $this->namespace;
41+
}
42+
43+
/**
44+
* @return array<string, string>
45+
*/
46+
public function getUses(): array
47+
{
48+
return $this->uses;
49+
}
50+
51+
/**
52+
* @return array<string, string>
53+
*/
54+
public function getConstUses(): array
55+
{
56+
return $this->constUses;
57+
}
58+
59+
public function getClassName(): ?string
60+
{
61+
return $this->className;
62+
}
63+
64+
public function getFunctionName(): ?string
65+
{
66+
return $this->functionName;
67+
}
68+
69+
/**
70+
* @return array<string, array{string, TemplateTagValueNode}>
71+
*/
72+
public function getTemplatePhpDocNodes(): array
73+
{
74+
return $this->templatePhpDocNodes;
75+
}
76+
77+
public function withTraitData(string $fileName, string $className, string $traitName, ?string $lookForTraitName, ?string $docComment): self
78+
{
79+
return new self(
80+
$this->namespace,
81+
$this->uses,
82+
$this->className,
83+
$this->functionName,
84+
$this->templatePhpDocNodes,
85+
$this->parent,
86+
$this->typeAliasesMap,
87+
$this->bypassTypeAliases,
88+
$this->constUses,
89+
$this->typeAliasClassName,
90+
[$fileName, $className, $traitName, $lookForTraitName, $docComment],
91+
);
92+
}
93+
94+
/**
95+
* @param string[] $namesToUnset
96+
*/
97+
public function unsetTemplatePhpDocNodes(array $namesToUnset): self
98+
{
99+
$templatePhpDocNodes = $this->templatePhpDocNodes;
100+
foreach ($namesToUnset as $name) {
101+
unset($templatePhpDocNodes[$name]);
102+
}
103+
return new self(
104+
$this->namespace,
105+
$this->uses,
106+
$this->className,
107+
$this->functionName,
108+
$templatePhpDocNodes,
109+
$this->parent,
110+
$this->typeAliasesMap,
111+
$this->bypassTypeAliases,
112+
$this->constUses,
113+
$this->typeAliasClassName,
114+
$this->traitData,
115+
);
116+
}
117+
118+
/**
119+
* @return array{string, string, string, string|null, string|null}|null
120+
*/
121+
public function getTraitData(): ?array
122+
{
123+
return $this->traitData;
124+
}
125+
126+
public function getParent(): ?self
127+
{
128+
return $this->parent;
129+
}
130+
131+
/**
132+
* @return array<string, true>
133+
*/
134+
public function getTypeAliasesMap(): array
135+
{
136+
return $this->typeAliasesMap;
137+
}
138+
139+
public function shouldBypassTypeAliases(): bool
140+
{
141+
return $this->bypassTypeAliases;
142+
}
143+
144+
public function getClassNameForTypeAlias(): ?string
145+
{
146+
return $this->typeAliasClassName;
147+
}
148+
149+
}

src/Analyser/NameScope.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Analyser;
44

5+
use PHPStan\PhpDoc\Tag\TemplateTag;
56
use PHPStan\Type\Generic\TemplateTypeMap;
67
use PHPStan\Type\Generic\TemplateTypeScope;
78
use PHPStan\Type\Type;
@@ -29,6 +30,7 @@ final class NameScope
2930
* @param non-empty-string|null $namespace
3031
* @param array<string, string> $uses alias(string) => fullName(string)
3132
* @param array<string, string> $constUses alias(string) => fullName(string)
33+
* @param array<string, TemplateTag> $templateTags
3234
* @param array<string, true> $typeAliasesMap
3335
*/
3436
public function __construct(
@@ -37,6 +39,7 @@ public function __construct(
3739
private ?string $className = null,
3840
private ?string $functionName = null,
3941
?TemplateTypeMap $templateTypeMap = null,
42+
private array $templateTags = [],
4043
private array $typeAliasesMap = [],
4144
private bool $bypassTypeAliases = false,
4245
private array $constUses = [],
@@ -158,12 +161,23 @@ public function getTemplateTypeMap(): TemplateTypeMap
158161
return $this->templateTypeMap;
159162
}
160163

164+
/**
165+
* @return array<string, TemplateTag>
166+
*/
167+
public function getTemplateTags(): array
168+
{
169+
return $this->templateTags;
170+
}
171+
161172
public function resolveTemplateTypeName(string $name): ?Type
162173
{
163174
return $this->templateTypeMap->getType($name);
164175
}
165176

166-
public function withTemplateTypeMap(TemplateTypeMap $map): self
177+
/**
178+
* @param array<string, TemplateTag> $templateTags
179+
*/
180+
public function withTemplateTypeMap(TemplateTypeMap $map, array $templateTags): self
167181
{
168182
if ($map->isEmpty() && $this->templateTypeMap->isEmpty()) {
169183
return $this;
@@ -178,6 +192,7 @@ public function withTemplateTypeMap(TemplateTypeMap $map): self
178192
$this->templateTypeMap->getTypes(),
179193
$map->getTypes(),
180194
)),
195+
$templateTags,
181196
$this->typeAliasesMap,
182197
$this->bypassTypeAliases,
183198
$this->constUses,
@@ -192,6 +207,7 @@ public function withoutNamespaceAndUses(): self
192207
$this->className,
193208
$this->functionName,
194209
$this->templateTypeMap,
210+
$this->templateTags,
195211
$this->typeAliasesMap,
196212
$this->bypassTypeAliases,
197213
$this->constUses,
@@ -206,6 +222,7 @@ public function withClassName(string $className): self
206222
$className,
207223
$this->functionName,
208224
$this->templateTypeMap,
225+
$this->templateTags,
209226
$this->typeAliasesMap,
210227
$this->bypassTypeAliases,
211228
$this->constUses,
@@ -225,6 +242,7 @@ public function unsetTemplateType(string $name): self
225242
$this->className,
226243
$this->functionName,
227244
$this->templateTypeMap->unsetType($name),
245+
$this->templateTags,
228246
$this->typeAliasesMap,
229247
$this->bypassTypeAliases,
230248
$this->constUses,
@@ -233,7 +251,7 @@ public function unsetTemplateType(string $name): self
233251

234252
public function bypassTypeAliases(): self
235253
{
236-
return new self($this->namespace, $this->uses, $this->className, $this->functionName, $this->templateTypeMap, $this->typeAliasesMap, true, $this->constUses);
254+
return new self($this->namespace, $this->uses, $this->className, $this->functionName, $this->templateTypeMap, $this->templateTags, $this->typeAliasesMap, true, $this->constUses);
237255
}
238256

239257
public function shouldBypassTypeAliases(): bool

src/PhpDoc/PhpDocNodeResolver.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public function resolveMethodTags(PhpDocNode $phpDocNode, NameScope $nameScope):
185185

186186
$templateTypeScope = TemplateTypeScope::createWithMethod($nameScope->getClassName(), $tagValue->methodName);
187187
$templateTypeMap = new TemplateTypeMap(array_map(static fn (TemplateTag $tag): Type => TemplateTypeFactory::fromTemplateTag($templateTypeScope, $tag), $templateTags));
188-
$nameScope = $nameScope->withTemplateTypeMap($templateTypeMap);
188+
$nameScope = $nameScope->withTemplateTypeMap($templateTypeMap, $templateTags);
189189
}
190190

191191
$parameters = [];
@@ -282,9 +282,10 @@ public function resolveUsesTags(PhpDocNode $phpDocNode, NameScope $nameScope): a
282282
}
283283

284284
/**
285+
* @param array<string, array{string, TemplateTagValueNode}> $templatePhpDocNodes
285286
* @return array<string, TemplateTag>
286287
*/
287-
public function resolveTemplateTags(PhpDocNode $phpDocNode, NameScope $nameScope): array
288+
public function resolveTemplateTags(array $templatePhpDocNodes, NameScope $nameScope): array
288289
{
289290
$resolved = [];
290291
$resolvedPrefix = [];
@@ -296,13 +297,7 @@ public function resolveTemplateTags(PhpDocNode $phpDocNode, NameScope $nameScope
296297
'phpstan' => 3,
297298
];
298299

299-
foreach ($phpDocNode->getTags() as $phpDocTagNode) {
300-
$valueNode = $phpDocTagNode->value;
301-
if (!$valueNode instanceof TemplateTagValueNode) {
302-
continue;
303-
}
304-
305-
$tagName = $phpDocTagNode->name;
300+
foreach ($templatePhpDocNodes as [$tagName, $valueNode]) {
306301
if (in_array($tagName, ['@template', '@phan-template', '@psalm-template', '@phpstan-template'], true)) {
307302
$variance = TemplateTypeVariance::createInvariant();
308303
} elseif (in_array($tagName, ['@template-covariant', '@psalm-template-covariant', '@phpstan-template-covariant'], true)) {

src/PhpDoc/TypeNodeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ private function resolveCallableTypeNode(CallableTypeNode $typeNode, NameScope $
982982
$templateTags,
983983
));
984984

985-
$nameScope = $nameScope->withTemplateTypeMap($templateTypeMap);
985+
$nameScope = $nameScope->withTemplateTypeMap($templateTypeMap, $templateTags);
986986
} else {
987987
$templateTypeMap = TemplateTypeMap::createEmpty();
988988
}

0 commit comments

Comments
 (0)