Skip to content

Commit 1b117f7

Browse files
arnaud-lbondrejmirtes
authored andcommitted
Do not generalize class-string during template type inference
1 parent 1739a9a commit 1b117f7

39 files changed

+202
-34
lines changed

src/Type/Accessory/AccessoryLiteralStringType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Type\Constant\ConstantIntegerType;
1010
use PHPStan\Type\ErrorType;
1111
use PHPStan\Type\FloatType;
12+
use PHPStan\Type\GeneralizePrecision;
1213
use PHPStan\Type\IntegerType;
1314
use PHPStan\Type\IntersectionType;
1415
use PHPStan\Type\MixedType;
@@ -186,6 +187,11 @@ public function traverse(callable $cb): Type
186187
return $this;
187188
}
188189

190+
public function generalize(GeneralizePrecision $precision): Type
191+
{
192+
return new StringType();
193+
}
194+
189195
public static function __set_state(array $properties): Type
190196
{
191197
return new self();

src/Type/Accessory/AccessoryNonEmptyStringType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Type\Constant\ConstantIntegerType;
99
use PHPStan\Type\ErrorType;
1010
use PHPStan\Type\FloatType;
11+
use PHPStan\Type\GeneralizePrecision;
1112
use PHPStan\Type\IntegerType;
1213
use PHPStan\Type\IntersectionType;
1314
use PHPStan\Type\StringType;
@@ -182,6 +183,11 @@ public function traverse(callable $cb): Type
182183
return $this;
183184
}
184185

186+
public function generalize(GeneralizePrecision $precision): Type
187+
{
188+
return new StringType();
189+
}
190+
185191
public static function __set_state(array $properties): Type
186192
{
187193
return new self();

src/Type/Accessory/AccessoryNumericStringType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Type\Constant\ConstantIntegerType;
99
use PHPStan\Type\ErrorType;
1010
use PHPStan\Type\FloatType;
11+
use PHPStan\Type\GeneralizePrecision;
1112
use PHPStan\Type\IntegerType;
1213
use PHPStan\Type\IntersectionType;
1314
use PHPStan\Type\StringType;
@@ -181,6 +182,11 @@ public function traverse(callable $cb): Type
181182
return $this;
182183
}
183184

