|
121 | 121 | use PHPStan\Type\ThisType;
|
122 | 122 | use PHPStan\Type\Type;
|
123 | 123 | use PHPStan\Type\TypeCombinator;
|
124 |
| -use PHPStan\Type\TypehintHelper; |
125 | 124 | use PHPStan\Type\TypeTraverser;
|
126 | 125 | use PHPStan\Type\TypeUtils;
|
127 | 126 | use PHPStan\Type\TypeWithClassName;
|
@@ -1277,7 +1276,8 @@ private function resolveType(string $exprString, Expr $node): Type
|
1277 | 1276 | } else {
|
1278 | 1277 | $returnType = $arrowScope->getKeepVoidType($node->expr);
|
1279 | 1278 | if ($node->returnType !== null) {
|
1280 |
| - $returnType = TypehintHelper::decideType($this->getFunctionType($node->returnType, false, false), $returnType); |
| 1279 | + $nativeReturnType = $this->getFunctionType($node->returnType, false, false); |
| 1280 | + $returnType = self::intersectButNotNever($nativeReturnType, $returnType); |
1281 | 1281 | }
|
1282 | 1282 | }
|
1283 | 1283 |
|
@@ -1434,7 +1434,10 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
|
1434 | 1434 | $returnType,
|
1435 | 1435 | ]);
|
1436 | 1436 | } else {
|
1437 |
| - $returnType = TypehintHelper::decideType($this->getFunctionType($node->returnType, false, false), $returnType); |
| 1437 | + if ($node->returnType !== null) { |
| 1438 | + $nativeReturnType = $this->getFunctionType($node->returnType, false, false); |
| 1439 | + $returnType = self::intersectButNotNever($nativeReturnType, $returnType); |
| 1440 | + } |
1438 | 1441 | }
|
1439 | 1442 |
|
1440 | 1443 | $usedVariables = [];
|
@@ -3213,16 +3216,16 @@ private function enterAnonymousFunctionWithoutReflection(
|
3213 | 3216 | $parameterType = $this->getFunctionType($parameter->type, $isNullable, $parameter->variadic);
|
3214 | 3217 | if ($callableParameters !== null) {
|
3215 | 3218 | if (isset($callableParameters[$i])) {
|
3216 |
| - $parameterType = TypehintHelper::decideType($parameterType, $callableParameters[$i]->getType()); |
| 3219 | + $parameterType = self::intersectButNotNever($parameterType, $callableParameters[$i]->getType()); |
3217 | 3220 | } elseif (count($callableParameters) > 0) {
|
3218 | 3221 | $lastParameter = $callableParameters[count($callableParameters) - 1];
|
3219 | 3222 | if ($lastParameter->isVariadic()) {
|
3220 |
| - $parameterType = TypehintHelper::decideType($parameterType, $lastParameter->getType()); |
| 3223 | + $parameterType = self::intersectButNotNever($parameterType, $lastParameter->getType()); |
3221 | 3224 | } else {
|
3222 |
| - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); |
| 3225 | + $parameterType = self::intersectButNotNever($parameterType, new MixedType()); |
3223 | 3226 | }
|
3224 | 3227 | } else {
|
3225 |
| - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); |
| 3228 | + $parameterType = self::intersectButNotNever($parameterType, new MixedType()); |
3226 | 3229 | }
|
3227 | 3230 | }
|
3228 | 3231 | $holder = ExpressionTypeHolder::createYes($parameter->var, $parameterType);
|
@@ -3389,16 +3392,16 @@ private function enterArrowFunctionWithoutReflection(Expr\ArrowFunction $arrowFu
|
3389 | 3392 |
|
3390 | 3393 | if ($callableParameters !== null) {
|
3391 | 3394 | if (isset($callableParameters[$i])) {
|
3392 |
| - $parameterType = TypehintHelper::decideType($parameterType, $callableParameters[$i]->getType()); |
| 3395 | + $parameterType = self::intersectButNotNever($parameterType, $callableParameters[$i]->getType()); |
3393 | 3396 | } elseif (count($callableParameters) > 0) {
|
3394 | 3397 | $lastParameter = $callableParameters[count($callableParameters) - 1];
|
3395 | 3398 | if ($lastParameter->isVariadic()) {
|
3396 |
| - $parameterType = TypehintHelper::decideType($parameterType, $lastParameter->getType()); |
| 3399 | + $parameterType = self::intersectButNotNever($parameterType, $lastParameter->getType()); |
3397 | 3400 | } else {
|
3398 |
| - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); |
| 3401 | + $parameterType = self::intersectButNotNever($parameterType, new MixedType()); |
3399 | 3402 | }
|
3400 | 3403 | } else {
|
3401 |
| - $parameterType = TypehintHelper::decideType($parameterType, new MixedType()); |
| 3404 | + $parameterType = self::intersectButNotNever($parameterType, new MixedType()); |
3402 | 3405 | }
|
3403 | 3406 | }
|
3404 | 3407 |
|
@@ -3483,6 +3486,20 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
|
3483 | 3486 | return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection() : null);
|
3484 | 3487 | }
|
3485 | 3488 |
|
| 3489 | + private static function intersectButNotNever(Type $nativeType, Type $inferredType): Type |
| 3490 | + { |
| 3491 | + if ($nativeType->isSuperTypeOf($inferredType)->no()) { |
| 3492 | + return $nativeType; |
| 3493 | + } |
| 3494 | + |
| 3495 | + $result = TypeCombinator::intersect($nativeType, $inferredType); |
| 3496 | + if (TypeCombinator::containsNull($nativeType)) { |
| 3497 | + return TypeCombinator::addNull($result); |
| 3498 | + } |
| 3499 | + |
| 3500 | + return $result; |
| 3501 | + } |
| 3502 | + |
3486 | 3503 | public function enterMatch(Expr\Match_ $expr): self
|
3487 | 3504 | {
|
3488 | 3505 | if ($expr->cond instanceof Variable) {
|
|
0 commit comments