Skip to content

Commit

Permalink
Support conditional return types by union-ing their branches
Browse files Browse the repository at this point in the history
First baby step for phpstan/phpstan#3853
  • Loading branch information
rvanvelzen authored and rajyan committed Apr 5, 2022
1 parent b220021 commit 08b4afd
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/PhpDoc/TypeNodeResolver.php
Expand Up @@ -20,6 +20,8 @@
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
Expand Down Expand Up @@ -121,6 +123,9 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): Type
} elseif ($typeNode instanceof IntersectionTypeNode) {
return $this->resolveIntersectionTypeNode($typeNode, $nameScope);

} elseif ($typeNode instanceof ConditionalTypeNode || $typeNode instanceof ConditionalTypeForParameterNode) {
return $this->resolveConditionalTypeNode($typeNode, $nameScope);

} elseif ($typeNode instanceof ArrayTypeNode) {
return $this->resolveArrayTypeNode($typeNode, $nameScope);

Expand Down Expand Up @@ -438,6 +443,12 @@ private function resolveIntersectionTypeNode(IntersectionTypeNode $typeNode, Nam
return TypeCombinator::intersect(...$types);
}

private function resolveConditionalTypeNode(ConditionalTypeNode|ConditionalTypeForParameterNode $typeNode, NameScope $nameScope): Type
{
$types = $this->resolveMultiple([$typeNode->if, $typeNode->else], $nameScope);
return TypeCombinator::union(...$types);
}

private function resolveArrayTypeNode(ArrayTypeNode $typeNode, NameScope $nameScope): Type
{
$itemType = $this->resolve($typeNode->type, $nameScope);
Expand Down
53 changes: 53 additions & 0 deletions tests/PHPStan/Analyser/data/bug-3853.php
@@ -0,0 +1,53 @@
<?php

namespace Bug3853;

use function PHPStan\Testing\assertType;

abstract class Test
{
/**
* @template TKey as array-key
* @template TArray as array<TKey, mixed>
*
* @param TArray $array
*
* @return (TArray is non-empty-array ? non-empty-list<TKey> : list<TKey>)
*/
abstract public function arrayKeys(array $array);

/**
* @param array $array
* @param non-empty-array $nonEmptyArray
*
* @param array<int, int> $intArray
* @param non-empty-array<int, int> $nonEmptyIntArray
*/
public function testArrayKeys(array $array, array $nonEmptyArray, array $intArray, array $nonEmptyIntArray): void
{
assertType('array<int, (int|string)>', $this->arrayKeys($array));
assertType('array<int, int>', $this->arrayKeys($intArray));

// TODO resolve correctly
//assertType('non-empty-array<int, (int|string)>', $this->arrayKeys($nonEmptyArray));
//assertType('non-empty-array<int, int>', $this->arrayKeys($nonEmptyIntArray));

assertType('array<int, (int|string)>', $this->arrayKeys($nonEmptyArray));
assertType('array<int, int>', $this->arrayKeys($nonEmptyIntArray));
}

/**
* @return ($as_float is true ? float : string)
*/
abstract public function microtime(bool $as_float = false);

public function testMicrotime(): void
{
// TODO resolve correctly
//assertType('float', $this->microtime(true));
//assertType('string', $this->microtime(false));

assertType('float|string', $this->microtime(true));
assertType('float|string', $this->microtime(false));
}
}

0 comments on commit 08b4afd

Please sign in to comment.