185+
public function generalize(GeneralizePrecision $precision): Type
186+
{
187+
return new StringType();
188+
}
189+
184190
public static function __set_state(array $properties): Type
185191
{
186192
return new self();

src/Type/Accessory/HasMethodType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\TrinaryLogic;
1212
use PHPStan\Type\CompoundType;
1313
use PHPStan\Type\IntersectionType;
14+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1415
use PHPStan\Type\Traits\NonGenericTypeTrait;
1516
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
1617
use PHPStan\Type\Traits\ObjectTypeTrait;
@@ -28,6 +29,7 @@ class HasMethodType implements AccessoryType, CompoundType
2829
use NonGenericTypeTrait;
2930
use UndecidedComparisonCompoundTypeTrait;
3031
use NonRemoveableTypeTrait;
32+
use NonGeneralizableTypeTrait;
3133

3234
/** @api */
3335
public function __construct(private string $methodName)

src/Type/Accessory/HasOffsetType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
1212
use PHPStan\Type\Traits\MaybeIterableTypeTrait;
1313
use PHPStan\Type\Traits\MaybeObjectTypeTrait;
14+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1415
use PHPStan\Type\Traits\NonGenericTypeTrait;
1516
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
1617
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
@@ -30,6 +31,7 @@ class HasOffsetType implements CompoundType, AccessoryType
3031
use NonGenericTypeTrait;
3132
use UndecidedComparisonCompoundTypeTrait;
3233
use NonRemoveableTypeTrait;
34+
use NonGeneralizableTypeTrait;
3335

3436
/** @api */
3537
public function __construct(private Type $offsetType)

src/Type/Accessory/HasPropertyType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\TrinaryLogic;
88
use PHPStan\Type\CompoundType;
99
use PHPStan\Type\IntersectionType;
10+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1011
use PHPStan\Type\Traits\NonGenericTypeTrait;
1112
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
1213
use PHPStan\Type\Traits\ObjectTypeTrait;
@@ -23,6 +24,7 @@ class HasPropertyType implements AccessoryType, CompoundType
2324
use NonGenericTypeTrait;
2425
use UndecidedComparisonCompoundTypeTrait;
2526
use NonRemoveableTypeTrait;
27+
use NonGeneralizableTypeTrait;
2628

2729
/** @api */
2830
public function __construct(private string $propertyName)

src/Type/Accessory/NonEmptyArrayType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Type\IntersectionType;
1111
use PHPStan\Type\MixedType;
1212
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
13+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1314
use PHPStan\Type\Traits\NonGenericTypeTrait;
1415
use PHPStan\Type\Traits\NonObjectTypeTrait;
1516
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
@@ -28,6 +29,7 @@ class NonEmptyArrayType implements CompoundType, AccessoryType
2829
use NonGenericTypeTrait;
2930
use UndecidedComparisonCompoundTypeTrait;
3031
use NonRemoveableTypeTrait;
32+
use NonGeneralizableTypeTrait;
3133

3234
/** @api */
3335
public function __construct()

src/Type/ArrayType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Type\Generic\TemplateTypeMap;
1919
use PHPStan\Type\Generic\TemplateTypeVariance;
2020
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
21+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
2122
use PHPStan\Type\Traits\NonObjectTypeTrait;
2223
use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
2324
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
@@ -36,6 +37,7 @@ class ArrayType implements Type
3637
use NonObjectTypeTrait;
3738
use UndecidedBooleanTypeTrait;
3839
use UndecidedComparisonTypeTrait;
40+
use NonGeneralizableTypeTrait;
3941

4042
private Type $keyType;
4143

src/Type/BooleanType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Type\Constant\ConstantIntegerType;
99
use PHPStan\Type\Constant\ConstantStringType;
1010
use PHPStan\Type\Traits\NonCallableTypeTrait;
11+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1112
use PHPStan\Type\Traits\NonGenericTypeTrait;
1213
use PHPStan\Type\Traits\NonIterableTypeTrait;
1314
use PHPStan\Type\Traits\NonObjectTypeTrait;
@@ -27,6 +28,7 @@ class BooleanType implements Type
2728
use UndecidedComparisonTypeTrait;
2829
use NonGenericTypeTrait;
2930
use NonOffsetAccessibleTypeTrait;
31+
use NonGeneralizableTypeTrait;
3032

3133
/** @api */
3234
public function __construct()

src/Type/CallableType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPStan\Type\Traits\MaybeIterableTypeTrait;
1717
use PHPStan\Type\Traits\MaybeObjectTypeTrait;
1818
use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait;
19+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1920
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
2021
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
2122
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
@@ -34,6 +35,7 @@ class CallableType implements CompoundType, ParametersAcceptor
3435
use TruthyBooleanTypeTrait;
3536
use UndecidedComparisonCompoundTypeTrait;
3637
use NonRemoveableTypeTrait;
38+
use NonGeneralizableTypeTrait;
3739

3840
/** @var array<int, ParameterReflection> */
3941
private array $parameters;

src/Type/ClosureType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use PHPStan\Type\Generic\TemplateType;
2323
use PHPStan\Type\Generic\TemplateTypeHelper;
2424
use PHPStan\Type\Generic\TemplateTypeMap;
25+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
2526
use PHPStan\Type\Traits\NonGenericTypeTrait;
2627
use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait;
2728
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
@@ -39,6 +40,7 @@ class ClosureType implements TypeWithClassName, ParametersAcceptor
3940
use UndecidedComparisonTypeTrait;
4041
use NonOffsetAccessibleTypeTrait;
4142
use NonRemoveableTypeTrait;
43+
use NonGeneralizableTypeTrait;
4244

4345
private ObjectType $objectType;
4446

src/Type/Constant/ConstantArrayType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,10 @@ public function generalize(GeneralizePrecision $precision): Type
625625
return $this;
626626
}
627627

628+
if ($precision->isTemplateArgument()) {
629+
return $this->traverse(static fn (Type $type) => $type->generalize($precision));
630+
}
631+
628632
$arrayType = new ArrayType(
629633
TypeUtils::generalizeType($this->getKeyType(), $precision),
630634
TypeUtils::generalizeType($this->getItemType(), $precision),

src/Type/FloatType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Type\Constant\ConstantArrayType;
88
use PHPStan\Type\Constant\ConstantIntegerType;
99
use PHPStan\Type\Traits\NonCallableTypeTrait;
10+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1011
use PHPStan\Type\Traits\NonGenericTypeTrait;
1112
use PHPStan\Type\Traits\NonIterableTypeTrait;
1213
use PHPStan\Type\Traits\NonObjectTypeTrait;
@@ -28,6 +29,7 @@ class FloatType implements Type
2829
use NonGenericTypeTrait;
2930
use NonOffsetAccessibleTypeTrait;
3031
use NonRemoveableTypeTrait;
32+
use NonGeneralizableTypeTrait;
3133

3234
/** @api */
3335
public function __construct()

src/Type/GeneralizePrecision.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class GeneralizePrecision
77

88
private const LESS_SPECIFIC = 1;
99
private const MORE_SPECIFIC = 2;
10+
private const TEMPLATE_ARGUMENT = 3;
1011

1112
/** @var self[] */
1213
private static array $registry;
@@ -33,9 +34,25 @@ public static function moreSpecific(): self
3334
return self::create(self::MORE_SPECIFIC);
3435
}
3536

37+
/** @api */
38+
public static function templateArgument(): self
39+
{
40+
return self::create(self::TEMPLATE_ARGUMENT);
41+
}
42+
43+
public function isLessSpecific(): bool
44+
{
45+
return $this->value === self::LESS_SPECIFIC;
46+
}
47+
3648
public function isMoreSpecific(): bool
3749
{
3850
return $this->value === self::MORE_SPECIFIC;
3951
}
4052

53+
public function isTemplateArgument(): bool
54+
{
55+
return $this->value === self::TEMPLATE_ARGUMENT;
56+
}
57+
4158
}

