Repository navigation
Expand file tree
/
Copy pathArrowFunctionHandler.php
More file actions
111 lines (100 loc) · 3.88 KB
/
Copy pathArrowFunctionHandler.php
File metadata and controls
111 lines (100 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php declare(strict_types = 1);
namespace PHPStan\Analyser\ExprHandler;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Stmt;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultFactory;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\ExprHandler;
use PHPStan\Analyser\ExprHandler\Helper\ClosureTypeResolver;
use PHPStan\Analyser\ExprHandler\Helper\DefaultNarrowingHelper;
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Analyser\VariableFlow;
use PHPStan\DependencyInjection\AutowiredService;
use function is_string;
/**
* @implements ExprHandler<ArrowFunction>
*/
#[AutowiredService]
final class ArrowFunctionHandler implements ExprHandler
{
public function __construct(
private ClosureTypeResolver $closureTypeResolver,
private ExpressionResultFactory $expressionResultFactory,
private DefaultNarrowingHelper $defaultNarrowingHelper,
)
{
}
public function supports(Expr $expr): bool
{
return $expr instanceof ArrowFunction;
}
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
{
$arrowFunctionResult = $nodeScopeResolver->processArrowFunctionNode($stmt, $expr, $scope, $storage, $nodeCallback, $context->getPassedToType(), $context->getNativePassedToType(), $context);
$result = $arrowFunctionResult->getExpressionResult();
// A plain typeCallback recursing through getClosureType() would re-walk
// the body each getType() ask before the cache populates and hang;
// ExpressionResult excludes closures from its tracked-type early return.
// Compute the ClosureType once here and store it as an eager value.
//
// Both flavours are built from the arrow function body the single walk in
// processArrowFunctionNode() already covered, without a second walk: the
// native flavour reads the body expression's stored native types off the
// same arrowScope (an arrow's native return type is its body's native type).
$arrowScope = $arrowFunctionResult->getArrowFunctionScope();
$type = $this->closureTypeResolver->buildClosureTypeForArrowFunction(
$scope,
$expr,
$arrowScope,
$arrowFunctionResult->getClosureTypeThrowPoints(),
$arrowFunctionResult->getClosureTypeImpurePoints(),
$arrowFunctionResult->getInvalidateExpressions(),
false,
$storage,
$context->getPassedToType(),
$context->getNativePassedToType(),
);
$nativeType = $this->closureTypeResolver->buildClosureTypeForArrowFunction(
$scope,
$expr,
$arrowScope,
$arrowFunctionResult->getClosureTypeThrowPoints(),
$arrowFunctionResult->getClosureTypeImpurePoints(),
$arrowFunctionResult->getInvalidateExpressions(),
true,
$storage,
$context->getPassedToType(),
$context->getNativePassedToType(),
);
return $this->expressionResultFactory->create(
$result->getScope(),
beforeScope: $scope,
expr: $expr,
variableFlow: $result->getVariableFlow(),
hasYield: $result->hasYield(),
isAlwaysTerminating: false,
throwPoints: [],
impurePoints: [],
specifyTypesCallback: fn (TypeSpecifierContext $c, bool $nativeTypesPromoted) => $this->defaultNarrowingHelper->specifyDefaultTypes($expr, $c),
type: $type,
nativeType: $nativeType,
typeCallback: null,
);
}
public static function getVariableFlow(ArrowFunction $expr, ExpressionResult $bodyResult): VariableFlow
{
$outputs = [];
foreach ($expr->params as $param) {
if (!$param->byRef || !$param->var instanceof Expr\Variable || !is_string($param->var->name)) {
continue;
}
$outputs[] = VariableFlow::read($param->var->name);
}
return VariableFlow::arrow($expr, $bodyResult->getVariableFlow(), VariableFlow::sequence(...$outputs));
}
}