diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index e6bfb30cab..a6ebbcc15c 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -19,6 +19,7 @@ use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; +use PHPStan\Type\Accessory\AccessoryType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\Generic\TemplateMixedType; use PHPStan\Type\Generic\TemplateType; @@ -67,6 +68,10 @@ public function __construct(private array $types, private bool $normalized = fal $throwException(); } foreach ($types as $type) { + if ($type instanceof AccessoryType) { + // accessory types need to be intersected with a main type + $throwException(); + } if (!($type instanceof UnionType)) { continue; } diff --git a/tests/PHPStan/Type/UnionTypeTest.php b/tests/PHPStan/Type/UnionTypeTest.php index cd0ee180cc..4c8a983179 100644 --- a/tests/PHPStan/Type/UnionTypeTest.php +++ b/tests/PHPStan/Type/UnionTypeTest.php @@ -8,9 +8,12 @@ use Iterator; use PHPStan\Reflection\Native\NativeParameterReflection; use PHPStan\Reflection\PassedByReference; +use PHPStan\ShouldNotHappenException; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryLiteralStringType; +use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; +use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; use PHPStan\Type\Accessory\AccessoryNumericStringType; use PHPStan\Type\Accessory\HasMethodType; use PHPStan\Type\Accessory\HasOffsetType; @@ -1640,4 +1643,30 @@ public function dataGetArrays(): iterable ]; } + /** + * @param Type[] $types + * + * @dataProvider dataUnionThrows + */ + public function testUnionThrows(array $types, string $message): void + { + $this->expectException(ShouldNotHappenException::class); + $this->expectExceptionMessage($message); + + new UnionType($types); + } + + public function dataUnionThrows(): array + { + return [ + // union type requires at least 2 types + [[new AccessoryNonEmptyStringType()], 'Cannot create PHPStan\\Type\\UnionType with: non-empty-string'], + // test invalid combinations + [[new AccessoryNonFalsyStringType(), new AccessoryNumericStringType()], 'Cannot create PHPStan\\Type\\UnionType with: non-falsy-string, numeric-string'], + [[new IntegerType(), new AccessoryNumericStringType()], 'Cannot create PHPStan\\Type\\UnionType with: int, numeric-string'], + [[new ArrayType(new IntegerType(), new StringType()), new AccessoryNonEmptyStringType()], 'Cannot create PHPStan\\Type\\UnionType with: array, non-empty-string'], + [[new IntegerType(), new NonEmptyArrayType()], 'Cannot create PHPStan\\Type\\UnionType with: int, non-empty-array'], + ]; + } + }