Skip to content

Commit e68ca10

Browse files
committed
Fixed class-scoped template type used as a bound in method-scoped template type
1 parent bce31f0 commit e68ca10

15 files changed

+302
-77
lines changed

src/Type/FileTypeMapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ private function shouldPhpDocNodeBeCachedToDisk(PhpDocNode $phpDocNode): bool
214214
private function getResolvedPhpDocMap(string $fileName): array
215215
{
216216
if (!isset($this->memoryCache[$fileName])) {
217-
$cacheKey = sprintf('%s-phpdocstring-v5-namespace', $fileName);
217+
$cacheKey = sprintf('%s-phpdocstring-v6-generic-bound', $fileName);
218218
$variableCacheKey = implode(',', array_map(static function (array $file): string {
219219
return sprintf('%s-%d', $file['filename'], $file['modifiedTime']);
220220
}, $this->getCachedDependentFilesWithTimestamps($fileName)));

src/Type/Generic/TemplateBenevolentUnionType.php

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,29 @@
55
use PHPStan\Type\BenevolentUnionType;
66
use PHPStan\Type\Type;
77

8+
/**
9+
* @method BenevolentUnionType getBound()
10+
*/
811
final class TemplateBenevolentUnionType extends BenevolentUnionType implements TemplateType
912
{
1013

1114
use TemplateTypeTrait;
1215

13-
/**
14-
* @param Type[] $types
15-
*/
1616
public function __construct(
1717
TemplateTypeScope $scope,
1818
TemplateTypeStrategy $templateTypeStrategy,
1919
TemplateTypeVariance $templateTypeVariance,
20-
array $types,
21-
string $name
20+
string $name,
21+
BenevolentUnionType $bound
2222
)
2323
{
24-
parent::__construct($types);
24+
parent::__construct($bound->getTypes());
2525

2626
$this->scope = $scope;
2727
$this->strategy = $templateTypeStrategy;
2828
$this->variance = $templateTypeVariance;
2929
$this->name = $name;
30-
$this->bound = new BenevolentUnionType($types);
30+
$this->bound = $bound;
3131
}
3232

3333
public function toArgument(): TemplateType
@@ -36,11 +36,27 @@ public function toArgument(): TemplateType
3636
$this->scope,
3737
new TemplateTypeArgumentStrategy(),
3838
$this->variance,
39-
$this->getTypes(),
40-
$this->name
39+
$this->name,
40+
TemplateTypeHelper::toArgument($this->getBound())
4141
);
4242
}
4343

44+
public function traverse(callable $cb): Type
45+
{
46+
$newBound = $cb($this->getBound());
47+
if ($this->getBound() !== $newBound && $newBound instanceof BenevolentUnionType) {
48+
return new self(
49+
$this->scope,
50+
$this->strategy,
51+
$this->variance,
52+
$this->name,
53+
$newBound
54+
);
55+
}
56+
57+
return $this;
58+
}
59+
4460
/**
4561
* @param mixed[] $properties
4662
* @return Type
@@ -51,8 +67,8 @@ public static function __set_state(array $properties): Type
5167
$properties['scope'],
5268
$properties['strategy'],
5369
$properties['variance'],
54-
$properties['types'],
55-
$properties['name']
70+
$properties['name'],
71+
$properties['bound']
5672
);
5773
}
5874

src/Type/Generic/TemplateGenericObjectType.php

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,30 @@
55
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
66
use PHPStan\Type\Type;
77

8+
/**
9+
* @method GenericObjectType getBound()
10+
*/
811
final class TemplateGenericObjectType extends GenericObjectType implements TemplateType
912
{
1013

1114
use UndecidedComparisonCompoundTypeTrait;
1215
use TemplateTypeTrait;
1316

14-
/**
15-
* @param Type[] $types
16-
*/
1717
public function __construct(
1818
TemplateTypeScope $scope,
1919
TemplateTypeStrategy $templateTypeStrategy,
2020
TemplateTypeVariance $templateTypeVariance,
2121
string $name,
22-
string $mainType,
23-
array $types
22+
GenericObjectType $bound
2423
)
2524
{
26-
parent::__construct($mainType, $types);
25+
parent::__construct($bound->getClassName(), $bound->getTypes());
2726

2827
$this->scope = $scope;
2928
$this->strategy = $templateTypeStrategy;
3029
$this->variance = $templateTypeVariance;
3130
$this->name = $name;
32-
$this->bound = new GenericObjectType($mainType, $types);
31+
$this->bound = $bound;
3332
}
3433

3534
public function toArgument(): TemplateType
@@ -39,20 +38,34 @@ public function toArgument(): TemplateType
3938
new TemplateTypeArgumentStrategy(),
4039
$this->variance,
4140
$this->name,
42-
$this->getClassName(),
43-
$this->getTypes()
41+
TemplateTypeHelper::toArgument($this->getBound())
4442
);
4543
}
4644

