Skip to content

Commit c0b434e

Browse files
committed
Skip conditional type search on types without late-resolvable types
`ConditionalReturnTypeRuleHelper::check()` walks every parameter type, out type, closure-this type and the return type with `TypeTraverser::map()` to collect `ConditionalType` and `ConditionalTypeForParameter` instances. Both are late-resolvable types, so a type whose `hasTemplateOrLateResolvableType()` is false can't contain one, and the traversal now gets skipped for those. When it does run, nothing changes: it still collects every occurrence without memoization, so this is independent from #6652, which keeps `map()` here on purpose. On the symfony/ux-css array shape (847 keys, 144 of them a nested shape alias), this traversal visits the shared nested shape once per offset using it. With #6671, `hasTemplateOrLateResolvableType()` is memoized on `ConstantArrayType`, so the check costs almost nothing there. For every `Type` whose `traverse()` visits children, `hasTemplateOrLateResolvableType()` checks those children too, except callable/closure parameter default values and `ObjectWithoutClassType`'s subtracted type, neither of which can hold a conditional type in a declared signature. I logged every traversal that found a conditional type while `hasTemplateOrLateResolvableType()` was false; that gave 0 cases over the full test suite and PHPStan's self-analysis. Measured with the https://github.com/Kocal/sf-ux-css-phpstan-reproducer reproducer, `phpstan-nested.neon`, `--debug`, 6 interleaved runs with the first discarded: | | before | after | |---|---|---| | 2.3.x, no turbo | 12.50-12.61 s | 12.12-12.17 s | | 2.3.x + #6652 + #6671, no turbo | 2.58-2.65 s | 2.11-2.13 s | | 2.3.x, turbo | 5.65-5.84 s | 5.46-5.50 s | `make tests` passes with and without the turbo extension, `make phpstan` and `make cs` pass. Refs phpstan/phpstan#15348
1 parent b0e7a99 commit c0b434e

1 file changed

Lines changed: 19 additions & 14 deletions

File tree

‎src/Rules/PhpDoc/ConditionalReturnTypeRuleHelper.php‎

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ public function check(ExtendedParametersAcceptor $acceptor): array
3030
$conditionalTypes = [];
3131
$parametersByName = [];
3232
foreach ($acceptor->getParameters() as $parameter) {
33-
TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
34-
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
35-
$conditionalTypes[] = $type;
36-
}
33+
// conditional types are late-resolvable, so a type without any cannot contain one
34+
if ($parameter->getType()->hasTemplateOrLateResolvableType()) {
35+
TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
36+
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
37+
$conditionalTypes[] = $type;
38+
}
3739

38-
return $traverse($type);
39-
});
40+
return $traverse($type);
41+
});
42+
}
4043

41-
if ($parameter->getOutType() !== null) {
44+
if ($parameter->getOutType() !== null && $parameter->getOutType()->hasTemplateOrLateResolvableType()) {
4245
TypeTraverser::map($parameter->getOutType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
4346
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
4447
$conditionalTypes[] = $type;
@@ -48,7 +51,7 @@ public function check(ExtendedParametersAcceptor $acceptor): array
4851
});
4952
}
5053

51-
if ($parameter->getClosureThisType() !== null) {
54+
if ($parameter->getClosureThisType() !== null && $parameter->getClosureThisType()->hasTemplateOrLateResolvableType()) {
5255
TypeTraverser::map($parameter->getClosureThisType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
5356
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
5457
$conditionalTypes[] = $type;
@@ -61,13 +64,15 @@ public function check(ExtendedParametersAcceptor $acceptor): array
6164
$parametersByName[$parameter->getName()] = $parameter;
6265
}
6366

64-
TypeTraverser::map($acceptor->getReturnType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
65-
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
66-
$conditionalTypes[] = $type;
67-
}
67+
if ($acceptor->getReturnType()->hasTemplateOrLateResolvableType()) {
68+
TypeTraverser::map($acceptor->getReturnType(), static function (Type $type, callable $traverse) use (&$conditionalTypes): Type {
69+
if ($type instanceof ConditionalType || $type instanceof ConditionalTypeForParameter) {
70+
$conditionalTypes[] = $type;
71+
}
6872

69-
return $traverse($type);
70-
});
73+
return $traverse($type);
74+
});
75+
}
7176

7277
$errors = [];
7378
foreach ($conditionalTypes as $conditionalType) {

0 commit comments

Comments
 (0)