diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index f9fdd009ba..21852d8546 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -946,7 +946,7 @@ public static function intersect(Type ...$types): Type $valueType = $types[$j]->getValueType(); $newValueType = self::intersect($types[$i]->getOffsetValueType($offsetType), $valueType); if ($newValueType instanceof NeverType) { - return new NeverType(); + return $newValueType; } $types[$i] = $types[$i]->setOffsetValueType($offsetType, $newValueType); array_splice($types, $j--, 1); @@ -959,7 +959,7 @@ public static function intersect(Type ...$types): Type $valueType = $types[$i]->getValueType(); $newValueType = self::intersect($types[$j]->getOffsetValueType($offsetType), $valueType); if ($newValueType instanceof NeverType) { - return new NeverType(); + return $newValueType; } $types[$j] = $types[$j]->setOffsetValueType($offsetType, $newValueType); @@ -1044,7 +1044,7 @@ public static function intersect(Type ...$types): Type ) { $keyType = self::intersect($types[$i]->getIterableKeyType(), $types[$j]->getIterableKeyType()); if ($keyType instanceof NeverType) { - return new NeverType(); + return $keyType; } $types[$i] = new ArrayType($keyType, $types[$i]->getItemType()); continue; diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 425e84052d..1814854c08 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -2475,7 +2475,7 @@ public function dataIntersect(): iterable StaticTypeFactory::truthy(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2483,7 +2483,7 @@ public function dataIntersect(): iterable new NeverType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2542,7 +2542,7 @@ public function dataIntersect(): iterable new IterableType(new StringType(), new MixedType()), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2550,7 +2550,7 @@ public function dataIntersect(): iterable new IterableType(new MixedType(), new StringType()), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2638,7 +2638,7 @@ public function dataIntersect(): iterable new HasMethodType('doBar'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2654,7 +2654,7 @@ public function dataIntersect(): iterable new HasMethodType('__toString'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2695,7 +2695,7 @@ public function dataIntersect(): iterable new HasPropertyType('fooProperty'), ], PHP_VERSION_ID < 80200 ? IntersectionType::class : NeverType::class, - PHP_VERSION_ID < 80200 ? 'Test\Foo&hasProperty(fooProperty)' : '*NEVER*', + PHP_VERSION_ID < 80200 ? 'Test\Foo&hasProperty(fooProperty)' : '*NEVER*=implicit', ], [ [ @@ -2727,7 +2727,7 @@ public function dataIntersect(): iterable new HasPropertyType('fooProperty'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2760,7 +2760,7 @@ public function dataIntersect(): iterable new HasPropertyType('fooProperty'), ], PHP_VERSION_ID < 80200 ? UnionType::class : NeverType::class, - PHP_VERSION_ID < 80200 ? '(Test\FirstInterface&hasProperty(fooProperty))|(Test\Foo&hasProperty(fooProperty))' : '*NEVER*', + PHP_VERSION_ID < 80200 ? '(Test\FirstInterface&hasProperty(fooProperty))|(Test\Foo&hasProperty(fooProperty))' : '*NEVER*=implicit', ], [ [ @@ -2817,7 +2817,7 @@ public function dataIntersect(): iterable new HasOffsetType(new ConstantStringType('b')), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2825,7 +2825,7 @@ public function dataIntersect(): iterable new HasOffsetType(new ConstantStringType('a')), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2907,7 +2907,7 @@ public function dataIntersect(): iterable new NonEmptyArrayType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2941,7 +2941,7 @@ public function dataIntersect(): iterable new NonEmptyArrayType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2963,7 +2963,7 @@ public function dataIntersect(): iterable new IntegerType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2971,7 +2971,7 @@ public function dataIntersect(): iterable new StringType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -2979,7 +2979,7 @@ public function dataIntersect(): iterable new ConstantStringType('foo'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3094,7 +3094,7 @@ public function dataIntersect(): iterable new ConstantStringType('Nonexistent'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3102,7 +3102,7 @@ public function dataIntersect(): iterable new IntegerType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3158,7 +3158,7 @@ public function dataIntersect(): iterable new GenericClassStringType(new ObjectType(stdClass::class)), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3190,7 +3190,7 @@ public function dataIntersect(): iterable new ConstantStringType(stdClass::class), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3214,7 +3214,7 @@ public function dataIntersect(): iterable IntegerRangeType::fromInterval(7, 9), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3230,7 +3230,7 @@ public function dataIntersect(): iterable new ConstantIntegerType(4), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3361,7 +3361,7 @@ public function dataIntersect(): iterable new ClassStringType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3423,7 +3423,7 @@ public function dataIntersect(): iterable new AccessoryNumericStringType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3447,7 +3447,7 @@ public function dataIntersect(): iterable new AccessoryNumericStringType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3458,7 +3458,7 @@ public function dataIntersect(): iterable new NeverType(), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ [ @@ -3507,6 +3507,22 @@ public function dataIntersect(): iterable StrictMixedType::class, 'mixed', ], + [ + [ + new NeverType(true), + new IntegerType(), + ], + NeverType::class, + '*NEVER*=explicit', + ], + [ + [ + new NeverType(), + new IntegerType(), + ], + NeverType::class, + '*NEVER*=implicit', + ], ]; if (PHP_VERSION_ID < 80100) { @@ -3527,7 +3543,7 @@ public function dataIntersect(): iterable new EnumCaseObjectType(stdClass::class, 'ONE'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ [ @@ -3551,7 +3567,7 @@ public function dataIntersect(): iterable new EnumCaseObjectType('PHPStan\Fixture\TestEnum', 'TWO'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ [ @@ -3575,7 +3591,7 @@ public function dataIntersect(): iterable new EnumCaseObjectType('PHPStan\Fixture\AnotherTestEnum', 'ONE'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ [ @@ -3583,7 +3599,7 @@ public function dataIntersect(): iterable new EnumCaseObjectType('PHPStan\Fixture\TestEnum', 'ONE'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ [ @@ -3599,7 +3615,7 @@ public function dataIntersect(): iterable new EnumCaseObjectType('PHPStan\Fixture\TestEnum', 'ONE'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ [ @@ -3660,7 +3676,7 @@ public function dataIntersect(): iterable new ConstantStringType('0'), ], NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ]; yield [ @@ -3711,6 +3727,13 @@ public function testIntersect( $actualTypeDescription .= '=implicit'; } } + if ($actualType instanceof NeverType) { + if ($actualType->isExplicit()) { + $actualTypeDescription .= '=explicit'; + } else { + $actualTypeDescription .= '=implicit'; + } + } $this->assertSame($expectedTypeDescription, $actualTypeDescription); $this->assertInstanceOf($expectedTypeClass, $actualType); } @@ -3735,6 +3758,13 @@ public function testIntersectInversed( $actualTypeDescription .= '=implicit'; } } + if ($actualType instanceof NeverType) { + if ($actualType->isExplicit()) { + $actualTypeDescription .= '=explicit'; + } else { + $actualTypeDescription .= '=implicit'; + } + } $this->assertSame($expectedTypeDescription, $actualTypeDescription); $this->assertInstanceOf($expectedTypeClass, $actualType); } @@ -3746,7 +3776,7 @@ public function dataRemove(): array new ConstantBooleanType(true), new ConstantBooleanType(true), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new UnionType([ @@ -3802,13 +3832,13 @@ public function dataRemove(): array new ConstantBooleanType(true), new BooleanType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new ConstantBooleanType(false), new BooleanType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new BooleanType(), @@ -3826,19 +3856,19 @@ public function dataRemove(): array new BooleanType(), new BooleanType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ StaticTypeFactory::falsey(), StaticTypeFactory::falsey(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ StaticTypeFactory::truthy(), StaticTypeFactory::truthy(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ StaticTypeFactory::truthy(), @@ -3959,7 +3989,7 @@ public function dataRemove(): array new BenevolentUnionType([new IntegerType(), new StringType()]), new UnionType([new IntegerType(), new StringType()]), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new ArrayType(new MixedType(), new MixedType()), @@ -3987,7 +4017,7 @@ public function dataRemove(): array ]), new NonEmptyArrayType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new ArrayType(new MixedType(), new MixedType()), @@ -4026,13 +4056,13 @@ public function dataRemove(): array new MixedType(false), new MixedType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new MixedType(false, new StringType()), new MixedType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new MixedType(false), @@ -4062,7 +4092,7 @@ public function dataRemove(): array new ObjectType('Exception'), new ObjectType('Throwable'), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new ObjectType('Exception', new ObjectType('InvalidArgumentException')), @@ -4104,25 +4134,25 @@ public function dataRemove(): array IntegerRangeType::fromInterval(0, 2), IntegerRangeType::fromInterval(-1, 3), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ IntegerRangeType::fromInterval(0, 2), IntegerRangeType::fromInterval(0, 3), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ IntegerRangeType::fromInterval(0, 2), IntegerRangeType::fromInterval(-1, 2), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ IntegerRangeType::fromInterval(0, 2), new IntegerType(), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ IntegerRangeType::fromInterval(null, 1), @@ -4155,7 +4185,7 @@ public function dataRemove(): array ], 2), new HasOffsetType(new ConstantIntegerType(1)), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new ConstantArrayType([ @@ -4179,7 +4209,7 @@ public function dataRemove(): array ], 2, [1]), new HasOffsetType(new ConstantIntegerType(0)), NeverType::class, - '*NEVER*', + '*NEVER*=implicit', ], [ new MixedType(), @@ -4225,7 +4255,15 @@ public function testRemove( ): void { $result = TypeCombinator::remove($fromType, $type); - $this->assertSame($expectedTypeDescription, $result->describe(VerbosityLevel::precise())); + $actualTypeDescription = $result->describe(VerbosityLevel::precise()); + if ($result instanceof NeverType) { + if ($result->isExplicit()) { + $actualTypeDescription .= '=explicit'; + } else { + $actualTypeDescription .= '=implicit'; + } + } + $this->assertSame($expectedTypeDescription, $actualTypeDescription); $this->assertInstanceOf($expectedTypeClass, $result); }