45+
public function traverse(callable $cb): Type
46+
{
47+
$newBound = $cb($this->getBound());
48+
if ($this->getBound() !== $newBound && $newBound instanceof GenericObjectType) {
49+
return new self(
50+
$this->scope,
51+
$this->strategy,
52+
$this->variance,
53+
$this->name,
54+
$newBound
55+
);
56+
}
57+
58+
return $this;
59+
}
60+
4761
protected function recreate(string $className, array $types, ?Type $subtractedType): GenericObjectType
4862
{
4963
return new self(
5064
$this->scope,
5165
$this->strategy,
5266
$this->variance,
5367
$this->name,
54-
$className,
55-
$types
68+
$this->getBound()
5669
);
5770
}
5871

@@ -67,8 +80,7 @@ public static function __set_state(array $properties): Type
6780
$properties['strategy'],
6881
$properties['variance'],
6982
$properties['name'],
70-
$properties['className'],
71-
$properties['types']
83+
$properties['bound']
7284
);
7385
}
7486

src/Type/Generic/TemplateIntegerType.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
77
use PHPStan\Type\Type;
88

9+
/**
10+
* @method IntegerType getBound()
11+
*/
912
final class TemplateIntegerType extends IntegerType implements TemplateType
1013
{
1114

@@ -16,14 +19,15 @@ public function __construct(
1619
TemplateTypeScope $scope,
1720
TemplateTypeStrategy $templateTypeStrategy,
1821
TemplateTypeVariance $templateTypeVariance,
19-
string $name
22+
string $name,
23+
IntegerType $bound
2024
)
2125
{
2226
$this->scope = $scope;
2327
$this->strategy = $templateTypeStrategy;
2428
$this->variance = $templateTypeVariance;
2529
$this->name = $name;
26-
$this->bound = new IntegerType();
30+
$this->bound = $bound;
2731
}
2832

2933
public function toArgument(): TemplateType
@@ -32,10 +36,27 @@ public function toArgument(): TemplateType
3236
$this->scope,
3337
new TemplateTypeArgumentStrategy(),
3438
$this->variance,
35-
$this->name
39+
$this->name,
40+
TemplateTypeHelper::toArgument($this->getBound())
3641
);
3742
}
3843

44+
public function traverse(callable $cb): Type
45+
{
46+
$newBound = $cb($this->getBound());
47+
if ($this->getBound() !== $newBound && $newBound instanceof IntegerType) {
48+
return new self(
49+
$this->scope,
50+
$this->strategy,
51+
$this->variance,
52+
$this->name,
53+
$newBound
54+
);
55+
}
56+
57+
return $this;
58+
}
59+
3960
protected function shouldGeneralizeInferredType(): bool
4061
{
4162
return false;
@@ -51,7 +72,8 @@ public static function __set_state(array $properties): Type
5172
$properties['scope'],
5273
$properties['strategy'],
5374
$properties['variance'],
54-
$properties['name']
75+
$properties['name'],
76+
$properties['bound']
5577
);
5678
}
5779

src/Type/Generic/TemplateMixedType.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use PHPStan\Type\MixedType;
77
use PHPStan\Type\Type;
88

