1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 13:51:54 +01:00

Fix intersection of final classes

This commit is contained in:
Daniil Gentili 2022-07-27 18:46:07 +02:00
parent c2a8d1bb06
commit c409675e3e
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
3 changed files with 27 additions and 19 deletions

View File

@ -795,15 +795,7 @@ class CallAnalyzer
continue;
}
foreach ($intersection->getAtomicTypes() as $atomic_type) {
if ($assertion_type_atomic instanceof TTemplateParam
&& $assertion_type_atomic->as->getId() === $atomic_type->getId()
) {
continue;
}
$assertion_rule = clone $assertion_rule;
$assertion_rule->setAtomicType($atomic_type);
$orred_rules[] = $assertion_rule;
$orred_rules[] = new IsIdentical($atomic_type);
}
} elseif ($assertion_rule instanceof IsType) {
if (!UnionTypeComparator::canExpressionTypesBeIdentical(

View File

@ -19,6 +19,7 @@ use Psalm\Type\Atomic\TClosure;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TFloat;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TIterable;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralClassString;
@ -691,6 +692,14 @@ abstract class Type
$intersection_performed = true;
}
}
if ($type_1_atomic instanceof TIntRange && $type_2_atomic instanceof TIntRange) {
$intersection_atomic = TIntRange::intersectIntRanges(
$type_1_atomic,
$type_2_atomic
);
$intersection_performed = true;
return $intersection_atomic;
}
if (null === $intersection_atomic) {
if (AtomicTypeComparator::isContainedBy(
@ -719,8 +728,8 @@ abstract class Type
}
}
if (static::mayHaveIntersection($type_1_atomic)
&& static::mayHaveIntersection($type_2_atomic)
if (self::mayHaveIntersection($type_1_atomic, $codebase)
&& self::mayHaveIntersection($type_2_atomic, $codebase)
) {
if ($intersection_atomic === null && $wider_type === null) {
$intersection_atomic = clone $type_1_atomic;
@ -733,8 +742,8 @@ abstract class Type
.' Did you forget to assign one of the variables?'
);
}
if (!static::mayHaveIntersection($intersection_atomic)
|| !static::mayHaveIntersection($wider_type)
if (!self::mayHaveIntersection($intersection_atomic, $codebase)
|| !self::mayHaveIntersection($wider_type, $codebase)
) {
throw new LogicException(
'$intersection_atomic and $wider_type should be both support intersection.'
@ -769,12 +778,19 @@ abstract class Type
/**
* @psalm-assert-if-true TIterable|TNamedObject|TTemplateParam|TObjectWithProperties $type
*/
private static function mayHaveIntersection(Atomic $type): bool
public static function mayHaveIntersection(Atomic $type, Codebase $codebase): bool
{
return $type instanceof TIterable
|| $type instanceof TNamedObject
if ($type instanceof TIterable
|| $type instanceof TTemplateParam
|| $type instanceof TObjectWithProperties;
|| $type instanceof TObjectWithProperties
) {
return true;
}
if (!$type instanceof TNamedObject) {
return false;
}
$storage = $codebase->classlike_storage_provider->get($type->value);
return !$storage->final;
}
private static function hasIntersection(Atomic $type): bool

View File

@ -1123,7 +1123,7 @@ class FunctionTemplateAssertTest extends TestCase
}',
'error_message' => 'RedundantCondition',
],
'SKIPPED-assertNotSameClasses' => [
'assertNotSameClasses' => [
'code' => '<?php
/**
* Asserts that two variables are the same.
@ -1137,7 +1137,7 @@ class FunctionTemplateAssertTest extends TestCase
class a {}
class b {}
class c {}
final class c {}
$expected = rand(0, 1) ? new a : new b;
$actual = new c;