Skip to content

Commit 8ad35b3

Browse files
authored
Implement conditional parameter types
1 parent 6865741 commit 8ad35b3

17 files changed

+175
-139
lines changed

Diff for: src/Analyser/MutatingScope.php

+1-15
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
use PHPStan\Type\IntegerRangeType;
9191
use PHPStan\Type\IntegerType;
9292
use PHPStan\Type\IntersectionType;
93-
use PHPStan\Type\LateResolvableType;
9493
use PHPStan\Type\MixedType;
9594
use PHPStan\Type\NeverType;
9695
use PHPStan\Type\NonexistentParentClassType;
@@ -575,7 +574,7 @@ public function getType(Expr $node): Type
575574
$key = $this->getNodeKey($node);
576575

577576
if (!array_key_exists($key, $this->resolvedTypes)) {
578-
$this->resolvedTypes[$key] = $this->resolveLateResolvableTypes($this->resolveType($node));
577+
$this->resolvedTypes[$key] = TypeUtils::resolveLateResolvableTypes($this->resolveType($node));
579578
}
580579
return $this->resolvedTypes[$key];
581580
}
@@ -6018,17 +6017,4 @@ private function integerRangeMath(Type $range, Expr $node, Type $operand): Type
60186017
return IntegerRangeType::fromInterval($min, $max);
60196018
}
60206019

6021-
private function resolveLateResolvableTypes(Type $type): Type
6022-
{
6023-
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
6024-
$type = $traverse($type);
6025-
6026-
if ($type instanceof LateResolvableType) {
6027-
$type = $type->resolve();
6028-
}
6029-
6030-
return $type;
6031-
});
6032-
}
6033-
60346020
}

Diff for: src/Reflection/FunctionVariant.php

+1-19
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
use PHPStan\Type\Generic\TemplateTypeMap;
66
use PHPStan\Type\Type;
7-
use PHPStan\Type\TypeUtils;
87

98
/** @api */
10-
class FunctionVariant implements ParametersAcceptor, SingleParametersAcceptor
9+
class FunctionVariant implements ParametersAcceptor
1110
{
1211

1312
/**
@@ -52,21 +51,4 @@ public function getReturnType(): Type
5251
return $this->returnType;
5352
}
5453

55-
/**
56-
* @return static
57-
*/
58-
public function flattenConditionalsInReturnType(): SingleParametersAcceptor
59-
{
60-
/** @var static $result */
61-
$result = new self(
62-
$this->templateTypeMap,
63-
$this->resolvedTemplateTypeMap,
64-
$this->parameters,
65-
$this->isVariadic,
66-
TypeUtils::flattenConditionals($this->returnType),
67-
);
68-
69-
return $result;
70-
}
71-
7254
}

Diff for: src/Reflection/FunctionVariantWithPhpDocs.php

-20
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PHPStan\Type\Generic\TemplateTypeMap;
66
use PHPStan\Type\Type;
7-
use PHPStan\Type\TypeUtils;
87

98
/** @api */
109
class FunctionVariantWithPhpDocs extends FunctionVariant implements ParametersAcceptorWithPhpDocs
@@ -54,23 +53,4 @@ public function getNativeReturnType(): Type
5453
return $this->nativeReturnType;
5554
}
5655

57-
/**
58-
* @return static
59-
*/
60-
public function flattenConditionalsInReturnType(): SingleParametersAcceptor
61-
{
62-
/** @var static $result */
63-
$result = new self(
64-
$this->getTemplateTypeMap(),
65-
$this->getResolvedTemplateTypeMap(),
66-
$this->getParameters(),
67-
$this->isVariadic(),
68-
TypeUtils::flattenConditionals($this->getReturnType()),
69-
TypeUtils::flattenConditionals($this->phpDocReturnType),
70-
$this->nativeReturnType,
71-
);
72-
73-
return $result;
74-
}
75-
7656
}

Diff for: src/Reflection/ParametersAcceptorSelector.php

+1-7
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,7 @@ public static function selectSingle(
5050
throw new ShouldNotHappenException('Multiple variants - use selectFromArgs() instead.');
5151
}
5252

53-
$parametersAcceptor = $parametersAcceptors[0];
54-
55-
if ($parametersAcceptor instanceof SingleParametersAcceptor) {
56-
$parametersAcceptor = $parametersAcceptor->flattenConditionalsInReturnType();
57-
}
58-
59-
return $parametersAcceptor;
53+
return $parametersAcceptors[0];
6054
}
6155