9+
/**
10+
* @method MixedType getBound()
11+
*/
912
final class TemplateMixedType extends MixedType implements TemplateType
1013
{
1114

@@ -15,7 +18,8 @@ public function __construct(
1518
TemplateTypeScope $scope,
1619
TemplateTypeStrategy $templateTypeStrategy,
1720
TemplateTypeVariance $templateTypeVariance,
18-
string $name
21+
string $name,
22+
MixedType $bound
1923
)
2024
{
2125
parent::__construct(true);
@@ -24,7 +28,7 @@ public function __construct(
2428
$this->strategy = $templateTypeStrategy;
2529
$this->variance = $templateTypeVariance;
2630
$this->name = $name;
27-
$this->bound = new MixedType(true);
31+
$this->bound = $bound;
2832
}
2933

3034
public function isSuperTypeOfMixed(MixedType $type): TrinaryLogic
@@ -47,10 +51,27 @@ public function toArgument(): TemplateType
4751
$this->scope,
4852
new TemplateTypeArgumentStrategy(),
4953
$this->variance,
50-
$this->name
54+
$this->name,
55+
TemplateTypeHelper::toArgument($this->getBound())
5156
);
5257
}
5358

59+
public function traverse(callable $cb): Type
60+
{
61+
$newBound = $cb($this->getBound());
62+
if ($this->getBound() !== $newBound && $newBound instanceof MixedType) {
63+
return new self(
64+
$this->scope,
65+
$this->strategy,
66+
$this->variance,
67+
$this->name,
68+
$newBound
69+
);
70+
}
71+
72+
return $this;
73+
}
74+
5475
/**
5576
* @param mixed[] $properties
5677
* @return Type
@@ -61,7 +82,8 @@ public static function __set_state(array $properties): Type
6182
$properties['scope'],
6283
$properties['strategy'],
6384
$properties['variance'],
64-
$properties['name']
85+
$properties['name'],
86+
$properties['bound']
6587
);
6688
}
6789

src/Type/Generic/TemplateObjectType.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
77
use PHPStan\Type\Type;
88

9+
/**
10+
* @method ObjectType getBound()
11+
*/
912
final class TemplateObjectType extends ObjectType implements TemplateType
1013
{
1114

@@ -17,16 +20,16 @@ public function __construct(
1720
TemplateTypeStrategy $templateTypeStrategy,
1821
TemplateTypeVariance $templateTypeVariance,
1922
string $name,
20-
string $class
23+
ObjectType $bound
2124
)
2225
{
23-
parent::__construct($class);
26+
parent::__construct($bound->getClassName());
2427

2528
$this->scope = $scope;
2629
$this->strategy = $templateTypeStrategy;
2730
$this->variance = $templateTypeVariance;
2831
$this->name = $name;
29-
$this->bound = new ObjectType($class);
32+
$this->bound = $bound;
3033
}
3134

3235
public function toArgument(): TemplateType
@@ -36,10 +39,26 @@ public function toArgument(): TemplateType
3639
new TemplateTypeArgumentStrategy(),
3740
$this->variance,
3841
$this->name,
39-
$this->getClassName()
42+
TemplateTypeHelper::toArgument($this->getBound())
4043
);
4144
}
4245

46+
public function traverse(callable $cb): Type
47+
{
48+
$newBound = $cb($this->getBound());
49+
if ($this->getBound() !== $newBound && $newBound instanceof ObjectType) {
50+
return new self(
51+
$this->scope,
52+
$this->strategy,
53+
$this->variance,
54+
$this->name,
55+
$newBound
56+
);
57+
}
58+
59+
return $this;
60+
}
61+
4362
/**
4463
* @param mixed[] $properties
4564
* @return Type
@@ -51,7 +70,7 @@ public static function __set_state(array $properties): Type
5170
$properties['strategy'],
5271
$properties['variance'],
5372
$properties['name'],
54-
$properties['className']
73+
$properties['bound']
5574
);
5675
}
5776

0 commit comments

Comments
 (0)