src/Type/Generic/TemplateTypeHelper.php

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
namespace PHPStan\Type\Generic;
44

5-
use PHPStan\Type\Constant\ConstantArrayType;
6-
use PHPStan\Type\ConstantType;
75
use PHPStan\Type\ErrorType;
8-
use PHPStan\Type\GeneralizePrecision;
9-
use PHPStan\Type\StringType;
106
use PHPStan\Type\Type;
117
use PHPStan\Type\TypeTraverser;
128

@@ -64,23 +60,4 @@ public static function toArgument(Type $type): Type
6460
});
6561
}
6662

67-
public static function generalizeType(Type $type): Type
68-
{
69-
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
70-
if ($type instanceof ConstantType && !$type instanceof ConstantArrayType) {
71-
return $type->generalize(GeneralizePrecision::lessSpecific());
72-
}
73-
74-
if ($type->isNonEmptyString()->yes()) {
75-
return new StringType();
76-
}
77-
78-
if ($type->isLiteralString()->yes()) {
79-
return new StringType();
80-
}
81-
82-
return $traverse($type);
83-
});
84-
}
85-
8663
}

src/Type/Generic/TemplateTypeTrait.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Type\Generic;
44

55
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\GeneralizePrecision;
67
use PHPStan\Type\IntersectionType;
78
use PHPStan\Type\MixedType;
89
use PHPStan\Type\Type;
@@ -194,7 +195,7 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
194195
$resolvedBound = TemplateTypeHelper::resolveTemplateTypes($this->getBound(), $map);
195196
if ($resolvedBound->isSuperTypeOf($receivedType)->yes()) {
196197
return (new TemplateTypeMap([
197-
$this->name => $this->shouldGeneralizeInferredType() ? TemplateTypeHelper::generalizeType($receivedType) : $receivedType,
198+
$this->name => $this->shouldGeneralizeInferredType() ? $receivedType->generalize(GeneralizePrecision::templateArgument()) : $receivedType,
198199
]))->union($map);
199200
}
200201

src/Type/IntegerType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Type\Constant\ConstantArrayType;
77
use PHPStan\Type\Constant\ConstantIntegerType;
88
use PHPStan\Type\Traits\NonCallableTypeTrait;
9+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
910
use PHPStan\Type\Traits\NonGenericTypeTrait;
1011
use PHPStan\Type\Traits\NonIterableTypeTrait;
1112
use PHPStan\Type\Traits\NonObjectTypeTrait;
@@ -25,6 +26,7 @@ class IntegerType implements Type
2526
use UndecidedComparisonTypeTrait;
2627
use NonGenericTypeTrait;
2728
use NonOffsetAccessibleTypeTrait;
29+
use NonGeneralizableTypeTrait;
2830

2931
/** @api */
3032
public function __construct()

src/Type/IntersectionType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use PHPStan\Type\Generic\TemplateType;
2323
use PHPStan\Type\Generic\TemplateTypeMap;
2424
use PHPStan\Type\Generic\TemplateTypeVariance;
25+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
26+
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
2527
use function array_map;
2628
use function count;
2729
use function implode;
@@ -34,6 +36,9 @@
3436
class IntersectionType implements CompoundType
3537
{
3638

39+
use NonRemoveableTypeTrait;
40+
use NonGeneralizableTypeTrait;
41+
3742
/** @var Type[] */
3843
private array $types;
3944

src/Type/IterableType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
1313
use PHPStan\Type\Traits\MaybeObjectTypeTrait;
1414
use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait;
15+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1516
use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
1617
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
1718
use Traversable;
@@ -27,6 +28,7 @@ class IterableType implements CompoundType
2728
use MaybeOffsetAccessibleTypeTrait;
2829
use UndecidedBooleanTypeTrait;
2930
use UndecidedComparisonCompoundTypeTrait;
31+
use NonGeneralizableTypeTrait;
3032

3133
/** @api */
3234
public function __construct(

src/Type/MixedType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\Type\Constant\ConstantBooleanType;
2020
use PHPStan\Type\Generic\TemplateMixedType;
2121
use PHPStan\Type\Generic\TemplateType;
22+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
2223
use PHPStan\Type\Traits\NonGenericTypeTrait;
2324
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
2425
use function sprintf;
@@ -29,6 +30,7 @@ class MixedType implements CompoundType, SubtractableType
2930

3031
use NonGenericTypeTrait;
3132
use UndecidedComparisonCompoundTypeTrait;
33+
use NonGeneralizableTypeTrait;
3234

3335
private ?Type $subtractedType;
3436

src/Type/NeverType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
1313
use PHPStan\ShouldNotHappenException;
1414
use PHPStan\TrinaryLogic;
15+
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1516
use PHPStan\Type\Traits\NonGenericTypeTrait;
1617
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
1718
use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
@@ -25,6 +26,7 @@ class NeverType implements CompoundType
2526
use NonGenericTypeTrait;
2627
use UndecidedComparisonCompoundTypeTrait;
2728
use NonRemoveableTypeTrait;
29+
use NonGeneralizableTypeTrait;
2830

2931
/** @api */
3032
public function __construct(private bool $isExplicit = false)

0 commit comments

Comments
 (0)