forked from phpstan/phpstan-src
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCallableTypeHelper.php
More file actions
119 lines (102 loc) · 3.83 KB
/
CallableTypeHelper.php
File metadata and controls
119 lines (102 loc) · 3.83 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
112
113
114
115
116
117
118
119
<?php declare(strict_types = 1);
namespace PHPStan\Type;
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
use PHPStan\TrinaryLogic;
use function array_key_exists;
use function array_merge;
use function count;
use function sprintf;
final class CallableTypeHelper
{
public static function isParametersAcceptorSuperTypeOf(
CallableParametersAcceptor $ours,
CallableParametersAcceptor $theirs,
bool $treatMixedAsAny,
bool $strictTypes = true,
): IsSuperTypeOfResult
{
$theirParameters = $theirs->getParameters();
$ourParameters = $ours->getParameters();
$lastParameter = null;
foreach ($theirParameters as $theirParameter) {
$lastParameter = $theirParameter;
}
$theirParameterCount = count($theirParameters);
$ourParameterCount = count($ourParameters);
if (
$lastParameter !== null
&& $lastParameter->isVariadic()
&& $theirParameterCount < $ourParameterCount
) {
foreach ($ourParameters as $i => $ourParameter) {
if (array_key_exists($i, $theirParameters)) {
continue;
}
$theirParameters[] = $lastParameter;
}
}
$result = IsSuperTypeOfResult::createYes();
foreach ($theirParameters as $i => $theirParameter) {
$parameterDescription = $theirParameter->getName() === '' ? sprintf('#%d', $i + 1) : sprintf('#%d $%s', $i + 1, $theirParameter->getName());
if (!isset($ourParameters[$i])) {
if ($theirParameter->isOptional()) {
continue;
}
$accepts = new IsSuperTypeOfResult(TrinaryLogic::createNo(), [
sprintf(
'Parameter %s of passed callable is required but accepting callable does not have that parameter. It will be called without it.',
$parameterDescription,
),
]);
$result = $result->and($accepts);
continue;
}
$ourParameter = $ourParameters[$i];
$ourParameterType = $ourParameter->getType();
if ($ourParameter->isOptional() && !$theirParameter->isOptional()) {
$accepts = new IsSuperTypeOfResult(TrinaryLogic::createNo(), [
sprintf(
'Parameter %s of passed callable is required but the parameter of accepting callable is optional. It might be called without it.',
$parameterDescription,
),
]);
$result = $result->and($accepts);
}
if ($treatMixedAsAny) {
$isSuperType = $theirParameter->getType()->accepts($ourParameterType, $strictTypes);
$isSuperType = new IsSuperTypeOfResult($isSuperType->result, $isSuperType->reasons);
} else {
$isSuperType = $theirParameter->getType()->isSuperTypeOf($ourParameterType);
}
if ($isSuperType->maybe()) {
$verbosity = VerbosityLevel::getRecommendedLevelByType($theirParameter->getType(), $ourParameterType);
$isSuperType = new IsSuperTypeOfResult($isSuperType->result, array_merge($isSuperType->reasons, [
sprintf(
'Type %s of parameter %s of passed callable needs to be same or wider than parameter type %s of accepting callable.',
$theirParameter->getType()->describe($verbosity),
$parameterDescription,
$ourParameterType->describe($verbosity),
),
]));
}
$result = $result->and($isSuperType);
}
if (!$treatMixedAsAny && $theirParameterCount < $ourParameterCount) {
$result = $result->and(IsSuperTypeOfResult::createMaybe());
}
$theirReturnType = $theirs->getReturnType();
if ($treatMixedAsAny) {
$isReturnTypeSuperType = $ours->getReturnType()->accepts($theirReturnType, true);
$isReturnTypeSuperType = new IsSuperTypeOfResult($isReturnTypeSuperType->result, $isReturnTypeSuperType->reasons);
} else {
$isReturnTypeSuperType = $ours->getReturnType()->isSuperTypeOf($theirReturnType);
}
$pure = $ours->isPure();
if ($pure->yes()) {
$result = $result->and(new IsSuperTypeOfResult($theirs->isPure(), []));
} elseif ($pure->no()) {
$result = $result->and(new IsSuperTypeOfResult($theirs->isPure()->negate(), []));
}
return $result->and($isReturnTypeSuperType);
}
}