1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Merge pull request #6874 from orklah/fix-never-combination

fix never combination
This commit is contained in:
orklah 2021-11-09 23:18:58 +01:00 committed by GitHub
commit a193ec4573
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 11 deletions

View File

@ -37,8 +37,11 @@ use Psalm\StatementsSource;
use Psalm\Storage\FunctionLikeStorage;
use Psalm\Storage\MethodStorage;
use Psalm\Type;
use Psalm\Type\Union;
use function array_diff;
use function array_filter;
use function array_values;
use function count;
use function in_array;
use function strpos;
@ -57,6 +60,7 @@ class ReturnTypeAnalyzer
* @return false|null
*
* @psalm-suppress PossiblyUnusedReturnValue unused but seems important
* @psalm-suppress ComplexMethod to be refactored
*/
public static function verifyReturnType(
FunctionLike $function,
@ -224,6 +228,19 @@ class ReturnTypeAnalyzer
return null;
}
$number_of_types = count($inferred_return_type_parts);
// we filter TNever and TEmpty that have no bearing on the return type
if ($number_of_types > 1) {
$inferred_return_type_parts = array_filter(
$inferred_return_type_parts,
static function (Union $union_type) : bool {
return !($union_type->isNever() || $union_type->isEmpty());
}
);
}
$inferred_return_type_parts = array_values($inferred_return_type_parts);
$inferred_return_type = $inferred_return_type_parts
? \Psalm\Type::combineUnionTypeArray($inferred_return_type_parts, $codebase)
: Type::getVoid();

View File

@ -3,9 +3,13 @@ namespace Psalm\Internal\Type;
use Psalm\Codebase;
use Psalm\Type;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TEmpty;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TTemplateParam;
use function array_filter;
use function array_merge;
use function array_values;
use function count;
@ -793,21 +797,43 @@ class TypeExpander
$else_conditional_return_types
);
foreach ($all_conditional_return_types as $i => $conditional_return_type) {
if ($conditional_return_type instanceof Type\Atomic\TVoid
&& count($all_conditional_return_types) > 1
) {
$all_conditional_return_types[$i] = new Type\Atomic\TNull();
$all_conditional_return_types[$i]->from_docblock = true;
$number_of_types = count($all_conditional_return_types);
// we filter TNever and TEmpty that have no bearing on the return type
if ($number_of_types > 1) {
$all_conditional_return_types = array_filter(
$all_conditional_return_types,
static function (Atomic $atomic_type) : bool {
return !($atomic_type instanceof TEmpty
|| $atomic_type instanceof TNever);
}
);
}
// if we still have more than one type, we remove TVoid and replace it by TNull
$number_of_types = count($all_conditional_return_types);
if ($number_of_types > 1) {
$all_conditional_return_types = array_filter(
$all_conditional_return_types,
static function (Atomic $atomic_type) : bool {
return !$atomic_type instanceof Atomic\TVoid;
}
);
if (count($all_conditional_return_types) !== $number_of_types) {
$null_type = new Type\Atomic\TNull();
$null_type->from_docblock = true;
$all_conditional_return_types[] = $null_type;
}
}
$combined = TypeCombiner::combine(
array_values($all_conditional_return_types),
$codebase
);
if ($all_conditional_return_types) {
$combined = TypeCombiner::combine(
array_values($all_conditional_return_types),
$codebase
);
return array_values($combined->getAtomicTypes());
return array_values($combined->getAtomicTypes());
}
}
$return_type->conditional_type = self::expandUnion(

View File

@ -978,6 +978,30 @@ class ReturnTypeTest extends TestCase
}
}'
],
'NeverAndVoid' => [
'<?php
function foo(): void
{
foreach ([0, 1, 2] as $_i) {
return;
}
throw new \Exception();
}'
],
'neverAndVoidOnConditional' => [
'<?php
/**
* @template T as bool
* @param T $end
* @return (T is true ? never : void)
*/
function a($end): void{
if($end){
die();
}
}'
],
];
}