diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 90b3fdd465..c24defb1f8 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -5594,18 +5594,7 @@ private function exactInstantiation(New_ $node, string $className): ?Type private function filterTypeWithMethod(Type $typeWithMethod, string $methodName): ?Type { if ($typeWithMethod instanceof UnionType) { - $newTypes = []; - foreach ($typeWithMethod->getTypes() as $innerType) { - if (!$innerType->hasMethod($methodName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithMethod = TypeCombinator::union(...$newTypes); + $typeWithMethod = $typeWithMethod->filterTypes(static fn (Type $innerType) => $innerType->hasMethod($methodName)->yes()); } if (!$typeWithMethod->hasMethod($methodName)->yes()) { @@ -5709,18 +5698,7 @@ private function methodCallReturnType(Type $typeWithMethod, string $methodName, public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection { if ($typeWithProperty instanceof UnionType) { - $newTypes = []; - foreach ($typeWithProperty->getTypes() as $innerType) { - if (!$innerType->hasProperty($propertyName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithProperty = TypeCombinator::union(...$newTypes); + $typeWithProperty = $typeWithProperty->filterTypes(static fn (Type $innerType) => $innerType->hasProperty($propertyName)->yes()); } if (!$typeWithProperty->hasProperty($propertyName)->yes()) { return null; @@ -5749,18 +5727,7 @@ private function propertyFetchType(Type $fetchedOnType, string $propertyName, Ex public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ConstantReflection { if ($typeWithConstant instanceof UnionType) { - $newTypes = []; - foreach ($typeWithConstant->getTypes() as $innerType) { - if (!$innerType->hasConstant($constantName)->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return null; - } - $typeWithConstant = TypeCombinator::union(...$newTypes); + $typeWithConstant = $typeWithConstant->filterTypes(static fn (Type $innerType) => $innerType->hasConstant($constantName)->yes()); } if (!$typeWithConstant->hasConstant($constantName)->yes()) { return null; @@ -5804,18 +5771,10 @@ private function getNativeConstantTypes(): array public function getIterableKeyType(Type $iteratee): Type { if ($iteratee instanceof UnionType) { - $newTypes = []; - foreach ($iteratee->getTypes() as $innerType) { - if (!$innerType->isIterable()->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return $iteratee->getIterableKeyType(); + $filtered = $iteratee->filterTypes(static fn (Type $innerType) => $innerType->isIterable()->yes()); + if (!$filtered instanceof NeverType) { + $iteratee = $filtered; } - $iteratee = TypeCombinator::union(...$newTypes); } return $iteratee->getIterableKeyType(); @@ -5824,18 +5783,10 @@ public function getIterableKeyType(Type $iteratee): Type public function getIterableValueType(Type $iteratee): Type { if ($iteratee instanceof UnionType) { - $newTypes = []; - foreach ($iteratee->getTypes() as $innerType) { - if (!$innerType->isIterable()->yes()) { - continue; - } - - $newTypes[] = $innerType; - } - if (count($newTypes) === 0) { - return $iteratee->getIterableValueType(); + $filtered = $iteratee->filterTypes(static fn (Type $innerType) => $innerType->isIterable()->yes()); + if (!$filtered instanceof NeverType) { + $iteratee = $filtered; } - $iteratee = TypeCombinator::union(...$newTypes); } return $iteratee->getIterableValueType(); diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 6b828625b6..b7e794378a 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -86,6 +86,23 @@ public function getTypes(): array return $this->types; } + /** + * @param callable(Type $type): bool $filterCb + */ + public function filterTypes(callable $filterCb): Type + { + $newTypes = []; + foreach ($this->getTypes() as $innerType) { + if (!$filterCb($innerType)) { + continue; + } + + $newTypes[] = $innerType; + } + + return TypeCombinator::union(...$newTypes); + } + public function isNormalized(): bool { return $this->normalized;