diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index c1bf18046..e4756e86d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -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( diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 88c31bac3..bad420734 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -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 diff --git a/tests/Template/FunctionTemplateAssertTest.php b/tests/Template/FunctionTemplateAssertTest.php index 57fbe6e26..61e9674c5 100644 --- a/tests/Template/FunctionTemplateAssertTest.php +++ b/tests/Template/FunctionTemplateAssertTest.php @@ -1123,7 +1123,7 @@ class FunctionTemplateAssertTest extends TestCase }', 'error_message' => 'RedundantCondition', ], - 'SKIPPED-assertNotSameClasses' => [ + 'assertNotSameClasses' => [ 'code' => '