Skip to content

Commit 3cdee3a

Browse files
authored
Make ParametersAcceptorSelector::selectSingle() return union-ed conditional types
1 parent eeedd1c commit 3cdee3a

13 files changed

+136
-6
lines changed

Diff for: src/Reflection/FunctionVariant.php

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

55
use PHPStan\Type\Generic\TemplateTypeMap;
66
use PHPStan\Type\Type;
7+
use PHPStan\Type\TypeUtils;
78

89
/** @api */
9-
class FunctionVariant implements ParametersAcceptor
10+
class FunctionVariant implements ParametersAcceptor, SingleParametersAcceptor
1011
{
1112

1213
/**
@@ -51,4 +52,21 @@ public function getReturnType(): Type
5152
return $this->returnType;
5253
}
5354

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+
5472
}

Diff for: src/Reflection/FunctionVariantWithPhpDocs.php

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

55
use PHPStan\Type\Generic\TemplateTypeMap;
66
use PHPStan\Type\Type;
7+
use PHPStan\Type\TypeUtils;
78

89
/** @api */
910
class FunctionVariantWithPhpDocs extends FunctionVariant implements ParametersAcceptorWithPhpDocs
@@ -53,4 +54,23 @@ public function getNativeReturnType(): Type
5354
return $this->nativeReturnType;
5455
}
5556

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+
5676
}

Diff for: src/Reflection/ParametersAcceptorSelector.php

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

51-
return $parametersAcceptors[0];
51+
$parametersAcceptor = $parametersAcceptors[0];
52+
53+
if ($parametersAcceptor instanceof SingleParametersAcceptor) {
54+
$parametersAcceptor = $parametersAcceptor->flattenConditionalsInReturnType();
55+
}
56+
57+
return $parametersAcceptor;
5258
}
5359

5460
/**

Diff for: src/Reflection/ResolvedFunctionVariant.php

+18-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
use PHPStan\Type\Generic\TemplateTypeMap;
99
use PHPStan\Type\Type;
1010
use PHPStan\Type\TypeTraverser;
11+
use PHPStan\Type\TypeUtils;
1112
use function array_key_exists;
1213
use function array_map;
1314

14-
class ResolvedFunctionVariant implements ParametersAcceptor
15+
class ResolvedFunctionVariant implements ParametersAcceptor, SingleParametersAcceptor
1516
{
1617

1718
/** @var ParameterReflection[]|null */
@@ -88,6 +89,22 @@ public function getReturnType(): Type
8889
return $type;
8990
}
9091

92+
/**
93+
* @return static
94+
*/
95+
public function flattenConditionalsInReturnType(): SingleParametersAcceptor
96+
{
97+
/** @var static $result */
98+
$result = new self(
99+
$this->parametersAcceptor,
100+
$this->resolvedTemplateTypeMap,
101+
$this->passedArgs,
102+
);
103+
$result->returnType = TypeUtils::flattenConditionals($this->getReturnType());
104+
105+
return $result;
106+
}
107+
91108
private function resolveConditionalTypes(Type $type): Type
92109
{
93110
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {

Diff for: src/Reflection/SingleParametersAcceptor.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection;
4+
5+
interface SingleParametersAcceptor
6+
{
7+
8+
/**
9+
* @return static
10+
*/
11+
public function flattenConditionalsInReturnType(): self;
12+
13+
}

Diff for: src/Type/ConditionalType.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private function resolve(): Type
9191
}
9292

9393
if ($this->isResolved()) {
94-
return TypeCombinator::union($this->if, $this->else);
94+
return $this->getResult();
9595
}
9696

9797
return $this;

Diff for: src/Type/Traits/ConditionalTypeTrait.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ public function isGreaterThanOrEqual(Type $otherType): TrinaryLogic
288288
return $otherType->isSmallerThanOrEqual($result);
289289
}
290290

291-
private function getResult(): Type
291+
public function getResult(): Type
292292
{
293293
if ($this->result === null) {
294294
return $this->result = TypeCombinator::union($this->if, $this->else);

Diff for: src/Type/TypeUtils.php

+11
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,15 @@ public static function containsCallable(Type $type): bool
328328
return false;
329329
}
330330

331+
public static function flattenConditionals(Type $type): Type
332+
{
333+
return TypeTraverser::map($type, static function (Type $type, callable $traverse) {
334+
while ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
335+
$type = $type->getResult();
336+
}
337+
338+
return $traverse($type);
339+
});
340+
}
341+
331342
}

Diff for: tests/PHPStan/Analyser/AnalyserIntegrationTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ public function testBug6896(): void
622622
}
623623

624624
$errors = $this->runAnalyse(__DIR__ . '/data/bug-6896.php');
625-
$this->assertCount(4, $errors);
625+
$this->assertCount(2, $errors);
626626
}
627627

628628
public function testBug6940(): void

Diff for: tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public function dataAsserts(): iterable
1111
{
1212
yield from $this->gatherAssertTypes(__DIR__ . '/data/dynamic-method-return-types.php');
1313
yield from $this->gatherAssertTypes(__DIR__ . '/data/dynamic-method-return-compound-types.php');
14+
yield from $this->gatherAssertTypes(__DIR__ . '/data/dynamic-method-return-getsingle-conditional.php');
1415
}
1516

1617
/**

Diff for: tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php

+20
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,23 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
189189
}
190190

191191
}
192+
193+
194+
class ConditionalGetSingle implements DynamicMethodReturnTypeExtension {
195+
196+
public function getClass(): string
197+
{
198+
return \DynamicMethodReturnGetSingleConditional\Foo::class;
199+
}
200+
201+
public function isMethodSupported(MethodReflection $methodReflection): bool
202+
{
203+
return $methodReflection->getName() === 'get';
204+
}
205+
206+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
207+
{
208+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
209+
}
210+
211+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace DynamicMethodReturnGetSingleConditional;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
abstract class Foo
8+
{
9+
/**
10+
* @return ($input is 1 ? true : false)
11+
*/
12+
abstract public function get(int $input): mixed;
13+
14+
public function doFoo(): void
15+
{
16+
assertType('bool', $this->get(0));
17+
assertType('bool', $this->get(1));
18+
assertType('bool', $this->get(2));
19+
}
20+
}

Diff for: tests/PHPStan/Analyser/dynamic-return-type.neon

+4
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ services:
2727
class: PHPStan\Tests\FooGetSelf
2828
tags:
2929
- phpstan.broker.dynamicMethodReturnTypeExtension
30+
-
31+
class: PHPStan\Tests\ConditionalGetSingle
32+
tags:
33+
- phpstan.broker.dynamicMethodReturnTypeExtension

0 commit comments

Comments
 (0)