Skip to content

Commit f9a2648

Browse files
Implement template default types
Co-authored-by: Richard van Velzen <[email protected]> Co-authored-by: Richard van Velzen <[email protected]>
1 parent c79b69a commit f9a2648

File tree

83 files changed

+861
-67
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+861
-67
lines changed

src/Analyser/MutatingScope.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,6 +2539,7 @@ private function createFirstClassCallable(
25392539
$templateTags[$templateType->getName()] = new TemplateTag(
25402540
$templateType->getName(),
25412541
$templateType->getBound(),
2542+
$templateType->getDefault(),
25422543
$templateType->getVariance(),
25432544
);
25442545
}
@@ -5606,6 +5607,11 @@ private function exactInstantiation(New_ $node, string $className): ?Type
56065607
$list[] = $templateType;
56075608
continue;
56085609
}
5610+
$default = $tag->getDefault();
5611+
if ($default !== null) {
5612+
$list[] = $default;
5613+
continue;
5614+
}
56095615
$bound = $tag->getBound();
56105616
if ($bound instanceof MixedType && $bound->isExplicitMixed()) {
56115617
$bound = new MixedType(false);

src/Dependency/DependencyResolver.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,17 @@ private function addClassToDependencies(string $className, array &$dependenciesR
531531
}
532532
$dependenciesReflections[] = $this->reflectionProvider->getClass($referencedClass);
533533
}
534+
535+
$default = $templateTag->getDefault();
536+
if ($default === null) {
537+
continue;
538+
}
539+
foreach ($default->getReferencedClasses() as $referencedClass) {
540+
if (!$this->reflectionProvider->hasClass($referencedClass)) {
541+
continue;
542+
}
543+
$dependenciesReflections[] = $this->reflectionProvider->getClass($referencedClass);
544+
}
534545
}
535546

536547
foreach ($classReflection->getPropertyTags() as $propertyTag) {

src/PhpDoc/PhpDocNodeResolver.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ public function resolveMethodTags(PhpDocNode $phpDocNode, NameScope $nameScope):
176176
$templateType->bound !== null
177177
? $this->typeNodeResolver->resolve($templateType->bound, $nameScope)
178178
: new MixedType(),
179+
$templateType->default !== null
180+
? $this->typeNodeResolver->resolve($templateType->default, $nameScope)
181+
: null,
179182
TemplateTypeVariance::createInvariant(),
180183
);
181184
}
@@ -327,9 +330,12 @@ public function resolveTemplateTags(PhpDocNode $phpDocNode, NameScope $nameScope
327330
}
328331
}
329332

333+
$nameScopeWithoutCurrent = $nameScope->unsetTemplateType($valueNode->name);
334+
330335
$resolved[$valueNode->name] = new TemplateTag(
331336
$valueNode->name,
332-
$valueNode->bound !== null ? $this->typeNodeResolver->resolve($valueNode->bound, $nameScope->unsetTemplateType($valueNode->name)) : new MixedType(true),
337+
$valueNode->bound !== null ? $this->typeNodeResolver->resolve($valueNode->bound, $nameScopeWithoutCurrent) : new MixedType(true),
338+
$valueNode->default !== null ? $this->typeNodeResolver->resolve($valueNode->default, $nameScopeWithoutCurrent) : null,
333339
$variance,
334340
);
335341
$resolvedPrefix[$valueNode->name] = $prefix;

src/PhpDoc/Tag/TemplateTag.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class TemplateTag
1515
/**
1616
* @param non-empty-string $name
1717
*/
18-
public function __construct(private string $name, private Type $bound, private TemplateTypeVariance $variance)
18+
public function __construct(private string $name, private Type $bound, private ?Type $default, private TemplateTypeVariance $variance)
1919
{
2020
}
2121

@@ -32,6 +32,11 @@ public function getBound(): Type
3232
return $this->bound;
3333
}
3434

