Skip to content

Commit 27beed4

Browse files
committed
Almost final StaticCallHandler
1 parent e74da56 commit 27beed4

File tree

5 files changed

+683
-3
lines changed

5 files changed

+683
-3
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator\ExprHandler;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PHPStan\Analyser\Generator\GeneratorNodeScopeResolver;
9+
use PHPStan\Analyser\Generator\GeneratorScope;
10+
use PHPStan\Analyser\Generator\RunInFiberRequest;
11+
use PHPStan\DependencyInjection\AutowiredService;
12+
use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
13+
use PHPStan\Reflection\MethodReflection;
14+
use PHPStan\Reflection\ParametersAcceptor;
15+
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeCombinator;
17+
use function count;
18+
19+
/**
20+
* @phpstan-import-type GeneratorTValueType from GeneratorNodeScopeResolver
21+
* @phpstan-import-type GeneratorTSendType from GeneratorNodeScopeResolver
22+
*/
23+
#[AutowiredService]
24+
final class MethodCallHelper
25+
{
26+
27+
public function __construct(
28+
private DynamicReturnTypeExtensionRegistryProvider $dynamicReturnTypeExtensionRegistryProvider,
29+
)
30+
{
31+
}
32+
33+
/**
34+
* @param list<non-empty-string> $objectClassNamesFromType
35+
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, ?Type>
36+
*/
37+
public function methodCallReturnType(
38+
GeneratorScope $scope,
39+
MethodReflection $methodReflection,
40+
ParametersAcceptor $parametersAcceptor,
41+
MethodCall|Expr\StaticCall|null $normalizedMethodCall,
42+
array $objectClassNamesFromType,
43+
): Generator
44+
{
45+
if ($normalizedMethodCall === null) {
46+
//return $this->transformVoidToNull($parametersAcceptor->getReturnType(), $methodCall);
47+
return $parametersAcceptor->getReturnType();
48+
}
49+
50+
$resolvedTypes = [];
51+
foreach ($objectClassNamesFromType as $className) {
52+
if ($normalizedMethodCall instanceof MethodCall) {
53+
foreach ($this->dynamicReturnTypeExtensionRegistryProvider->getRegistry()->getDynamicMethodReturnTypeExtensionsForClass($className) as $dynamicMethodReturnTypeExtension) {
54+
if (!$dynamicMethodReturnTypeExtension->isMethodSupported($methodReflection)) {
55+
continue;
56+
}
57+
58+
$resolvedType = (yield new RunInFiberRequest(static fn () => $dynamicMethodReturnTypeExtension->getTypeFromMethodCall($methodReflection, $normalizedMethodCall, $scope)))->value;
59+
if ($resolvedType === null) {
60+
continue;
61+
}
62+
63+
$resolvedTypes[] = $resolvedType;
64+
}
65+
} else {
66+
foreach ($this->dynamicReturnTypeExtensionRegistryProvider->getRegistry()->getDynamicStaticMethodReturnTypeExtensionsForClass($className) as $dynamicStaticMethodReturnTypeExtension) {
67+
if (!$dynamicStaticMethodReturnTypeExtension->isStaticMethodSupported($methodReflection)) {
68+
continue;
69+
}
70+
71+
$resolvedType = (yield new RunInFiberRequest(static fn () => $dynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall(
72+
$methodReflection,
73+
$normalizedMethodCall,
74+
$scope,
75+
)))->value;
76+
if ($resolvedType === null) {
77+
continue;
78+
}
79+
80+
$resolvedTypes[] = $resolvedType;
81+
}
82+
}
83+
}
84+
85+
if (count($resolvedTypes) > 0) {
86+
//return $this->transformVoidToNull(TypeCombinator::union(...$resolvedTypes), $methodCall);
87+
return TypeCombinator::union(...$resolvedTypes);
88+
}
89+
90+
//return $this->transformVoidToNull($parametersAcceptor->getReturnType(), $methodCall);
91+
return $parametersAcceptor->getReturnType();
92+
}
93+
94+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator\ExprHandler;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Expr\PropertyFetch;
9+
use PHPStan\Analyser\Generator\GeneratorNodeScopeResolver;
10+
use PHPStan\Analyser\Generator\TypeExprRequest;
11+
use PHPStan\DependencyInjection\AutowiredService;
12+
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeCombinator;
14+
15+
/**
16+
* @phpstan-import-type GeneratorTValueType from GeneratorNodeScopeResolver
17+
* @phpstan-import-type GeneratorTSendType from GeneratorNodeScopeResolver
18+
*/
19+
#[AutowiredService]
20+
final class NullsafeShortCircuitingHelper
21+
{
22+
23+
/**
24+
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, Type>
25+
*/
26+
public function getNullsafeShortCircuitingType(Expr $expr, Type $type): Generator
27+
{
28+
if ($expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\NullsafeMethodCall) {
29+
$varType = (yield new TypeExprRequest($expr->var))->type;
30+
if (TypeCombinator::containsNull($varType)) {
31+
return TypeCombinator::addNull($type);
32+
}
33+
34+
return $type;
35+
}
36+
37+
if ($expr instanceof Expr\ArrayDimFetch) {
38+
$gen = $this->getNullsafeShortCircuitingType($expr->var, $type);
39+
yield from $gen;
40+
return $gen->getReturn();
41+
}
42+
43+
if ($expr instanceof PropertyFetch) {
44+
$gen = $this->getNullsafeShortCircuitingType($expr->var, $type);
45+
yield from $gen;
46+
return $gen->getReturn();
47+
}
48+
49+
if ($expr instanceof Expr\StaticPropertyFetch && $expr->class instanceof Expr) {
50+
$gen = $this->getNullsafeShortCircuitingType($expr->class, $type);
51+
yield from $gen;
52+
return $gen->getReturn();
53+
}
54+
55+
if ($expr instanceof MethodCall) {
56+
$gen = $this->getNullsafeShortCircuitingType($expr->var, $type);
57+
yield from $gen;
58+
return $gen->getReturn();
59+
}
60+
61+
if ($expr instanceof Expr\StaticCall && $expr->class instanceof Expr) {
62+
$gen = $this->getNullsafeShortCircuitingType($expr->class, $type);
63+
yield from $gen;
64+
return $gen->getReturn();
65+
}
66+
67+
return $type;
68+
}
69+
70+
}

0 commit comments

Comments
 (0)