6256
/**

Diff for: src/Reflection/ResolvedFunctionVariant.php

+14-21
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use function array_key_exists;
1515
use function array_map;
1616

17-
class ResolvedFunctionVariant implements ParametersAcceptor, SingleParametersAcceptor
17+
class ResolvedFunctionVariant implements ParametersAcceptor
1818
{
1919

2020
/** @var ParameterReflection[]|null */
@@ -57,7 +57,13 @@ public function getParameters(): array
5757
if ($parameters === null) {
5858
$parameters = array_map(fn (ParameterReflection $param): ParameterReflection => new DummyParameter(
5959
$param->getName(),
60-
TemplateTypeHelper::resolveTemplateTypes($param->getType(), $this->resolvedTemplateTypeMap),
60+
TypeUtils::resolveLateResolvableTypes(
61+
TemplateTypeHelper::resolveTemplateTypes(
62+
$this->resolveConditionalTypesForParameter($param->getType()),
63+
$this->resolvedTemplateTypeMap,
64+
),
65+
false,
66+
),
6167
$param->isOptional(),
6268
$param->passedByReference(),
6369
$param->isVariadic(),
@@ -88,9 +94,12 @@ public function getReturnType(): Type
8894
$type = $this->returnType;
8995

9096
if ($type === null) {
91-
$type = TemplateTypeHelper::resolveTemplateTypes(
92-
$this->getReturnTypeWithUnresolvableTemplateTypes(),
93-
$this->resolvedTemplateTypeMap,
97+
$type = TypeUtils::resolveLateResolvableTypes(
98+
TemplateTypeHelper::resolveTemplateTypes(
99+
$this->getReturnTypeWithUnresolvableTemplateTypes(),
100+
$this->resolvedTemplateTypeMap,
101+
),
102+
false,
94103
);
95104

96105
$this->returnType = $type;
@@ -99,22 +108,6 @@ public function getReturnType(): Type
99108
return $type;
100109
}
101110

102-
/**
103-
* @return static
104-
*/
105-
public function flattenConditionalsInReturnType(): SingleParametersAcceptor
106-
{
107-
/** @var static $result */
108-
$result = new self(
109-
$this->parametersAcceptor,
110-
$this->resolvedTemplateTypeMap,
111-
$this->passedArgs,
112-
);
113-
$result->returnType = TypeUtils::flattenConditionals($this->getReturnType());
114-
115-
return $result;
116-
}
117-
118111
private function resolveResolvableTemplateTypes(Type $type): Type
119112
{
120113
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {

Diff for: src/Reflection/SingleParametersAcceptor.php

-13
This file was deleted.

Diff for: src/Rules/FunctionCallParametersCheck.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public function check(
248248
continue;
249249
}
250250

251-
$parameterType = $parameter->getType();
251+
$parameterType = TypeUtils::resolveLateResolvableTypes($parameter->getType());
252252
if (
253253
$this->checkArgumentTypes
254254
&& !$parameter->passedByReference()->createsNewVariable()

Diff for: src/Rules/FunctionReturnTypeCheck.php

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Type\GenericTypeVariableResolver;
1010
use PHPStan\Type\NeverType;
1111
use PHPStan\Type\Type;
12+
use PHPStan\Type\TypeUtils;
1213
use PHPStan\Type\TypeWithClassName;
1314
use PHPStan\Type\VerbosityLevel;
1415
use PHPStan\Type\VoidType;
@@ -36,6 +37,8 @@ public function checkReturnType(
3637
bool $isGenerator,
3738
): array
3839
{
40+
$returnType = TypeUtils::resolveLateResolvableTypes($returnType);
41+
3942
if ($returnType instanceof NeverType && $returnType->isExplicit()) {
4043
return [
4144
RuleErrorBuilder::message($neverMessage)

Diff for: src/Type/ConditionalType.php

+16-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace PHPStan\Type;
44

55
use PHPStan\Type\Generic\TemplateTypeVariance;
6-
use PHPStan\Type\Traits\ConditionalTypeTrait;
6+
use PHPStan\Type\Traits\LateResolvableTypeTrait;
77
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
88
use function array_merge;
99
use function sprintf;
@@ -12,19 +12,17 @@
1212
final class ConditionalType implements CompoundType, LateResolvableType
1313
{
1414

15-
use ConditionalTypeTrait;
15+
use LateResolvableTypeTrait;
1616
use NonGeneralizableTypeTrait;
1717

1818
public function __construct(
1919
private Type $subject,
2020
private Type $target,
21-
Type $if,
22-
Type $else,
21+
private Type $if,
22+
private Type $else,
2323
private bool $negated,
2424
)
2525
{
26-
$this->if = $if;
27-
$this->else = $else;
2826
}
2927

3028
public function getSubject(): Type
@@ -83,26 +81,24 @@ public function describe(VerbosityLevel $level): string
8381
);
8482
}
8583

86-
public function resolve(): Type
84+
public function isResolvable(): bool
8785
{
88-
return $this->getResult();
86+
return !TypeUtils::containsTemplateType($this->subject) && !TypeUtils::containsTemplateType($this->target);
8987
}
9088

91-
public function getResult(): Type
89+
protected function getResult(): Type
9290
{
93-
if ($this->result === null) {
94-
$isSuperType = $this->target->isSuperTypeOf($this->subject);
95-
96-
if ($isSuperType->yes()) {
97-
$this->result = !$this->negated ? $this->if : $this->else;
98-
} elseif ($isSuperType->no()) {
99-
$this->result = !$this->negated ? $this->else : $this->if;
100-
} else {
101-
$this->result = TypeCombinator::union($this->if, $this->else);
102-
}
91+
$isSuperType = $this->target->isSuperTypeOf($this->subject);
92+
93+
if ($isSuperType->yes()) {
94+
return !$this->negated ? $this->if : $this->else;
95+
}
96+
97+
if ($isSuperType->no()) {
98+
return !$this->negated ? $this->else : $this->if;
10399
}
104100

105-
return $this->result;
101+
return TypeCombinator::union($this->if, $this->else);
106102
}
107103

108104
public function traverse(callable $cb): Type

Diff for: src/Type/ConditionalTypeForParameter.php

+15-7
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,26 @@
33
namespace PHPStan\Type;
44

55
use PHPStan\Type\Generic\TemplateTypeVariance;
6-
use PHPStan\Type\Traits\ConditionalTypeTrait;
6+
use PHPStan\Type\Traits\LateResolvableTypeTrait;
77
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
88
use function array_merge;
99
use function sprintf;
1010

1111
/** @api */
12-
final class ConditionalTypeForParameter implements CompoundType
12+
final class ConditionalTypeForParameter implements CompoundType, LateResolvableType
1313
{
1414

15-
use ConditionalTypeTrait;
15+
use LateResolvableTypeTrait;
1616
use NonGeneralizableTypeTrait;
1717

1818
public function __construct(
1919
private string $parameterName,
2020
private Type $target,
21-
Type $if,
22-
Type $else,
21+
private Type $if,
22+
private Type $else,
2323
private bool $negated,
2424
)
2525
{
26-
$this->if = $if;
27-
$this->else = $else;
2826
}
2927

3028
public function getParameterName(): string
@@ -103,6 +101,16 @@ public function describe(VerbosityLevel $level): string
103101
);
104102
}
105103

104+
public function isResolvable(): bool
105+
{
106+
return false;
107+
}
108+
109+
protected function getResult(): Type
110+
{
111+
return TypeCombinator::union($this->if, $this->else);
112+
}
113+
106114
/**
107115
* @param callable(Type): Type $cb
108116
*/

Diff for: src/Type/LateResolvableType.php

+2
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ interface LateResolvableType
88

99
public function resolve(): Type;
1010

11+
public function isResolvable(): bool;
12+
1113
}

Diff for: src/Type/Traits/ConditionalTypeTrait.php renamed to src/Type/Traits/LateResolvableTypeTrait.php

+5-8
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,10 @@
1313
use PHPStan\Type\CompoundType;
1414
use PHPStan\Type\Generic\TemplateTypeMap;
1515
use PHPStan\Type\Type;
16-
use PHPStan\Type\TypeCombinator;
1716

18-
trait ConditionalTypeTrait
17+
trait LateResolvableTypeTrait
1918
{
2019

21-
private Type $if;
22-
23-
private Type $else;
24-
2520
private ?Type $result = null;
2621

2722
public function accepts(Type $type, bool $strictTypes): TrinaryLogic
@@ -288,13 +283,15 @@ public function isGreaterThanOrEqual(Type $otherType): TrinaryLogic
288283
return $otherType->isSmallerThanOrEqual($result);
289284
}
290285

291-
public function getResult(): Type
286+
public function resolve(): Type
292287
{
293288
if ($this->result === null) {
294-
return $this->result = TypeCombinator::union($this->if, $this->else);
289+
return $this->result = $this->getResult();
295290
}
296291

297292
return $this->result;
298293
}
299294

295+
abstract protected function getResult(): Type;
296+
300297
}

0 commit comments

Comments
 (0)