35+
public function getDefault(): ?Type
36+
{
37+
return $this->default;
38+
}
39+
3540
public function getVariance(): TemplateTypeVariance
3641
{
3742
return $this->variance;

src/PhpDoc/TypeNodeResolver.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
use Traversable;
107107
use function array_key_exists;
108108
use function array_map;
109+
use function array_values;
109110
use function count;
110111
use function explode;
111112
use function get_class;
@@ -792,6 +793,15 @@ static function (string $variance): TemplateTypeVariance {
792793

793794
$classReflection = $this->getReflectionProvider()->getClass($mainTypeClassName);
794795
if ($classReflection->isGeneric()) {
796+
$templateTypes = array_values($classReflection->getTemplateTypeMap()->getTypes());
797+
for ($i = count($genericTypes), $templateTypesCount = count($templateTypes); $i < $templateTypesCount; $i++) {
798+
$templateType = $templateTypes[$i];
799+
if (!$templateType instanceof TemplateType || $templateType->getDefault() === null) {
800+
continue;
801+
}
802+
$genericTypes[] = $templateType->getDefault();
803+
}
804+
795805
if (in_array($mainTypeClassName, [
796806
Traversable::class,
797807
IteratorAggregate::class,
@@ -910,6 +920,9 @@ private function resolveCallableTypeNode(CallableTypeNode $typeNode, NameScope $
910920
$templateType->bound !== null
911921
? $this->resolve($templateType->bound, $nameScope)
912922
: new MixedType(),
923+
$templateType->default !== null
924+
? $this->resolve($templateType->default, $nameScope)
925+
: null,
913926
TemplateTypeVariance::createInvariant(),
914927
);
915928
}

src/Reflection/ClassReflection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,7 +1442,7 @@ public function typeMapFromList(array $types): TemplateTypeMap
14421442
$map = [];
14431443
$i = 0;
14441444
foreach ($resolvedPhpDoc->getTemplateTags() as $tag) {
1445-
$map[$tag->getName()] = $types[$i] ?? $tag->getBound();
1445+
$map[$tag->getName()] = $types[$i] ?? $tag->getDefault() ?? $tag->getBound();
14461446
$i++;
14471447
}
14481448

@@ -1479,7 +1479,7 @@ public function typeMapToList(TemplateTypeMap $typeMap): array
14791479

14801480
$list = [];
14811481
foreach ($resolvedPhpDoc->getTemplateTags() as $tag) {
1482-
$list[] = $typeMap->getType($tag->getName()) ?? $tag->getBound();
1482+
$list[] = $typeMap->getType($tag->getName()) ?? $tag->getDefault() ?? $tag->getBound();
14831483
}
14841484

14851485
return $list;

src/Rules/Classes/LocalTypeAliasesCheck.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use PHPStan\Type\VerbosityLevel;
2525
use function array_key_exists;
2626
use function array_merge;
27-
use function implode;
2827
use function in_array;
2928
use function sprintf;
3029

@@ -211,7 +210,7 @@ public function checkInTraitDefinitionContext(ClassReflection $reflection): arra
211210
$reflection->getDisplayName(),
212211
$aliasName,
213212
$name,
214-
implode(', ', $genericTypeNames),
213+
$genericTypeNames,
215214
))
216215
->identifier('missingType.generics')
217216
->build();

src/Rules/Classes/MethodTagCheck.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PHPStan\Type\Type;
1717
use PHPStan\Type\VerbosityLevel;
1818
use function array_merge;
19-
use function implode;
2019
use function sprintf;
2120

2221
final class MethodTagCheck
@@ -174,7 +173,7 @@ private function checkMethodTypeInTraitDefinitionContext(ClassReflection $classR
174173
$methodName,
175174
$description,
176175
$innerName,
177-
implode(', ', $genericTypeNames),
176+
$genericTypeNames,
178177
))
179178
->identifier('missingType.generics')
180179
->build();

src/Rules/Classes/MixinCheck.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use PHPStan\Rules\RuleErrorBuilder;
1515
use PHPStan\Type\VerbosityLevel;
1616
use function array_merge;
17-
use function implode;
1817
use function sprintf;
1918

2019
final class MixinCheck
@@ -90,7 +89,7 @@ public function checkInTraitDefinitionContext(ClassReflection $classReflection):
9089
$errors[] = RuleErrorBuilder::message(sprintf(
9190
'PHPDoc tag @mixin contains generic %s but does not specify its types: %s',
9291
$innerName,
93-
implode(', ', $genericTypeNames),
92+
$genericTypeNames,
9493
))
9594
->identifier('missingType.generics')
9695
->build();

src/Rules/Classes/PropertyTagCheck.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use PHPStan\Type\Type;
1919
use PHPStan\Type\VerbosityLevel;
2020
use function array_merge;
21-
use function implode;
2221
use function sprintf;
2322

2423
final class PropertyTagCheck
@@ -155,7 +154,7 @@ private function checkPropertyTypeInTraitDefinitionContext(ClassReflection $clas
155154
$classReflection->getDisplayName(),
156155
$propertyName,
157156
$innerName,
158-
implode(', ', $genericTypeNames),
157+
$genericTypeNames,
159158
))
160159
->identifier('missingType.generics')
161160
->build();

0 commit comments

Comments
 (0)